qorzen_oxide/ui/pages/
not_found.rs

1// src/ui/pages/not_found.rs - 404 Not Found page
2
3use dioxus::prelude::*;
4#[allow(unused_imports)]
5use dioxus_router::prelude::*;
6
7use crate::ui::router::Route;
8
9/// 404 Not Found page component
10#[component]
11pub fn NotFound(#[props(default = "".to_string())] path: String) -> Element {
12    rsx! {
13        div {
14            class: "min-h-screen bg-white px-4 py-16 sm:px-6 sm:py-24 md:grid md:place-items-center lg:px-8",
15            div {
16                class: "max-w-max mx-auto",
17                main {
18                    class: "sm:flex",
19                    p {
20                        class: "text-4xl font-extrabold text-blue-600 sm:text-5xl",
21                        "404"
22                    }
23                    div {
24                        class: "sm:ml-6",
25                        div {
26                            class: "sm:border-l sm:border-gray-200 sm:pl-6",
27                            h1 {
28                                class: "text-4xl font-extrabold text-gray-900 tracking-tight sm:text-5xl",
29                                "Page not found"
30                            }
31                            p {
32                                class: "mt-1 text-base text-gray-500",
33                                "Sorry, we couldn't find the page you're looking for."
34                            }
35                            if !path.is_empty() {
36                                p {
37                                    class: "mt-2 text-sm text-gray-400 font-mono bg-gray-100 px-2 py-1 rounded",
38                                    "Path: /{path}"
39                                }
40                            }
41                        }
42                        div {
43                            class: "mt-10 flex space-x-3 sm:border-l sm:border-transparent sm:pl-6",
44                            Link {
45                                to: Route::Dashboard {},
46                                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",
47                                svg {
48                                    class: "-ml-1 mr-2 h-4 w-4",
49                                    xmlns: "http://www.w3.org/2000/svg",
50                                    fill: "none",
51                                    view_box: "0 0 24 24",
52                                    stroke: "currentColor",
53                                    path {
54                                        stroke_linecap: "round",
55                                        stroke_linejoin: "round",
56                                        stroke_width: "2",
57                                        d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
58                                    }
59                                }
60                                "Go back home"
61                            }
62                            button {
63                                r#type: "button",
64                                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",
65                                onclick: move |_| {
66                                    // Go back in history
67                                    #[cfg(target_arch = "wasm32")]
68                                    web_sys::window().unwrap().history().unwrap().back().unwrap();
69                                },
70                                svg {
71                                    class: "-ml-1 mr-2 h-4 w-4",
72                                    xmlns: "http://www.w3.org/2000/svg",
73                                    fill: "none",
74                                    view_box: "0 0 24 24",
75                                    stroke: "currentColor",
76                                    path {
77                                        stroke_linecap: "round",
78                                        stroke_linejoin: "round",
79                                        stroke_width: "2",
80                                        d: "M10 19l-7-7m0 0l7-7m-7 7h18"
81                                    }
82                                }
83                                "Go back"
84                            }
85                        }
86                    }
87                }
88
89                // Helpful suggestions
90                div {
91                    class: "mt-16",
92                    div {
93                        class: "bg-gray-50 rounded-lg px-6 py-8",
94                        h2 {
95                            class: "text-lg font-medium text-gray-900 mb-4",
96                            "What you can do:"
97                        }
98                        ul {
99                            class: "space-y-3 text-sm text-gray-600",
100                            li {
101                                class: "flex items-start",
102                                svg {
103                                    class: "flex-shrink-0 h-5 w-5 text-green-500 mt-0.5 mr-3",
104                                    xmlns: "http://www.w3.org/2000/svg",
105                                    view_box: "0 0 20 20",
106                                    fill: "currentColor",
107                                    path {
108                                        fill_rule: "evenodd",
109                                        d: "M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z",
110                                        clip_rule: "evenodd"
111                                    }
112                                }
113                                "Check the URL for any typos"
114                            }
115                            li {
116                                class: "flex items-start",
117                                svg {
118                                    class: "flex-shrink-0 h-5 w-5 text-green-500 mt-0.5 mr-3",
119                                    xmlns: "http://www.w3.org/2000/svg",
120                                    view_box: "0 0 20 20",
121                                    fill: "currentColor",
122                                    path {
123                                        fill_rule: "evenodd",
124                                        d: "M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z",
125                                        clip_rule: "evenodd"
126                                    }
127                                }
128                                "Use the navigation menu to find what you're looking for"
129                            }
130                            li {
131                                class: "flex items-start",
132                                svg {
133                                    class: "flex-shrink-0 h-5 w-5 text-green-500 mt-0.5 mr-3",
134                                    xmlns: "http://www.w3.org/2000/svg",
135                                    view_box: "0 0 20 20",
136                                    fill: "currentColor",
137                                    path {
138                                        fill_rule: "evenodd",
139                                        d: "M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z",
140                                        clip_rule: "evenodd"
141                                    }
142                                }
143                                "Contact support if you believe this is an error"
144                            }
145                        }
146                    }
147                }
148
149                // Popular pages
150                div {
151                    class: "mt-8",
152                    h3 {
153                        class: "text-sm font-medium text-gray-900 mb-4",
154                        "Popular pages:"
155                    }
156                    div {
157                        class: "grid grid-cols-1 sm:grid-cols-2 gap-4",
158
159                        // Dashboard link
160                        Link {
161                            to: Route::Dashboard {},
162                            class: "group relative rounded-lg p-6 bg-white border border-gray-300 shadow-sm hover:shadow-md transition-shadow",
163                            div {
164                                class: "flex items-center",
165                                div {
166                                    class: "flex-shrink-0",
167                                    span {
168                                        class: "text-2xl",
169                                        "📊"
170                                    }
171                                }
172                                div {
173                                    class: "ml-4 min-w-0 flex-1",
174                                    p {
175                                        class: "text-base font-medium text-gray-900 group-hover:text-blue-600",
176                                        "Dashboard"
177                                    }
178                                    p {
179                                        class: "text-sm text-gray-500",
180                                        "View your overview and stats"
181                                    }
182                                }
183                                div {
184                                    class: "flex-shrink-0 ml-4",
185                                    svg {
186                                        class: "h-5 w-5 text-gray-400 group-hover:text-blue-500",
187                                        xmlns: "http://www.w3.org/2000/svg",
188                                        view_box: "0 0 20 20",
189                                        fill: "currentColor",
190                                        path {
191                                            fill_rule: "evenodd",
192                                            d: "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 111.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z",
193                                            clip_rule: "evenodd"
194                                        }
195                                    }
196                                }
197                            }
198                        }
199
200                        // Profile link
201                        Link {
202                            to: Route::Profile {},
203                            class: "group relative rounded-lg p-6 bg-white border border-gray-300 shadow-sm hover:shadow-md transition-shadow",
204                            div {
205                                class: "flex items-center",
206                                div {
207                                    class: "flex-shrink-0",
208                                    span {
209                                        class: "text-2xl",
210                                        "👤"
211                                    }
212                                }
213                                div {
214                                    class: "ml-4 min-w-0 flex-1",
215                                    p {
216                                        class: "text-base font-medium text-gray-900 group-hover:text-blue-600",
217                                        "Profile"
218                                    }
219                                    p {
220                                        class: "text-sm text-gray-500",
221                                        "Manage your account settings"
222                                    }
223                                }
224                                div {
225                                    class: "flex-shrink-0 ml-4",
226                                    svg {
227                                        class: "h-5 w-5 text-gray-400 group-hover:text-blue-500",
228                                        xmlns: "http://www.w3.org/2000/svg",
229                                        view_box: "0 0 20 20",
230                                        fill: "currentColor",
231                                        path {
232                                            fill_rule: "evenodd",
233                                            d: "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 111.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z",
234                                            clip_rule: "evenodd"
235                                        }
236                                    }
237                                }
238                            }
239                        }
240
241                        // Plugins link
242                        Link {
243                            to: Route::Plugins {},
244                            class: "group relative rounded-lg p-6 bg-white border border-gray-300 shadow-sm hover:shadow-md transition-shadow",
245                            div {
246                                class: "flex items-center",
247                                div {
248                                    class: "flex-shrink-0",
249                                    span {
250                                        class: "text-2xl",
251                                        "🧩"
252                                    }
253                                }
254                                div {
255                                    class: "ml-4 min-w-0 flex-1",
256                                    p {
257                                        class: "text-base font-medium text-gray-900 group-hover:text-blue-600",
258                                        "Plugins"
259                                    }
260                                    p {
261                                        class: "text-sm text-gray-500",
262                                        "Browse available plugins"
263                                    }
264                                }
265                                div {
266                                    class: "flex-shrink-0 ml-4",
267                                    svg {
268                                        class: "h-5 w-5 text-gray-400 group-hover:text-blue-500",
269                                        xmlns: "http://www.w3.org/2000/svg",
270                                        view_box: "0 0 20 20",
271                                        fill: "currentColor",
272                                        path {
273                                            fill_rule: "evenodd",
274                                            d: "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 111.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z",
275                                            clip_rule: "evenodd"
276                                        }
277                                    }
278                                }
279                            }
280                        }
281
282                        // Settings link
283                        Link {
284                            to: Route::Settings {},
285                            class: "group relative rounded-lg p-6 bg-white border border-gray-300 shadow-sm hover:shadow-md transition-shadow",
286                            div {
287                                class: "flex items-center",
288                                div {
289                                    class: "flex-shrink-0",
290                                    span {
291                                        class: "text-2xl",
292                                        "⚙️"
293                                    }
294                                }
295                                div {
296                                    class: "ml-4 min-w-0 flex-1",
297                                    p {
298                                        class: "text-base font-medium text-gray-900 group-hover:text-blue-600",
299                                        "Settings"
300                                    }
301                                    p {
302                                        class: "text-sm text-gray-500",
303                                        "Configure application preferences"
304                                    }
305                                }
306                                div {
307                                    class: "flex-shrink-0 ml-4",
308                                    svg {
309                                        class: "h-5 w-5 text-gray-400 group-hover:text-blue-500",
310                                        xmlns: "http://www.w3.org/2000/svg",
311                                        view_box: "0 0 20 20",
312                                        fill: "currentColor",
313                                        path {
314                                            fill_rule: "evenodd",
315                                            d: "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 111.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z",
316                                            clip_rule: "evenodd"
317                                        }
318                                    }
319                                }
320                            }
321                        }
322                    }
323                }
324            }
325        }
326    }
327}
328
329#[cfg(test)]
330mod tests {
331    use super::*;
332    use dioxus::prelude::*;
333
334    #[test]
335    fn test_not_found_component_creation() {
336        let _not_found = rsx! {
337            NotFound {
338                path: "test/path".to_string()
339            }
340        };
341    }
342
343    #[test]
344    fn test_not_found_without_path() {
345        let _not_found = rsx! { NotFound {} };
346    }
347}