| import { useState, useEffect, useCallback } from "react"; |
| import { NavLink, Link, useNavigate } from "react-router-dom"; |
| import { useWorkspaceContext } from "../../context/WorkspaceContext"; |
| import { useAuth } from "../../context/AuthContext"; |
| import { getDashboardStats } from "../../api/dashboard"; |
| import { DocWeaveLogo } from "../DocWeaveLogo"; |
| import "./Sidebar.css"; |
|
|
| export function Sidebar() { |
| const [collapsed, setCollapsed] = useState(false); |
| const navigate = useNavigate(); |
| const { workspace } = useWorkspaceContext(); |
| const { user, logout } = useAuth(); |
| const [counts, setCounts] = useState({}); |
|
|
| const loadCounts = useCallback(async () => { |
| if (!workspace) return; |
| try { |
| const s = await getDashboardStats(workspace.id); |
| if (s && !s.detail) setCounts(s); |
| } catch { } |
| }, [workspace]); |
|
|
| useEffect(() => { loadCounts(); }, [loadCounts]); |
| |
| useEffect(() => { |
| const id = setInterval(loadCounts, 30000); |
| return () => clearInterval(id); |
| }, [loadCounts]); |
|
|
| const NAV_ITEMS = [ |
| { to: "/", label: "Dashboard", icon: "π ", end: true }, |
| { to: "/documents", label: "Documents", icon: "π", count: counts.total_documents }, |
| { to: "/knowledge", label: "Knowledge", icon: "π§ ", count: counts.knowledge_items }, |
| { to: "/search", label: "Search", icon: "π" }, |
| { to: "/workflows", label: "Workflows", icon: "β‘", count: counts.workflows_waiting_for_review, countTone: "warning" }, |
| { to: "/activity", label: "Activity", icon: "π" }, |
| { to: "/settings", label: "Settings", icon: "β" }, |
| ]; |
|
|
| return ( |
| <aside className={`dw-sidebar ${collapsed ? "dw-sidebar--collapsed" : ""}`}> |
| <div className="dw-sidebar__top"> |
| <Link to="/" className="dw-sidebar__brand"> |
| <DocWeaveLogo size={collapsed ? 36 : 42} className="dw-sidebar__logo" /> |
| {!collapsed && <span className="dw-sidebar__wordmark">DocWeave</span>} |
| </Link> |
| <button className="dw-sidebar__collapse-btn" onClick={() => setCollapsed((v) => !v)} title={collapsed ? "Expand" : "Collapse"}> |
| {collapsed ? "βΈ" : "β"} |
| </button> |
| </div> |
| |
| <nav className="dw-sidebar__nav"> |
| {NAV_ITEMS.map((item) => ( |
| <NavLink |
| key={item.to} |
| to={item.to} |
| end={item.end} |
| title={collapsed ? item.label : undefined} |
| className={({ isActive }) => `dw-sidebar__link ${isActive ? "dw-sidebar__link--active" : ""}`} |
| > |
| <span className="dw-sidebar__link-icon">{item.icon}</span> |
| {!collapsed && ( |
| <> |
| <span className="dw-sidebar__link-label">{item.label}</span> |
| {item.count != null && item.count > 0 && ( |
| <span className={`dw-sidebar__badge ${item.countTone === "warning" ? "dw-sidebar__badge--warning" : ""}`}> |
| {item.count} |
| </span> |
| )} |
| </> |
| )} |
| </NavLink> |
| ))} |
| </nav> |
|
|
| <div className="dw-sidebar__footer"> |
| <div className="dw-sidebar__user"> |
| <span className="dw-sidebar__user-avatar">{(user?.username || "U")[0].toUpperCase()}</span> |
| {!collapsed && ( |
| <span className="dw-sidebar__user-name">{user?.username || "User"}</span> |
| )} |
| </div> |
| <button |
| className="dw-sidebar__logout-btn" |
| title="Log out" |
| onClick={() => { logout(); navigate("/login"); }} |
| > |
| {collapsed ? "βͺ" : "Log out"} |
| </button> |
| </div> |
| </aside> |
| ); |
| } |
|
|