qorzen_oxide/ui/pages/
admin.rs

1// src/ui/pages/admin.rs - Administrative dashboard and user management
2
3use dioxus::prelude::*;
4
5use crate::ui::pages::{EmptyState, PageWrapper, StatCard, StatTrend};
6
7/// Main admin page component
8#[component]
9pub fn Admin() -> Element {
10    let mut active_tab = use_signal(|| "overview".to_string());
11
12    let page_actions = rsx! {
13        div {
14            class: "flex space-x-3",
15            button {
16                r#type: "button",
17                class: "inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500",
18                svg {
19                    class: "-ml-1 mr-2 h-4 w-4",
20                    xmlns: "http://www.w3.org/2000/svg",
21                    fill: "none",
22                    view_box: "0 0 24 24",
23                    stroke: "currentColor",
24                    path {
25                        stroke_linecap: "round",
26                        stroke_linejoin: "round",
27                        stroke_width: "2",
28                        d: "M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
29                    }
30                }
31                "Export Report"
32            }
33            button {
34                r#type: "button",
35                class: "inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500",
36                svg {
37                    class: "-ml-1 mr-2 h-4 w-4",
38                    xmlns: "http://www.w3.org/2000/svg",
39                    fill: "none",
40                    view_box: "0 0 24 24",
41                    stroke: "currentColor",
42                    path {
43                        stroke_linecap: "round",
44                        stroke_linejoin: "round",
45                        stroke_width: "2",
46                        d: "M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"
47                    }
48                }
49                "Add User"
50            }
51        }
52    };
53
54    let admin_warning_banner = rsx! {
55        div {
56            class: "mb-6 bg-red-50 border-l-4 border-red-400 p-4",
57            div {
58                class: "flex",
59                div {
60                    class: "flex-shrink-0",
61                    svg {
62                        class: "h-5 w-5 text-red-400",
63                        xmlns: "http://www.w3.org/2000/svg",
64                        view_box: "0 0 20 20",
65                        fill: "currentColor",
66                        path {
67                            fill_rule: "evenodd",
68                            d: "M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z",
69                            clip_rule: "evenodd"
70                        }
71                    }
72                }
73                div {
74                    class: "ml-3",
75                    h3 {
76                        class: "text-sm font-medium text-red-800",
77                        "Administrative Access"
78                    }
79                    div {
80                        class: "mt-2 text-sm text-red-700",
81                        p {
82                            "You have administrative privileges. Please use these tools responsibly. Changes made here can affect all users and the entire system."
83                        }
84                    }
85                }
86            }
87        }
88    };
89
90    let navigation_tabs = rsx! {
91        div {
92            class: "border-b border-gray-200 mb-6",
93            nav {
94                class: "-mb-px flex space-x-8",
95                button {
96                    r#type: "button",
97                    class: if active_tab() == "overview" {
98                        "py-2 px-1 border-b-2 font-medium text-sm border-blue-500 text-blue-600"
99                    } else {
100                        "py-2 px-1 border-b-2 font-medium text-sm border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
101                    },
102                    onclick: move |_| active_tab.set("overview".to_string()),
103                    "Overview"
104                }
105                button {
106                    r#type: "button",
107                    class: if active_tab() == "users" {
108                        "py-2 px-1 border-b-2 font-medium text-sm border-blue-500 text-blue-600"
109                    } else {
110                        "py-2 px-1 border-b-2 font-medium text-sm border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
111                    },
112                    onclick: move |_| active_tab.set("users".to_string()),
113                    "Users"
114                }
115                button {
116                    r#type: "button",
117                    class: if active_tab() == "system" {
118                        "py-2 px-1 border-b-2 font-medium text-sm border-blue-500 text-blue-600"
119                    } else {
120                        "py-2 px-1 border-b-2 font-medium text-sm border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
121                    },
122                    onclick: move |_| active_tab.set("system".to_string()),
123                    "System"
124                }
125                button {
126                    r#type: "button",
127                    class: if active_tab() == "plugins" {
128                        "py-2 px-1 border-b-2 font-medium text-sm border-blue-500 text-blue-600"
129                    } else {
130                        "py-2 px-1 border-b-2 font-medium text-sm border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
131                    },
132                    onclick: move |_| active_tab.set("plugins".to_string()),
133                    "Plugins"
134                }
135                button {
136                    r#type: "button",
137                    class: if active_tab() == "logs" {
138                        "py-2 px-1 border-b-2 font-medium text-sm border-blue-500 text-blue-600"
139                    } else {
140                        "py-2 px-1 border-b-2 font-medium text-sm border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
141                    },
142                    onclick: move |_| active_tab.set("logs".to_string()),
143                    "Logs"
144                }
145            }
146        }
147    };
148
149    let tab_content = match active_tab().as_str() {
150        "overview" => rsx! { OverviewTab {} },
151        "users" => rsx! { UsersTab {} },
152        "system" => rsx! { SystemTab {} },
153        "plugins" => rsx! { PluginsTab {} },
154        "logs" => rsx! { LogsTab {} },
155        _ => rsx! { div { "Unknown tab" } },
156    };
157
158    rsx! {
159        PageWrapper {
160            title: "Administration".to_string(),
161            subtitle: Some("Manage users, system settings, and monitor application health".to_string()),
162            actions: Some(page_actions),
163
164            {admin_warning_banner}
165            {navigation_tabs}
166            {tab_content}
167        }
168    }
169}
170
171/// Overview tab with system statistics
172#[component]
173fn OverviewTab() -> Element {
174    let stats = get_system_stats();
175
176    let statistics_grid = rsx! {
177        div {
178            class: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6",
179            for stat in stats {
180                StatCard {
181                    key: "{stat.id}",
182                    title: stat.title,
183                    value: stat.value,
184                    change: stat.change,
185                    trend: stat.trend,
186                    icon: stat.icon
187                }
188            }
189        }
190    };
191
192    let system_health_grid = rsx! {
193        div {
194            class: "grid grid-cols-1 lg:grid-cols-2 gap-6",
195
196            // Recent activity
197            div {
198                class: "bg-white shadow rounded-lg",
199                div {
200                    class: "px-4 py-5 sm:px-6 border-b border-gray-200",
201                    h3 {
202                        class: "text-lg leading-6 font-medium text-gray-900",
203                        "Recent Admin Activity"
204                    }
205                }
206                div {
207                    class: "px-4 py-5 sm:p-6",
208                    RecentActivityList {}
209                }
210            }
211
212            // System alerts
213            div {
214                class: "bg-white shadow rounded-lg",
215                div {
216                    class: "px-4 py-5 sm:px-6 border-b border-gray-200",
217                    h3 {
218                        class: "text-lg leading-6 font-medium text-gray-900",
219                        "System Alerts"
220                    }
221                }
222                div {
223                    class: "px-4 py-5 sm:p-6",
224                    SystemAlerts {}
225                }
226            }
227        }
228    };
229
230    rsx! {
231        div {
232            class: "space-y-6",
233            {statistics_grid}
234            {system_health_grid}
235        }
236    }
237}
238
239/// Users management tab
240#[component]
241fn UsersTab() -> Element {
242    let mut search_query = use_signal(String::new);
243    let users = get_mock_users();
244
245    let filtered_users: Vec<_> = users
246        .into_iter()
247        .filter(|user| {
248            if search_query().is_empty() {
249                true
250            } else {
251                let query = search_query().to_lowercase();
252                user.name.to_lowercase().contains(&query)
253                    || user.email.to_lowercase().contains(&query)
254                    || user.role.to_lowercase().contains(&query)
255            }
256        })
257        .collect();
258
259    let active_users = filtered_users
260        .iter()
261        .filter(|u| u.status == "Active")
262        .count();
263    let inactive_users = filtered_users
264        .iter()
265        .filter(|u| u.status == "Inactive")
266        .count();
267
268    let search_bar = rsx! {
269        div {
270            class: "flex justify-between items-center",
271            div {
272                class: "flex-1 max-w-md",
273                div {
274                    class: "relative",
275                    input {
276                        r#type: "text",
277                        placeholder: "Search users...",
278                        class: "block w-full pr-12 border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500 sm:text-sm",
279                        value: "{search_query}",
280                        oninput: move |e| search_query.set(e.value())
281                    }
282                    div {
283                        class: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none",
284                        svg {
285                            class: "h-5 w-5 text-gray-400",
286                            xmlns: "http://www.w3.org/2000/svg",
287                            view_box: "0 0 20 20",
288                            fill: "currentColor",
289                            path {
290                                fill_rule: "evenodd",
291                                d: "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z",
292                                clip_rule: "evenodd"
293                            }
294                        }
295                    }
296                }
297            }
298
299            // User statistics
300            div {
301                class: "flex space-x-4 text-sm text-gray-500",
302                span { "Total: {filtered_users.len()}" }
303                span {
304                    "Active: {active_users}",
305                }
306                span {
307                    "Inactive: {inactive_users}"
308                }
309            }
310        }
311    };
312
313    let users_table = if filtered_users.is_empty() {
314        rsx! {
315            div {
316                class: "bg-white shadow overflow-hidden sm:rounded-md",
317                div {
318                    class: "p-6",
319                    EmptyState {
320                        icon: "👥".to_string(),
321                        title: "No users found".to_string(),
322                        description: "Try adjusting your search criteria".to_string(),
323                    }
324                }
325            }
326        }
327    } else {
328        rsx! {
329            div {
330                class: "bg-white shadow overflow-hidden sm:rounded-md",
331                ul {
332                    class: "divide-y divide-gray-200",
333                    for user in filtered_users {
334                        UserListItem { key: "{user.id}", user: user.clone() }
335                    }
336                }
337            }
338        }
339    };
340
341    rsx! {
342        div {
343            class: "space-y-6",
344            {search_bar}
345            {users_table}
346        }
347    }
348}
349
350/// Individual user list item component
351#[component]
352fn UserListItem(user: MockUser) -> Element {
353    let user_initial = user.name.chars().next().unwrap_or('U');
354    let status_class = match user.status.as_str() {
355        "Active" => "bg-green-100 text-green-800",
356        "Inactive" => "bg-gray-100 text-gray-800",
357        _ => "bg-red-100 text-red-800",
358    };
359
360    rsx! {
361        li {
362            class: "px-6 py-4 hover:bg-gray-50",
363            div {
364                class: "flex items-center justify-between",
365                div {
366                    class: "flex items-center",
367                    div {
368                        class: "flex-shrink-0 h-10 w-10",
369                        div {
370                            class: "h-10 w-10 rounded-full bg-blue-500 flex items-center justify-center",
371                            span {
372                                class: "text-sm font-medium text-white",
373                                "{user_initial}"
374                            }
375                        }
376                    }
377                    div {
378                        class: "ml-4",
379                        div {
380                            class: "flex items-center",
381                            p {
382                                class: "text-sm font-medium text-gray-900",
383                                "{user.name}"
384                            }
385                            span {
386                                class: "ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium {status_class}",
387                                "{user.status}"
388                            }
389                        }
390                        p {
391                            class: "text-sm text-gray-500",
392                            "{user.email}"
393                        }
394                        p {
395                            class: "text-sm text-gray-500",
396                            "Role: {user.role} • Last login: {user.last_login}"
397                        }
398                    }
399                }
400                div {
401                    class: "flex items-center space-x-2",
402                    button {
403                        r#type: "button",
404                        class: "text-blue-600 hover:text-blue-900 text-sm font-medium",
405                        "Edit"
406                    }
407                    button {
408                        r#type: "button",
409                        class: "text-red-600 hover:text-red-900 text-sm font-medium",
410                        "Disable"
411                    }
412                }
413            }
414        }
415    }
416}
417
418/// System monitoring tab
419#[component]
420fn SystemTab() -> Element {
421    let system_metrics = rsx! {
422        div {
423            class: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6",
424
425            MetricCard {
426                title: "CPU Usage".to_string(),
427                value: "23%".to_string(),
428                status: "healthy".to_string(),
429                icon: "🖥️".to_string()
430            }
431
432            MetricCard {
433                title: "Memory Usage".to_string(),
434                value: "67%".to_string(),
435                status: "warning".to_string(),
436                icon: "💾".to_string()
437            }
438
439            MetricCard {
440                title: "Storage".to_string(),
441                value: "45%".to_string(),
442                status: "healthy".to_string(),
443                icon: "💿".to_string()
444            }
445
446            MetricCard {
447                title: "Network".to_string(),
448                value: "12 MB/s".to_string(),
449                status: "healthy".to_string(),
450                icon: "🌐".to_string()
451            }
452        }
453    };
454
455    let system_services = rsx! {
456        div {
457            class: "bg-white shadow rounded-lg",
458            div {
459                class: "px-4 py-5 sm:px-6 border-b border-gray-200",
460                h3 {
461                    class: "text-lg leading-6 font-medium text-gray-900",
462                    "System Services"
463                }
464            }
465            div {
466                class: "px-4 py-5 sm:p-6",
467                SystemServicesList {}
468            }
469        }
470    };
471
472    rsx! {
473        div {
474            class: "space-y-6",
475            {system_metrics}
476            {system_services}
477        }
478    }
479}
480
481/// Admin plugins management tab
482#[component]
483fn PluginsTab() -> Element {
484    let plugins = get_admin_plugins();
485
486    let plugin_header = rsx! {
487        div {
488            class: "px-4 py-5 sm:px-6 border-b border-gray-200",
489            div {
490                class: "flex justify-between items-center",
491                h3 {
492                    class: "text-lg leading-6 font-medium text-gray-900",
493                    "Plugin Management"
494                }
495                button {
496                    r#type: "button",
497                    class: "inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500",
498                    "Install Plugin"
499                }
500            }
501        }
502    };
503
504    let plugin_list = rsx! {
505        div {
506            class: "px-4 py-5 sm:p-6",
507            div {
508                class: "space-y-4",
509                for plugin in plugins {
510                    PluginListItem { key: "{plugin.id}", plugin: plugin.clone() }
511                }
512            }
513        }
514    };
515
516    rsx! {
517        div {
518            class: "space-y-6",
519            div {
520                class: "bg-white shadow rounded-lg",
521                {plugin_header}
522                {plugin_list}
523            }
524        }
525    }
526}
527
528/// Individual plugin list item component
529#[component]
530fn PluginListItem(plugin: AdminPlugin) -> Element {
531    let action_buttons = if plugin.status == "Active" {
532        rsx! {
533            button {
534                r#type: "button",
535                class: "text-yellow-600 hover:text-yellow-900 text-sm font-medium",
536                "Disable"
537            }
538        }
539    } else {
540        rsx! {
541            button {
542                r#type: "button",
543                class: "text-green-600 hover:text-green-900 text-sm font-medium",
544                "Enable"
545            }
546        }
547    };
548
549    rsx! {
550        div {
551            class: "flex items-center justify-between p-4 border border-gray-200 rounded-lg",
552            div {
553                class: "flex items-center",
554                span {
555                    class: "text-2xl mr-4",
556                    "{plugin.icon}"
557                }
558                div {
559                    h4 {
560                        class: "text-sm font-medium text-gray-900",
561                        "{plugin.name}"
562                    }
563                    p {
564                        class: "text-sm text-gray-500",
565                        "v{plugin.version} • {plugin.status}"
566                    }
567                }
568            }
569            div {
570                class: "flex items-center space-x-2",
571                {action_buttons}
572                button {
573                    r#type: "button",
574                    class: "text-blue-600 hover:text-blue-900 text-sm font-medium",
575                    "Configure"
576                }
577                button {
578                    r#type: "button",
579                    class: "text-red-600 hover:text-red-900 text-sm font-medium",
580                    "Uninstall"
581                }
582            }
583        }
584    }
585}
586
587/// System logs tab
588#[component]
589fn LogsTab() -> Element {
590    let logs = get_system_logs();
591
592    let logs_header = rsx! {
593        div {
594            class: "px-4 py-5 sm:px-6 border-b border-gray-200",
595            div {
596                class: "flex justify-between items-center",
597                h3 {
598                    class: "text-lg leading-6 font-medium text-gray-900",
599                    "System Logs"
600                }
601                div {
602                    class: "flex space-x-2",
603                    button {
604                        r#type: "button",
605                        class: "inline-flex items-center px-3 py-2 border border-gray-300 shadow-sm text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500",
606                        "Clear Logs"
607                    }
608                    button {
609                        r#type: "button",
610                        class: "inline-flex items-center px-3 py-2 border border-gray-300 shadow-sm text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500",
611                        "Export"
612                    }
613                }
614            }
615        }
616    };
617
618    let logs_list = rsx! {
619        div {
620            class: "px-4 py-5 sm:p-6",
621            div {
622                class: "max-h-96 overflow-y-auto",
623                div {
624                    class: "space-y-2",
625                    for log in logs {
626                        LogEntry { key: "{log.id}", log: log.clone() }
627                    }
628                }
629            }
630        }
631    };
632
633    rsx! {
634        div {
635            class: "space-y-6",
636            div {
637                class: "bg-white shadow rounded-lg",
638                {logs_header}
639                {logs_list}
640            }
641        }
642    }
643}
644
645/// Individual log entry component
646#[component]
647fn LogEntry(log: SystemLog) -> Element {
648    let log_class = match log.level.as_str() {
649        "ERROR" => "bg-red-50 text-red-800 border-l-4 border-red-400",
650        "WARN" => "bg-yellow-50 text-yellow-800 border-l-4 border-yellow-400",
651        "INFO" => "bg-blue-50 text-blue-800 border-l-4 border-blue-400",
652        _ => "bg-gray-50 text-gray-800 border-l-4 border-gray-400",
653    };
654
655    let log_details = if !log.details.is_empty() {
656        rsx! {
657            div {
658                class: "mt-1 text-xs opacity-75",
659                "{log.details}"
660            }
661        }
662    } else {
663        rsx! {}
664    };
665
666    rsx! {
667        div {
668            class: "p-3 rounded-md text-sm font-mono {log_class}",
669            div {
670                class: "flex justify-between items-start",
671                div {
672                    class: "flex-1",
673                    span {
674                        class: "font-semibold mr-2",
675                        "[{log.level}]"
676                    }
677                    span { "{log.message}" }
678                }
679                span {
680                    class: "text-xs opacity-75 ml-4",
681                    "{log.timestamp}"
682                }
683            }
684            {log_details}
685        }
686    }
687}
688
689// Helper components
690#[component]
691fn RecentActivityList() -> Element {
692    let activities = get_recent_admin_activities();
693
694    rsx! {
695        div {
696            class: "space-y-3",
697            for activity in activities {
698                div {
699                    key: "{activity.id}",
700                    class: "flex items-start space-x-3",
701                    div {
702                        class: "flex-shrink-0",
703                        span {
704                            class: "inline-flex items-center justify-center h-8 w-8 rounded-full text-white text-sm {activity.color}",
705                            "{activity.icon}"
706                        }
707                    }
708                    div {
709                        class: "min-w-0 flex-1",
710                        p {
711                            class: "text-sm text-gray-900",
712                            "{activity.action}"
713                        }
714                        p {
715                            class: "text-sm text-gray-500",
716                            "by {activity.user} • {activity.timestamp}"
717                        }
718                    }
719                }
720            }
721        }
722    }
723}
724
725#[component]
726fn SystemAlerts() -> Element {
727    let alerts = get_system_alerts();
728
729    if alerts.is_empty() {
730        rsx! {
731            div {
732                class: "text-center py-4",
733                span {
734                    class: "text-2xl mb-2 block",
735                    "✅"
736                }
737                p {
738                    class: "text-sm text-gray-500",
739                    "No active alerts"
740                }
741            }
742        }
743    } else {
744        rsx! {
745            div {
746                class: "space-y-3",
747                for alert in alerts {
748                    SystemAlertItem { key: "{alert.id}", alert: alert.clone() }
749                }
750            }
751        }
752    }
753}
754
755#[component]
756fn SystemAlertItem(alert: SystemAlert) -> Element {
757    let alert_class = match alert.severity.as_str() {
758        "critical" => "bg-red-50 border border-red-200",
759        "warning" => "bg-yellow-50 border border-yellow-200",
760        _ => "bg-blue-50 border border-blue-200",
761    };
762
763    let text_color = match alert.severity.as_str() {
764        "critical" => ("text-red-800", "text-red-700"),
765        "warning" => ("text-yellow-800", "text-yellow-700"),
766        _ => ("text-blue-800", "text-blue-700"),
767    };
768
769    rsx! {
770        div {
771            class: "p-3 rounded-md {alert_class}",
772            div {
773                class: "flex items-start",
774                div {
775                    class: "flex-shrink-0",
776                    span {
777                        class: "text-lg",
778                        "{alert.icon}"
779                    }
780                }
781                div {
782                    class: "ml-3 flex-1",
783                    p {
784                        class: "text-sm font-medium {text_color.0}",
785                        "{alert.title}"
786                    }
787                    p {
788                        class: "text-sm {text_color.1}",
789                        "{alert.message}"
790                    }
791                }
792            }
793        }
794    }
795}
796
797#[component]
798fn MetricCard(title: String, value: String, status: String, icon: String) -> Element {
799    let status_color = match status.as_str() {
800        "healthy" => "text-green-600",
801        "warning" => "text-yellow-600",
802        "critical" => "text-red-600",
803        _ => "text-gray-600",
804    };
805
806    rsx! {
807        div {
808            class: "bg-white overflow-hidden shadow rounded-lg",
809            div {
810                class: "p-5",
811                div {
812                    class: "flex items-center",
813                    div {
814                        class: "flex-shrink-0",
815                        span {
816                            class: "text-2xl",
817                            "{icon}"
818                        }
819                    }
820                    div {
821                        class: "ml-5 w-0 flex-1",
822                        dl {
823                            dt {
824                                class: "text-sm font-medium text-gray-500 truncate",
825                                "{title}"
826                            }
827                            dd {
828                                class: "flex items-baseline",
829                                div {
830                                    class: "text-2xl font-semibold {status_color}",
831                                    "{value}"
832                                }
833                            }
834                        }
835                    }
836                }
837            }
838        }
839    }
840}
841
842#[component]
843fn SystemServicesList() -> Element {
844    let services = get_system_services();
845
846    rsx! {
847        div {
848            class: "space-y-3",
849            for service in services {
850                SystemServiceItem { key: "{service.name}", service: service.clone() }
851            }
852        }
853    }
854}
855
856#[component]
857fn SystemServiceItem(service: SystemService) -> Element {
858    let status_dot_class = if service.running {
859        "bg-green-400"
860    } else {
861        "bg-red-400"
862    };
863    let status_badge_class = if service.running {
864        "bg-green-100 text-green-800"
865    } else {
866        "bg-red-100 text-red-800"
867    };
868    let status_text = if service.running {
869        "Running"
870    } else {
871        "Stopped"
872    };
873
874    let action_button = if service.running {
875        rsx! {
876            button {
877                r#type: "button",
878                class: "text-red-600 hover:text-red-900 text-sm font-medium",
879                "Stop"
880            }
881        }
882    } else {
883        rsx! {
884            button {
885                r#type: "button",
886                class: "text-green-600 hover:text-green-900 text-sm font-medium",
887                "Start"
888            }
889        }
890    };
891
892    rsx! {
893        div {
894            class: "flex items-center justify-between py-2",
895            div {
896                class: "flex items-center",
897                div {
898                    class: "h-3 w-3 rounded-full mr-3 {status_dot_class}"
899                }
900                div {
901                    p {
902                        class: "text-sm font-medium text-gray-900",
903                        "{service.name}"
904                    }
905                    p {
906                        class: "text-sm text-gray-500",
907                        "{service.description}"
908                    }
909                }
910            }
911            div {
912                class: "flex items-center space-x-2",
913                span {
914                    class: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium {status_badge_class}",
915                    "{status_text}"
916                }
917                {action_button}
918            }
919        }
920    }
921}
922
923// Mock data structures and functions
924#[derive(Debug, Clone)]
925struct AdminStat {
926    id: String,
927    title: String,
928    value: String,
929    change: Option<String>,
930    trend: Option<StatTrend>,
931    icon: Option<String>,
932}
933
934#[derive(Debug, Clone, PartialEq)]
935struct MockUser {
936    id: String,
937    name: String,
938    email: String,
939    role: String,
940    status: String,
941    last_login: String,
942}
943
944#[derive(Debug, Clone, PartialEq)]
945struct AdminActivity {
946    id: String,
947    action: String,
948    user: String,
949    timestamp: String,
950    icon: String,
951    color: String,
952}
953
954#[derive(Debug, Clone, PartialEq)]
955struct SystemAlert {
956    id: String,
957    title: String,
958    message: String,
959    severity: String,
960    icon: String,
961}
962
963#[derive(Debug, Clone, PartialEq)]
964struct AdminPlugin {
965    id: String,
966    name: String,
967    version: String,
968    status: String,
969    icon: String,
970}
971
972#[derive(Debug, Clone, PartialEq)]
973struct SystemService {
974    name: String,
975    description: String,
976    running: bool,
977}
978
979#[derive(Debug, Clone, PartialEq)]
980struct SystemLog {
981    id: String,
982    level: String,
983    message: String,
984    timestamp: String,
985    details: String,
986}
987
988fn get_system_stats() -> Vec<AdminStat> {
989    vec![
990        AdminStat {
991            id: "total_users".to_string(),
992            title: "Total Users".to_string(),
993            value: "1,234".to_string(),
994            change: Some("+12%".to_string()),
995            trend: Some(StatTrend::Up),
996            icon: Some("👥".to_string()),
997        },
998        AdminStat {
999            id: "active_sessions".to_string(),
1000            title: "Active Sessions".to_string(),
1001            value: "87".to_string(),
1002            change: Some("-5%".to_string()),
1003            trend: Some(StatTrend::Down),
1004            icon: Some("🔐".to_string()),
1005        },
1006        AdminStat {
1007            id: "system_uptime".to_string(),
1008            title: "System Uptime".to_string(),
1009            value: "99.9%".to_string(),
1010            change: None,
1011            trend: None,
1012            icon: Some("⚡".to_string()),
1013        },
1014        AdminStat {
1015            id: "storage_used".to_string(),
1016            title: "Storage Used".to_string(),
1017            value: "45%".to_string(),
1018            change: Some("+3%".to_string()),
1019            trend: Some(StatTrend::Up),
1020            icon: Some("💾".to_string()),
1021        },
1022    ]
1023}
1024
1025fn get_mock_users() -> Vec<MockUser> {
1026    vec![
1027        MockUser {
1028            id: "1".to_string(),
1029            name: "John Doe".to_string(),
1030            email: "john.doe@example.com".to_string(),
1031            role: "Administrator".to_string(),
1032            status: "Active".to_string(),
1033            last_login: "2 hours ago".to_string(),
1034        },
1035        MockUser {
1036            id: "2".to_string(),
1037            name: "Jane Smith".to_string(),
1038            email: "jane.smith@example.com".to_string(),
1039            role: "User".to_string(),
1040            status: "Active".to_string(),
1041            last_login: "1 day ago".to_string(),
1042        },
1043        MockUser {
1044            id: "3".to_string(),
1045            name: "Bob Johnson".to_string(),
1046            email: "bob.johnson@example.com".to_string(),
1047            role: "Moderator".to_string(),
1048            status: "Inactive".to_string(),
1049            last_login: "1 week ago".to_string(),
1050        },
1051    ]
1052}
1053
1054fn get_recent_admin_activities() -> Vec<AdminActivity> {
1055    vec![
1056        AdminActivity {
1057            id: "1".to_string(),
1058            action: "User created".to_string(),
1059            user: "admin".to_string(),
1060            timestamp: "5 minutes ago".to_string(),
1061            icon: "👤".to_string(),
1062            color: "bg-green-500".to_string(),
1063        },
1064        AdminActivity {
1065            id: "2".to_string(),
1066            action: "Plugin installed".to_string(),
1067            user: "admin".to_string(),
1068            timestamp: "1 hour ago".to_string(),
1069            icon: "🧩".to_string(),
1070            color: "bg-blue-500".to_string(),
1071        },
1072        AdminActivity {
1073            id: "3".to_string(),
1074            action: "System backup completed".to_string(),
1075            user: "system".to_string(),
1076            timestamp: "3 hours ago".to_string(),
1077            icon: "💾".to_string(),
1078            color: "bg-gray-500".to_string(),
1079        },
1080    ]
1081}
1082
1083fn get_system_alerts() -> Vec<SystemAlert> {
1084    vec![SystemAlert {
1085        id: "1".to_string(),
1086        title: "High Memory Usage".to_string(),
1087        message: "System memory usage is above 75%".to_string(),
1088        severity: "warning".to_string(),
1089        icon: "⚠️".to_string(),
1090    }]
1091}
1092
1093fn get_admin_plugins() -> Vec<AdminPlugin> {
1094    vec![
1095        AdminPlugin {
1096            id: "inventory".to_string(),
1097            name: "Inventory Management".to_string(),
1098            version: "2.1.0".to_string(),
1099            status: "Active".to_string(),
1100            icon: "📦".to_string(),
1101        },
1102        AdminPlugin {
1103            id: "backup".to_string(),
1104            name: "Backup & Sync".to_string(),
1105            version: "3.0.1".to_string(),
1106            status: "Active".to_string(),
1107            icon: "☁️".to_string(),
1108        },
1109        AdminPlugin {
1110            id: "analytics".to_string(),
1111            name: "Analytics Dashboard".to_string(),
1112            version: "1.5.2".to_string(),
1113            status: "Inactive".to_string(),
1114            icon: "📊".to_string(),
1115        },
1116    ]
1117}
1118
1119fn get_system_services() -> Vec<SystemService> {
1120    vec![
1121        SystemService {
1122            name: "Web Server".to_string(),
1123            description: "HTTP/HTTPS web server".to_string(),
1124            running: true,
1125        },
1126        SystemService {
1127            name: "Database".to_string(),
1128            description: "Primary database service".to_string(),
1129            running: true,
1130        },
1131        SystemService {
1132            name: "Cache Service".to_string(),
1133            description: "Redis cache service".to_string(),
1134            running: false,
1135        },
1136        SystemService {
1137            name: "Background Tasks".to_string(),
1138            description: "Task queue processor".to_string(),
1139            running: true,
1140        },
1141    ]
1142}
1143
1144fn get_system_logs() -> Vec<SystemLog> {
1145    vec![
1146        SystemLog {
1147            id: "1".to_string(),
1148            level: "INFO".to_string(),
1149            message: "User login successful".to_string(),
1150            timestamp: "2024-01-15 10:30:25".to_string(),
1151            details: "user_id=123, ip=192.168.1.100".to_string(),
1152        },
1153        SystemLog {
1154            id: "2".to_string(),
1155            level: "WARN".to_string(),
1156            message: "High memory usage detected".to_string(),
1157            timestamp: "2024-01-15 10:25:15".to_string(),
1158            details: "memory_usage=78%, threshold=75%".to_string(),
1159        },
1160        SystemLog {
1161            id: "3".to_string(),
1162            level: "ERROR".to_string(),
1163            message: "Database connection failed".to_string(),
1164            timestamp: "2024-01-15 10:20:45".to_string(),
1165            details: "connection_timeout=5000ms, retries=3".to_string(),
1166        },
1167        SystemLog {
1168            id: "4".to_string(),
1169            level: "INFO".to_string(),
1170            message: "Plugin installed successfully".to_string(),
1171            timestamp: "2024-01-15 10:15:30".to_string(),
1172            details: "plugin=inventory-management, version=2.1.0".to_string(),
1173        },
1174    ]
1175}
1176
1177#[cfg(test)]
1178mod tests {
1179    use super::*;
1180
1181    #[test]
1182    fn test_admin_component_creation() {
1183        let _admin = rsx! { Admin {} };
1184    }
1185
1186    #[test]
1187    fn test_mock_data() {
1188        let users = get_mock_users();
1189        let activities = get_recent_admin_activities();
1190        let alerts = get_system_alerts();
1191
1192        assert!(!users.is_empty());
1193        assert!(!activities.is_empty());
1194        // alerts might be empty (no current alerts)
1195    }
1196}