armature_admin/
config.rs

1//! Admin configuration
2
3use serde::{Deserialize, Serialize};
4
5/// Admin dashboard configuration
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct AdminConfig {
8    /// Dashboard title
9    pub title: String,
10    /// Base URL path (e.g., "/admin")
11    pub base_path: String,
12    /// Theme settings
13    pub theme: Theme,
14    /// Items per page for lists
15    pub items_per_page: usize,
16    /// Maximum items per page allowed
17    pub max_items_per_page: usize,
18    /// Require authentication
19    pub require_auth: bool,
20    /// Enable search globally
21    pub enable_search: bool,
22    /// Enable export functionality
23    pub enable_export: bool,
24    /// Date format
25    pub date_format: String,
26    /// DateTime format
27    pub datetime_format: String,
28    /// Logo URL
29    pub logo_url: Option<String>,
30    /// Favicon URL
31    pub favicon_url: Option<String>,
32    /// Custom CSS
33    pub custom_css: Option<String>,
34    /// Custom JavaScript
35    pub custom_js: Option<String>,
36    /// Footer text
37    pub footer_text: Option<String>,
38}
39
40impl Default for AdminConfig {
41    fn default() -> Self {
42        Self {
43            title: "Admin Dashboard".to_string(),
44            base_path: "/admin".to_string(),
45            theme: Theme::default(),
46            items_per_page: 25,
47            max_items_per_page: 100,
48            require_auth: true,
49            enable_search: true,
50            enable_export: true,
51            date_format: "%Y-%m-%d".to_string(),
52            datetime_format: "%Y-%m-%d %H:%M:%S".to_string(),
53            logo_url: None,
54            favicon_url: None,
55            custom_css: None,
56            custom_js: None,
57            footer_text: None,
58        }
59    }
60}
61
62/// Theme configuration
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct Theme {
65    /// Theme name/preset
66    pub name: ThemePreset,
67    /// Primary color
68    pub primary_color: String,
69    /// Secondary color
70    pub secondary_color: String,
71    /// Accent color
72    pub accent_color: String,
73    /// Background color
74    pub background_color: String,
75    /// Surface color (cards, etc.)
76    pub surface_color: String,
77    /// Text color
78    pub text_color: String,
79    /// Muted text color
80    pub text_muted_color: String,
81    /// Border color
82    pub border_color: String,
83    /// Success color
84    pub success_color: String,
85    /// Warning color
86    pub warning_color: String,
87    /// Error color
88    pub error_color: String,
89    /// Sidebar width
90    pub sidebar_width: String,
91    /// Border radius
92    pub border_radius: String,
93    /// Font family
94    pub font_family: String,
95}
96
97impl Default for Theme {
98    fn default() -> Self {
99        Self::dark()
100    }
101}
102
103impl Theme {
104    /// Dark theme preset
105    pub fn dark() -> Self {
106        Self {
107            name: ThemePreset::Dark,
108            primary_color: "#6366f1".to_string(),    // Indigo
109            secondary_color: "#8b5cf6".to_string(),  // Violet
110            accent_color: "#22d3ee".to_string(),     // Cyan
111            background_color: "#0f172a".to_string(), // Slate 900
112            surface_color: "#1e293b".to_string(),    // Slate 800
113            text_color: "#f8fafc".to_string(),       // Slate 50
114            text_muted_color: "#94a3b8".to_string(), // Slate 400
115            border_color: "#334155".to_string(),     // Slate 700
116            success_color: "#22c55e".to_string(),    // Green
117            warning_color: "#f59e0b".to_string(),    // Amber
118            error_color: "#ef4444".to_string(),      // Red
119            sidebar_width: "260px".to_string(),
120            border_radius: "0.5rem".to_string(),
121            font_family: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif".to_string(),
122        }
123    }
124
125    /// Light theme preset
126    pub fn light() -> Self {
127        Self {
128            name: ThemePreset::Light,
129            primary_color: "#4f46e5".to_string(),    // Indigo
130            secondary_color: "#7c3aed".to_string(),  // Violet
131            accent_color: "#0891b2".to_string(),     // Cyan
132            background_color: "#f8fafc".to_string(), // Slate 50
133            surface_color: "#ffffff".to_string(),    // White
134            text_color: "#0f172a".to_string(),       // Slate 900
135            text_muted_color: "#64748b".to_string(), // Slate 500
136            border_color: "#e2e8f0".to_string(),     // Slate 200
137            success_color: "#16a34a".to_string(),    // Green
138            warning_color: "#d97706".to_string(),    // Amber
139            error_color: "#dc2626".to_string(),      // Red
140            sidebar_width: "260px".to_string(),
141            border_radius: "0.5rem".to_string(),
142            font_family: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif".to_string(),
143        }
144    }
145
146    /// Corporate blue theme
147    pub fn corporate() -> Self {
148        Self {
149            name: ThemePreset::Corporate,
150            primary_color: "#2563eb".to_string(),    // Blue
151            secondary_color: "#1d4ed8".to_string(),  // Blue darker
152            accent_color: "#0ea5e9".to_string(),     // Sky
153            background_color: "#f1f5f9".to_string(), // Slate 100
154            surface_color: "#ffffff".to_string(),
155            text_color: "#1e293b".to_string(),       // Slate 800
156            text_muted_color: "#64748b".to_string(), // Slate 500
157            border_color: "#cbd5e1".to_string(),     // Slate 300
158            success_color: "#059669".to_string(),    // Emerald
159            warning_color: "#ca8a04".to_string(),    // Yellow
160            error_color: "#dc2626".to_string(),      // Red
161            sidebar_width: "240px".to_string(),
162            border_radius: "0.375rem".to_string(),
163            font_family: "'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif".to_string(),
164        }
165    }
166
167    /// Generate CSS variables
168    pub fn to_css_variables(&self) -> String {
169        format!(
170            r#":root {{
171  --admin-primary: {};
172  --admin-secondary: {};
173  --admin-accent: {};
174  --admin-bg: {};
175  --admin-surface: {};
176  --admin-text: {};
177  --admin-text-muted: {};
178  --admin-border: {};
179  --admin-success: {};
180  --admin-warning: {};
181  --admin-error: {};
182  --admin-sidebar-width: {};
183  --admin-radius: {};
184  --admin-font: {};
185}}"#,
186            self.primary_color,
187            self.secondary_color,
188            self.accent_color,
189            self.background_color,
190            self.surface_color,
191            self.text_color,
192            self.text_muted_color,
193            self.border_color,
194            self.success_color,
195            self.warning_color,
196            self.error_color,
197            self.sidebar_width,
198            self.border_radius,
199            self.font_family,
200        )
201    }
202}
203
204/// Theme presets
205#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
206pub enum ThemePreset {
207    #[default]
208    Dark,
209    Light,
210    Corporate,
211    Custom,
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217
218    #[test]
219    fn test_default_config() {
220        let config = AdminConfig::default();
221        assert_eq!(config.title, "Admin Dashboard");
222        assert_eq!(config.base_path, "/admin");
223        assert_eq!(config.items_per_page, 25);
224    }
225
226    #[test]
227    fn test_theme_presets() {
228        let dark = Theme::dark();
229        assert_eq!(dark.name, ThemePreset::Dark);
230
231        let light = Theme::light();
232        assert_eq!(light.name, ThemePreset::Light);
233    }
234
235    #[test]
236    fn test_css_variables() {
237        let theme = Theme::dark();
238        let css = theme.to_css_variables();
239        assert!(css.contains("--admin-primary:"));
240        assert!(css.contains("--admin-bg:"));
241    }
242}
243