feat(web): add 404 page

This commit is contained in:
Kristofers Solo 2025-11-26 18:44:21 +02:00
parent a04abffe45
commit 3dc7187910
Signed by: kristoferssolo
GPG Key ID: 8687F2D3EEE6F0ED
6 changed files with 80 additions and 12 deletions

View File

@ -1,5 +1,7 @@
use crate::pages::{aes::AesPage, des::DesPage, footer::Footer, header::Header, home::Home}; use crate::pages::{
use leptos::prelude::*; aes::AesPage, des::DesPage, footer::Footer, header::Header, home::Home, not_found::NotFound,
};
use leptos::{prelude::*, svg::view};
use leptos_meta::{MetaTags, Stylesheet, Title, provide_meta_context}; use leptos_meta::{MetaTags, Stylesheet, Title, provide_meta_context};
use leptos_router::{ use leptos_router::{
StaticSegment, StaticSegment,
@ -43,7 +45,7 @@ pub fn App() -> impl IntoView {
<div class="app-containter"> <div class="app-containter">
<Header /> <Header />
<main> <main>
<Routes fallback=|| "Page not found.".into_view()> <Routes fallback=|| view! { <NotFound /> }>
<Route path=StaticSegment("/") view=Home /> <Route path=StaticSegment("/") view=Home />
<Route path=StaticSegment("/des") view=DesPage /> <Route path=StaticSegment("/des") view=DesPage />
<Route path=StaticSegment("/aes") view=AesPage /> <Route path=StaticSegment("/aes") view=AesPage />

View File

@ -5,9 +5,10 @@ async fn main() {
use leptos::logging::log; use leptos::logging::log;
use leptos::prelude::*; use leptos::prelude::*;
use leptos_axum::{LeptosRoutes, generate_route_list}; use leptos_axum::{LeptosRoutes, generate_route_list};
use tokio::net::TcpListener;
use web::app::*; use web::app::*;
let conf = get_configuration(None).unwrap(); let conf = get_configuration(None).expect("Valid config file");
let addr = conf.leptos_options.site_addr; let addr = conf.leptos_options.site_addr;
let leptos_options = conf.leptos_options; let leptos_options = conf.leptos_options;
// Generate the list of routes in your Leptos App // Generate the list of routes in your Leptos App
@ -24,10 +25,11 @@ async fn main() {
// run our app with hyper // run our app with hyper
// `axum::Server` is a re-export of `hyper::Server` // `axum::Server` is a re-export of `hyper::Server`
log!("listening on http://{}", &addr); log!("listening on http://{}", &addr);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); if let Ok(listener) = TcpListener::bind(&addr).await {
axum::serve(listener, app.into_make_service()) axum::serve(listener, app.into_make_service())
.await .await
.unwrap(); .expect("valid serving");
}
} }
#[cfg(not(feature = "ssr"))] #[cfg(not(feature = "ssr"))]

View File

@ -1,5 +1,4 @@
use leptos::prelude::*; use leptos::prelude::*;
use leptos_router::components::A;
#[component] #[component]
pub fn Footer() -> impl IntoView { pub fn Footer() -> impl IntoView {
@ -12,9 +11,9 @@ pub fn Footer() -> impl IntoView {
"No data is transmitted to any server. You can verify this by disconnecting your internet connection." "No data is transmitted to any server. You can verify this by disconnecting your internet connection."
</p> </p>
<div class="footer-links"> <div class="footer-links">
<A href="https://github.com/kristoferssolo/cipher-workshop" target="_blank"> <a href="https://github.com/kristoferssolo/cipher-workshop" target="_blank">
"View Source on GitHub" "View Source on GitHub"
</A> </a>
</div> </div>
</div> </div>
</footer> </footer>

View File

@ -3,3 +3,4 @@ pub mod des;
pub mod footer; pub mod footer;
pub mod header; pub mod header;
pub mod home; pub mod home;
pub mod not_found;

View File

@ -0,0 +1,17 @@
use leptos::prelude::*;
#[component]
pub fn NotFound() -> impl IntoView {
view! {
<div class="not-found-container">
<div class="error-code">"404"</div>
<h1>"Page Not Found"</h1>
<p>"The data you are looking for has been encrypted into the void."</p>
<div class="binary-decoration">
"01000100 01000101 01000001 01000100 00100000 01001100 01001001 01001110 01001011"
</div>
<a href="/" class="btn-primary btn-link">
"Return to Home"
</a>
</div>
}
}

View File

@ -150,7 +150,7 @@ main {
.app-footer { .app-footer {
margin-top: auto; margin-top: auto;
padding: 2rem 0; padding: 2rem 0;
border-top: 1px solid var(--border); border-top: 2px solid var(--border);
background: var(--bg-card); background: var(--bg-card);
text-align: center; text-align: center;
font-size: 0.9rem; font-size: 0.9rem;
@ -537,3 +537,50 @@ main {
color: var(--text-main); color: var(--text-main);
font-weight: 500; font-weight: 500;
} }
.not-found-container {
text-align: center;
padding: 4rem 1rem;
max-width: 600px;
margin: 0 auto;
animation: fadeIn 0.5s ease-out;
.error-code {
font-size: 6rem;
font-weight: 800;
color: var(--error);
font-family: "Consolar", "Monaco", monospace;
opacity: 0.8;
line-height: 1;
margin-bottom: 1rem;
text-shadow: 4px 4px 0px rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 2rem;
color: var(--text-main);
margin-bottom: 1rem;
}
p {
color: var(--text-muted);
font-size: 1.1rem;
margin-bottom: 2rem;
}
.binary-decoration {
font-family: "Consolas", "Monaco", monospace;
color: var(--text-muted);
font-size: 0.85rem;
opacity: 0.5;
margin-bottom: 2.5rem;
word-break: break-all;
}
}
.btn-link {
text-decoration: none;
display: inline-block;
width: auto;
text-align: center;
}