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}