qorzen_oxide/ui/layout/
footer.rs

1// src/ui/layout/footer.rs - Application footer with links and information
2
3use crate::utils::Time;
4use chrono::Datelike;
5use dioxus::prelude::*;
6#[allow(unused_imports)]
7use dioxus_router::prelude::*;
8
9use crate::ui::router::Route;
10
11/// Footer component
12#[component]
13pub fn Footer() -> Element {
14    let current_year = Time::now().year();
15    let build = option_env!("BUILD_HASH").unwrap_or("dev");
16
17    rsx! {
18        footer {
19            class: "bg-white border-t border-gray-200 mt-auto",
20            div {
21                class: "mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-6",
22
23                // Main footer content
24                div {
25                    class: "md:flex md:items-center md:justify-between",
26
27                    // Left side - Links
28                    div {
29                        class: "flex flex-wrap justify-center md:justify-start space-x-6 md:order-2",
30
31                        // Internal links
32                        Link {
33                            to: Route::Dashboard {},
34                            class: "text-sm text-gray-500 hover:text-gray-600 transition-colors",
35                            "Dashboard"
36                        }
37
38                        // External links
39                        a {
40                            href: "https://docs.qorzen.com",
41                            target: "_blank",
42                            rel: "noopener noreferrer",
43                            class: "text-sm text-gray-500 hover:text-gray-600 transition-colors",
44                            "Documentation"
45                        }
46
47                        a {
48                            href: "https://github.com/qorzen/qorzen-oxide",
49                            target: "_blank",
50                            rel: "noopener noreferrer",
51                            class: "text-sm text-gray-500 hover:text-gray-600 transition-colors",
52                            "GitHub"
53                        }
54
55                        a {
56                            href: "mailto:support@qorzen.com",
57                            class: "text-sm text-gray-500 hover:text-gray-600 transition-colors",
58                            "Support"
59                        }
60
61                        a {
62                            href: "/privacy",
63                            class: "text-sm text-gray-500 hover:text-gray-600 transition-colors",
64                            "Privacy"
65                        }
66
67                        a {
68                            href: "/terms",
69                            class: "text-sm text-gray-500 hover:text-gray-600 transition-colors",
70                            "Terms"
71                        }
72                    }
73
74                    // Right side - Copyright and version
75                    div {
76                        class: "mt-4 md:mt-0 md:order-1",
77                        div {
78                            class: "flex flex-col items-center md:items-start space-y-1",
79                            p {
80                                class: "text-sm text-gray-500",
81                                "© {current_year} Qorzen. All rights reserved."
82                            }
83                            p {
84                                class: "text-xs text-gray-400",
85                                "Version {crate::VERSION}"
86                            }
87                        }
88                    }
89                }
90
91                // Additional footer information
92                div {
93                    class: "mt-6 pt-6 border-t border-gray-200",
94                    div {
95                        class: "flex flex-col sm:flex-row sm:items-center sm:justify-between text-xs text-gray-400 space-y-2 sm:space-y-0",
96
97                        // System status
98                        div {
99                            class: "flex items-center space-x-4",
100                            SystemStatus {}
101                            div {
102                                class: "flex items-center",
103                                span {
104                                    class: "inline-block w-2 h-2 bg-green-400 rounded-full mr-1"
105                                }
106                                "All systems operational"
107                            }
108                        }
109
110                        // Build info
111                        div {
112                            class: "flex items-center space-x-4",
113                            span { "Built with ❤️ and 🦀" }
114                            span {
115                                "Build: {build}",
116                            }
117                        }
118                    }
119                }
120            }
121        }
122    }
123}
124
125/// System status indicator component
126#[component]
127fn SystemStatus() -> Element {
128    // In a real app, this would check actual system health
129    let status = use_signal(|| "healthy");
130    let last_check = use_signal(Time::now);
131
132    // Simulate periodic health checks
133    use_effect({
134        let mut status = status;
135        let mut last_check = last_check;
136
137        move || {
138            spawn(async move {
139                loop {
140                    #[cfg(not(target_arch = "wasm32"))]
141                    tokio::time::sleep(std::time::Duration::from_secs(30)).await;
142                    #[cfg(target_arch = "wasm32")]
143                    gloo_timers::future::TimeoutFuture::new(30000).await;
144
145                    // Mock health check
146                    let health_good = rand::random::<f32>() > 0.1; // 90% chance of good health
147                    status.set(if health_good { "healthy" } else { "degraded" });
148                    last_check.set(Time::now());
149                }
150            });
151        }
152    });
153
154    let (status_color, status_text) = match status() {
155        "healthy" => ("bg-green-400", "Healthy"),
156        "degraded" => ("bg-yellow-400", "Degraded"),
157        "down" => ("bg-red-400", "Down"),
158        _ => ("bg-gray-400", "Unknown"),
159    };
160
161    fn fmt_time(ts: chrono::DateTime<chrono::Utc>) -> String {
162        ts.format("%H:%M:%S UTC").to_string()
163    }
164
165    rsx! {
166        div {
167            class: "flex items-center space-x-1",
168            title: "{fmt_time(last_check())}",
169            span {
170                class: format!("inline-block w-2 h-2 rounded-full {}", status_color)
171            }
172            span { "System: {status_text}" }
173        }
174    }
175}
176
177/// Expandable footer variant with more information
178#[component]
179pub fn ExpandedFooter() -> Element {
180    let current_year = Time::now().year();
181
182    rsx! {
183        footer {
184            class: "bg-gray-50 border-t border-gray-200 mt-auto",
185            div {
186                class: "mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-12",
187
188                // Main footer grid
189                div {
190                    class: "grid grid-cols-1 md:grid-cols-4 gap-8",
191
192                    // Company info
193                    div {
194                        class: "col-span-1 md:col-span-2",
195                        div {
196                            class: "flex items-center mb-4",
197                            div {
198                                class: "h-8 w-8 bg-blue-600 rounded-lg flex items-center justify-center mr-3",
199                                span {
200                                    class: "text-white font-bold text-sm",
201                                    "Q"
202                                }
203                            }
204                            span {
205                                class: "text-xl font-bold text-gray-900",
206                                "Qorzen"
207                            }
208                        }
209                        p {
210                            class: "text-sm text-gray-600 mb-4 max-w-md",
211                            "A modular, cross-platform application framework built with Rust and Dioxus.
212                             Extensible through plugins and designed for modern development workflows."
213                        }
214                        div {
215                            class: "flex space-x-4",
216                            // Social links would go here
217                            a {
218                                href: "https://github.com/qorzen",
219                                target: "_blank",
220                                rel: "noopener noreferrer",
221                                class: "text-gray-400 hover:text-gray-600 transition-colors",
222                                "GitHub"
223                            }
224                            a {
225                                href: "https://twitter.com/qorzen",
226                                target: "_blank",
227                                rel: "noopener noreferrer",
228                                class: "text-gray-400 hover:text-gray-600 transition-colors",
229                                "Twitter"
230                            }
231                        }
232                    }
233
234                    // Product links
235                    div {
236                        h3 {
237                            class: "text-sm font-semibold text-gray-900 tracking-wider uppercase mb-4",
238                            "Product"
239                        }
240                        ul {
241                            class: "space-y-2",
242                            li {
243                                Link {
244                                    to: Route::Dashboard {},
245                                    class: "text-sm text-gray-600 hover:text-gray-900 transition-colors",
246                                    "Dashboard"
247                                }
248                            }
249                            li {
250                                Link {
251                                    to: Route::Plugins {},
252                                    class: "text-sm text-gray-600 hover:text-gray-900 transition-colors",
253                                    "Plugins"
254                                }
255                            }
256                            li {
257                                Link {
258                                    to: Route::Settings {},
259                                    class: "text-sm text-gray-600 hover:text-gray-900 transition-colors",
260                                    "Settings"
261                                }
262                            }
263                            li {
264                                a {
265                                    href: "/api/docs",
266                                    class: "text-sm text-gray-600 hover:text-gray-900 transition-colors",
267                                    "API Documentation"
268                                }
269                            }
270                        }
271                    }
272
273                    // Support links
274                    div {
275                        h3 {
276                            class: "text-sm font-semibold text-gray-900 tracking-wider uppercase mb-4",
277                            "Support"
278                        }
279                        ul {
280                            class: "space-y-2",
281                            li {
282                                a {
283                                    href: "https://docs.qorzen.com",
284                                    target: "_blank",
285                                    rel: "noopener noreferrer",
286                                    class: "text-sm text-gray-600 hover:text-gray-900 transition-colors",
287                                    "Documentation"
288                                }
289                            }
290                            li {
291                                a {
292                                    href: "https://docs.qorzen.com/guides",
293                                    target: "_blank",
294                                    rel: "noopener noreferrer",
295                                    class: "text-sm text-gray-600 hover:text-gray-900 transition-colors",
296                                    "Guides"
297                                }
298                            }
299                            li {
300                                a {
301                                    href: "mailto:support@qorzen.com",
302                                    class: "text-sm text-gray-600 hover:text-gray-900 transition-colors",
303                                    "Contact Support"
304                                }
305                            }
306                            li {
307                                a {
308                                    href: "https://status.qorzen.com",
309                                    target: "_blank",
310                                    rel: "noopener noreferrer",
311                                    class: "text-sm text-gray-600 hover:text-gray-900 transition-colors",
312                                    "System Status"
313                                }
314                            }
315                        }
316                    }
317                }
318
319                // Bottom section
320                div {
321                    class: "mt-12 pt-8 border-t border-gray-200",
322                    div {
323                        class: "md:flex md:items-center md:justify-between",
324                        div {
325                            class: "flex space-x-6 md:order-2",
326                            a {
327                                href: "/privacy",
328                                class: "text-sm text-gray-500 hover:text-gray-600 transition-colors",
329                                "Privacy Policy"
330                            }
331                            a {
332                                href: "/terms",
333                                class: "text-sm text-gray-500 hover:text-gray-600 transition-colors",
334                                "Terms of Service"
335                            }
336                            a {
337                                href: "/cookies",
338                                class: "text-sm text-gray-500 hover:text-gray-600 transition-colors",
339                                "Cookie Policy"
340                            }
341                        }
342                        p {
343                            class: "mt-4 text-sm text-gray-500 md:mt-0 md:order-1",
344                            "© {current_year} Qorzen. All rights reserved. Version {crate::VERSION}"
345                        }
346                    }
347                }
348            }
349        }
350    }
351}
352
353#[cfg(test)]
354mod tests {
355    use super::*;
356    use dioxus::prelude::*;
357
358    #[test]
359    fn test_footer_component_creation() {
360        let _footer = rsx! { Footer {} };
361    }
362
363    #[test]
364    fn test_expanded_footer_component_creation() {
365        let _footer = rsx! { ExpandedFooter {} };
366    }
367
368    #[test]
369    fn test_system_status_component_creation() {
370        let _status = rsx! { SystemStatus {} };
371    }
372}