File size: 4,243 Bytes
dd92235
46cb663
dd92235
033de88
dd92235
7acdf04
9308228
 
 
046481b
 
dd92235
033de88
dd92235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8f548b0
dd92235
046481b
9308228
046481b
 
46cb663
7acdf04
046481b
46cb663
dd92235
046481b
 
9308228
 
 
 
 
 
 
 
046481b
dd92235
9308228
046481b
dd92235
 
 
 
 
 
 
 
 
 
9308228
 
 
 
 
033de88
 
 
 
 
 
 
 
 
 
 
 
 
9308228
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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 { /* silent */ }
    }, [workspace]);

    useEffect(() => { loadCounts(); }, [loadCounts]);
    // Refresh counts every 30s
    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>
    );
}