"use client"; import { useEffect, useState } from "react"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { BarChart3, Briefcase, Compass, Microscope, Shield, Settings, ChevronDown, type LucideIcon, } from "lucide-react"; import { api } from "@/lib/trpc"; import { useFeatureAccess, type ModuleKey } from "@/lib/useFeatureAccess"; import { useWorkspaceProfile } from "@/lib/useWorkspaceProfile"; import { useActiveSymbolGuarded } from "@/stores/active-symbol-store"; import type { NavItemId } from "@/lib/workspace-profile"; import { scrollAppMainToTopSoon } from "@/lib/scroll-app-main"; /** * Left sidebar — management-centered sections, filtered by module access + workspace density. */ interface NavItem { label: string; href: string; navId: NavItemId; } interface NavSection { title: string; icon: LucideIcon; module: ModuleKey; items: NavItem[]; } const SECTIONS: NavSection[] = [ { title: "Research", icon: BarChart3, module: "research", items: [ { label: "Symbol", href: "/", navId: "research" }, { label: "Market Outlook", href: "/market-outlook", navId: "market" }, { label: "Charts", href: "/chart-lab", navId: "charts" }, { label: "Screener", href: "/screener", navId: "screener" }, { label: "Institutional", href: "/institutional", navId: "institutional" }, { label: "Dealer Flow", href: "/dealer-flow", navId: "dealer-flow" }, ], }, { title: "Book", icon: Briefcase, module: "execution", items: [ { label: "Portfolio", href: "/portfolio", navId: "portfolio" }, { label: "Risk", href: "/risk", navId: "risk" }, { label: "Tracked Funds", href: "/funds", navId: "funds" }, ], }, { title: "Process", icon: Compass, module: "execution", items: [ { label: "Get Started", href: "/guided-start", navId: "guided-start" }, { label: "Decision plan", href: "/plan", navId: "plan" }, { label: "Theses", href: "/theses", navId: "theses" }, { label: "Journal", href: "/journal", navId: "journal" }, { label: "Strategies", href: "/strategies", navId: "strategies" }, { label: "Strategy lab", href: "/lab", navId: "lab" }, { label: "Position considerations", href: "/exits", navId: "exits" }, { label: "Goals", href: "/daily-focus", navId: "goals" }, ], }, { title: "Tools", icon: Microscope, module: "analytics", items: [ { label: "Alerts", href: "/alerts", navId: "alerts" }, { label: "Reports", href: "/reports", navId: "reports" }, { label: "Filings", href: "/filings", navId: "filings" }, { label: "Options DD", href: "/options-dd", navId: "options" }, ], }, { title: "Admin", icon: Shield, module: "admin", items: [ { label: "Overview", href: "/admin", navId: "admin" }, { label: "Users", href: "/admin/users", navId: "admin" }, { label: "Queue", href: "/admin/queue", navId: "admin" }, { label: "Audit Logs", href: "/admin/audit-logs", navId: "admin" }, { label: "X Accounts", href: "/admin/x-accounts", navId: "admin" }, { label: "SMTP", href: "/admin/smtp", navId: "admin" }, ], }, { title: "Settings", icon: Settings, module: "settings", items: [{ label: "Profile / Auth", href: "/settings", navId: "settings" }], }, ]; const STORAGE_KEY = "iflow-nav-collapsed"; function getInitialCollapsed(pathname: string, visibleSections: NavSection[]): Record { if (typeof window !== "undefined") { try { const stored = localStorage.getItem(STORAGE_KEY); if (stored) return JSON.parse(stored); } catch { /* ignore */ } } const initial: Record = {}; const norm = (pathname || "/").replace(/\/+$/, "") || "/"; for (const section of visibleSections) { const isActive = section.items.some((item) => item.href === norm); initial[section.title] = !isActive; } return initial; } export function SidebarNav({ collapsed: panelCollapsed, onToggle }: { collapsed?: boolean; onToggle?: () => void }) { const pathname = usePathname(); const normalizedPath = (pathname || "/").replace(/\/+$/, "") || "/"; const { modules, loading } = useFeatureAccess(); const { isNavVisible, density } = useWorkspaceProfile(); const activeSymbol = useActiveSymbolGuarded(); const [collapsed, setCollapsed] = useState>({}); const [initialized, setInitialized] = useState(false); const [unackedCount, setUnackedCount] = useState(0); // Research → "Symbol" shows the focused ticker, e.g. "Symbol: IREN" const itemLabel = (item: { label: string }) => item.label === "Symbol" && activeSymbol ? `Symbol: ${activeSymbol}` : item.label; const visibleSections = SECTIONS .filter((s) => modules.includes(s.module)) .map((s) => ({ ...s, items: s.items.filter((item) => isNavVisible(item.navId)), })) .filter((s) => s.items.length > 0); useEffect(() => { const fetchCount = () => api.alerts.unackedCount().then((r) => setUnackedCount(r.count)).catch(() => {}); fetchCount(); const handler = () => fetchCount(); window.addEventListener("alert-dismissed", handler); return () => window.removeEventListener("alert-dismissed", handler); }, []); useEffect(() => { setCollapsed(getInitialCollapsed(pathname, visibleSections)); setInitialized(true); // eslint-disable-next-line react-hooks/exhaustive-deps -- re-init when module/density loading settles }, [loading, density]); useEffect(() => { if (initialized) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(collapsed)); } catch { /* ignore */ } } }, [collapsed, initialized]); const toggleCollapse = (sectionTitle: string) => { setCollapsed((prev) => ({ ...prev, [sectionTitle]: !prev[sectionTitle] })); }; return ( ); }