use dioxus::prelude::*; use dioxus_free_icons::icons::bs_icons::{ BsBoxArrowRight, BsFileEarmarkText, BsGear, BsGithub, BsGrid, BsHouseDoor, BsRobot, }; use dioxus_free_icons::icons::fa_solid_icons::FaCubes; use dioxus_free_icons::Icon; use crate::Route; /// Navigation entry for the sidebar. struct NavItem { label: &'static str, route: Route, /// Bootstrap icon element rendered beside the label. icon: Element, } /// Fixed left sidebar containing header, navigation, logout, and footer. /// /// # Arguments /// /// * `email` - Email address displayed beneath the avatar placeholder. /// * `avatar_url` - URL for the avatar image (unused placeholder for now). #[component] pub fn Sidebar(email: String, avatar_url: String) -> Element { let nav_items: Vec = vec![ NavItem { label: "Overview", route: Route::OverviewPage {}, icon: rsx! { Icon { icon: BsHouseDoor, width: 18, height: 18 } }, }, NavItem { label: "Documentation", route: Route::OverviewPage {}, icon: rsx! { Icon { icon: BsFileEarmarkText, width: 18, height: 18 } }, }, NavItem { label: "Agents", route: Route::OverviewPage {}, icon: rsx! { Icon { icon: BsRobot, width: 18, height: 18 } }, }, NavItem { label: "Models", route: Route::OverviewPage {}, icon: rsx! { Icon { icon: FaCubes, width: 18, height: 18 } }, }, NavItem { label: "Settings", route: Route::OverviewPage {}, icon: rsx! { Icon { icon: BsGear, width: 18, height: 18 } }, }, ]; // Determine current path to highlight the active nav link. let current_route = use_route::(); rsx! { aside { class: "sidebar", // -- Header: avatar circle + email -- SidebarHeader { email: email.clone(), avatar_url } // -- Navigation links -- nav { class: "sidebar-nav", for item in nav_items { { // Simple active check: highlight Overview only when on `/`. let is_active = item.route == current_route; let cls = if is_active { "sidebar-link active" } else { "sidebar-link" }; rsx! { Link { to: item.route, class: cls, {item.icon} span { "{item.label}" } } } } } } // -- Logout button -- div { class: "sidebar-logout", Link { to: NavigationTarget::::External("/auth/logout".into()), class: "sidebar-link logout-btn", Icon { icon: BsBoxArrowRight, width: 18, height: 18 } span { "Logout" } } } // -- Footer: version + social links -- SidebarFooter {} } } } /// Avatar circle and email display at the top of the sidebar. /// /// # Arguments /// /// * `email` - User email to display. /// * `avatar_url` - Placeholder for future avatar image URL. #[component] fn SidebarHeader(email: String, avatar_url: String) -> Element { // Extract initials from email (first two chars before @). let initials: String = email .split('@') .next() .unwrap_or("U") .chars() .take(2) .collect::() .to_uppercase(); rsx! { div { class: "sidebar-header", div { class: "avatar-circle", span { class: "avatar-initials", "{initials}" } } p { class: "sidebar-email", "{email}" } } } } /// Footer section with version string and placeholder social links. #[component] fn SidebarFooter() -> Element { let version = env!("CARGO_PKG_VERSION"); rsx! { footer { class: "sidebar-footer", div { class: "sidebar-social", a { href: "#", class: "social-link", title: "GitHub", Icon { icon: BsGithub, width: 16, height: 16 } } a { href: "#", class: "social-link", title: "Impressum", Icon { icon: BsGrid, width: 16, height: 16 } } } p { class: "sidebar-version", "v{version}" } } } }