use dioxus::prelude::*; use crate::api; use crate::types::MeResponse; #[derive(Routable, Clone, Debug, PartialEq)] #[rustfmt::skip] pub enum Route { #[layout(Layout)] #[route("/")] Home {}, #[route("/families")] FamilyList {}, #[route("/families/:slug")] FamilyDetail { slug: String }, #[route("/species")] SpeciesList {}, #[route("/species/:slug")] SpeciesDetail { slug: String }, #[route("/cultivars")] CultivarList {}, #[route("/cultivars/:slug")] CultivarDetail { slug: String }, #[route("/suppliers")] SupplierList {}, #[route("/suppliers/:slug")] SupplierDetail { slug: String }, #[route("/search")] SearchPage {}, #[end_layout] #[route("/:..segments")] NotFound { segments: Vec }, } #[component] pub fn App() -> Element { rsx! { Router:: {} } } #[component] fn Layout() -> Element { // Try to get current user (may be None for public access) let auth = use_resource(|| async { api::get_current_user().await.ok() }); let user: Option = auth.read().as_ref().and_then(|r| r.clone()); rsx! { div { class: "app-layout", nav { class: "sidebar", div { class: "sidebar-brand", span { class: "brand-icon", "\u{1F33F}" } div { class: "brand-text-group", span { class: "brand-text", "HerbAPI" } span { class: "brand-sub", "Plant Database" } } } div { class: "sidebar-nav", NavLink { to: Route::Home {}, label: "Home" } NavLink { to: Route::FamilyList {}, label: "Families" } NavLink { to: Route::SpeciesList {}, label: "Species" } NavLink { to: Route::CultivarList {}, label: "Cultivars" } NavLink { to: Route::SupplierList {}, label: "Suppliers" } NavLink { to: Route::SearchPage {}, label: "Search" } } div { class: "sidebar-user", if let Some(ref u) = user { span { class: "user-name", "{u.nickname.as_deref().or(u.name.as_deref()).unwrap_or(&u.email)}" } a { class: "logout-link", href: "/auth/oidc/logout", "Logout" } } else { a { class: "login-link", href: "/auth/oidc/login", "Login" } } } } main { class: "content", Outlet:: {} } } } } #[component] fn NavLink(to: Route, label: &'static str) -> Element { rsx! { Link { to: to, class: "nav-link", span { class: "nav-label", "{label}" } } } } #[component] fn NotFound(segments: Vec) -> Element { rsx! { div { class: "not-found", h1 { "404" } p { "Page not found: /{segments.join(\"/\")}" } Link { to: Route::Home {}, "Back to Home" } } } } // Re-export page components for the router pub use crate::pages::cultivars::{CultivarDetail, CultivarList}; pub use crate::pages::families::{FamilyDetail, FamilyList}; pub use crate::pages::home::Home; pub use crate::pages::search::SearchPage; pub use crate::pages::species::{SpeciesDetail, SpeciesList}; pub use crate::pages::suppliers::{SupplierDetail, SupplierList};