skill_web/components/
sidebar.rs1use yew::prelude::*;
4use yew_router::prelude::*;
5
6use crate::router::Route;
7use super::icons::{AnalyticsIcon, DashboardIcon, SkillsIcon, PlayIcon, HistoryIcon, SettingsIcon, SearchIcon};
8
9struct NavItem {
11 route: Route,
12 label: &'static str,
13 icon: fn(&'static str) -> Html,
14}
15
16#[function_component(Sidebar)]
18pub fn sidebar() -> Html {
19 let route = use_route::<Route>();
20
21 let nav_items = vec![
22 NavItem {
23 route: Route::Dashboard,
24 label: "Dashboard",
25 icon: |class| html! { <DashboardIcon class={class} /> },
26 },
27 NavItem {
28 route: Route::Skills,
29 label: "Skills",
30 icon: |class| html! { <SkillsIcon class={class} /> },
31 },
32 NavItem {
33 route: Route::Run,
34 label: "Run",
35 icon: |class| html! { <PlayIcon class={class} /> },
36 },
37 NavItem {
38 route: Route::History,
39 label: "History",
40 icon: |class| html! { <HistoryIcon class={class} /> },
41 },
42 NavItem {
43 route: Route::SearchTest,
44 label: "Search Test",
45 icon: |class| html! { <SearchIcon class={class} /> },
46 },
47 NavItem {
48 route: Route::Analytics,
49 label: "Analytics",
50 icon: |class| html! { <AnalyticsIcon class={class} /> },
51 },
52 NavItem {
53 route: Route::Settings,
54 label: "Settings",
55 icon: |class| html! { <SettingsIcon class={class} /> },
56 },
57 ];
58
59 html! {
60 <aside class="fixed left-0 top-16 bottom-0 w-64 bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 overflow-y-auto z-30">
61 <nav class="p-4 space-y-1">
62 { for nav_items.iter().map(|item| {
63 let is_active = route.as_ref().map(|r| is_route_match(r, &item.route)).unwrap_or(false);
64 let class = if is_active { "nav-link-active" } else { "nav-link" };
65
66 html! {
67 <Link<Route> to={item.route.clone()} classes={class}>
68 { (item.icon)("w-5 h-5") }
69 <span>{ item.label }</span>
70 </Link<Route>>
71 }
72 }) }
73 </nav>
74
75 <div class="absolute bottom-0 left-0 right-0 p-4 border-t border-gray-200 dark:border-gray-700">
77 <Link<Route>
78 to={Route::Run}
79 classes="btn btn-primary w-full"
80 >
81 <PlayIcon class="w-4 h-4 mr-2" />
82 { "Run Skill" }
83 </Link<Route>>
84 </div>
85 </aside>
86 }
87}
88
89fn is_route_match(current: &Route, target: &Route) -> bool {
91 match (current, target) {
92 (Route::Dashboard, Route::Dashboard) => true,
93 (Route::Skills, Route::Skills) => true,
94 (Route::SkillDetail { .. }, Route::Skills) => true,
95 (Route::SkillInstance { .. }, Route::Skills) => true,
96 (Route::Run, Route::Run) => true,
97 (Route::RunSkill { .. }, Route::Run) => true,
98 (Route::RunSkillTool { .. }, Route::Run) => true,
99 (Route::History, Route::History) => true,
100 (Route::HistoryDetail { .. }, Route::History) => true,
101 (Route::SearchTest, Route::SearchTest) => true,
102 (Route::Analytics, Route::Analytics) => true,
103 (Route::Settings, Route::Settings) => true,
104 _ => current == target,
105 }
106}