//! Template List Widget //! //! Provides a widget for displaying and managing enrolled face templates. //! Supports viewing template details and removing templates. use crate::dbus_client::TemplateInfo; /// Template list box containing enrolled templates #[derive(Debug, Clone)] pub struct TemplateListBox { /// List of templates templates: Vec, } impl TemplateListBox { /// Create a new template list box pub fn new(templates: Vec) -> Self { Self { templates } } /// Get the list of templates pub fn templates(&self) -> &[TemplateInfo] { &self.templates } /// Find a template by ID pub fn find_by_id(&self, id: &str) -> Option<&TemplateInfo> { self.templates.iter().find(|t| t.id == id) } /// Find a template by label pub fn find_by_label(&self, label: &str) -> Option<&TemplateInfo> { self.templates.iter().find(|t| t.label == label) } /// Check if any templates are enrolled pub fn is_empty(&self) -> bool { self.templates.is_empty() } /// Get the count of enrolled templates pub fn count(&self) -> usize { self.templates.len() } /// Update the template list pub fn update(&mut self, templates: Vec) { self.templates = templates; } } impl Default for TemplateListBox { fn default() -> Self { Self::new(Vec::new()) } } /// Template display model for UI binding #[derive(Debug, Clone)] pub struct TemplateDisplayModel { /// Template ID pub id: String, /// Display label pub label: String, /// Username pub username: String, /// Formatted creation date pub created_date: String, /// Formatted last used date pub last_used_date: String, /// Whether this template has been used recently pub recently_used: bool, } impl TemplateDisplayModel { /// Create a display model from template info pub fn from_template(template: &TemplateInfo) -> Self { use chrono::{DateTime, Duration, Local, Utc}; let created_date = DateTime::::from_timestamp(template.created_at, 0) .map(|dt| dt.with_timezone(&Local)) .map(|dt| dt.format("%B %d, %Y").to_string()) .unwrap_or_else(|| "Unknown".to_string()); let (last_used_date, recently_used) = if let Some(ts) = template.last_used { let dt = DateTime::::from_timestamp(ts, 0).map(|dt| dt.with_timezone(&Local)); let date_str = dt .as_ref() .map(|d| d.format("%B %d, %Y at %H:%M").to_string()) .unwrap_or_else(|| "Unknown".to_string()); // Check if used within the last 24 hours let recent = dt .map(|d| { let now = Local::now(); now.signed_duration_since(d) < Duration::hours(24) }) .unwrap_or(false); (date_str, recent) } else { ("Never".to_string(), false) }; Self { id: template.id.clone(), label: template.label.clone(), username: template.username.clone(), created_date, last_used_date, recently_used, } } } /// Template action types #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TemplateAction { /// View template details View, /// Remove template Remove, /// Test authentication with template Test, } #[cfg(test)] mod tests { use super::*; fn create_test_template(id: &str, label: &str) -> TemplateInfo { TemplateInfo { id: id.to_string(), label: label.to_string(), username: "testuser".to_string(), created_at: 1704067200, // 2024-01-01 00:00:00 UTC last_used: Some(1704153600), // 2024-01-02 00:00:00 UTC } } #[test] fn test_template_list_box() { let templates = vec![ create_test_template("1", "default"), create_test_template("2", "backup"), ]; let list = TemplateListBox::new(templates); assert!(!list.is_empty()); assert_eq!(list.count(), 2); assert!(list.find_by_id("1").is_some()); assert!(list.find_by_label("backup").is_some()); assert!(list.find_by_id("nonexistent").is_none()); } #[test] fn test_empty_list() { let list = TemplateListBox::default(); assert!(list.is_empty()); assert_eq!(list.count(), 0); } #[test] fn test_template_display_model() { let template = create_test_template("test-id", "Test Face"); let model = TemplateDisplayModel::from_template(&template); assert_eq!(model.id, "test-id"); assert_eq!(model.label, "Test Face"); assert_eq!(model.username, "testuser"); assert!(!model.created_date.is_empty()); assert_ne!(model.last_used_date, "Never"); } }