154 lines
4.7 KiB
Rust
154 lines
4.7 KiB
Rust
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<NavItem> = 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::<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::<Route>::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::<String>()
|
|
.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}" }
|
|
}
|
|
}
|
|
}
|