1use dioxus::prelude::*;
4
5use crate::ui::pages::{EmptyState, PageWrapper, StatCard, StatTrend};
6
7#[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#[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 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 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#[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 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#[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#[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#[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#[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#[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#[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#[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#[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 }
1196}