From 1532cb6111a240c312ecf21dbcf60d026b4017df Mon Sep 17 00:00:00 2001 From: mawkone Date: Fri, 12 Jun 2026 12:34:39 -0700 Subject: [PATCH] design(dashboard): rebuild sidebar to match Base44 navigation hierarchy and aesthetic --- .../components/project/dashboard-sidebar.tsx | 310 ++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 vibn-frontend/components/project/dashboard-sidebar.tsx diff --git a/vibn-frontend/components/project/dashboard-sidebar.tsx b/vibn-frontend/components/project/dashboard-sidebar.tsx new file mode 100644 index 0000000..a80efbb --- /dev/null +++ b/vibn-frontend/components/project/dashboard-sidebar.tsx @@ -0,0 +1,310 @@ +"use client"; + +import { useState } from "react"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { + Search, + LayoutGrid, + Users, + Database, + BarChart2, + TrendingUp, + Globe, + Plug, + ShieldCheck, + Code2, + Bot, + Zap, + Terminal, + FileJson, + Settings, + ChevronDown, + ChevronRight, +} from "lucide-react"; + +export function DashboardSidebar({ + workspace, + projectId, + children, +}: { + workspace: string; + projectId: string; + children: React.ReactNode; +}) { + const pathname = usePathname() ?? ""; + const projectBase = `/${workspace}/project/${projectId}`; + const isPreview = + pathname === `${projectBase}/preview` || + pathname.startsWith(`${projectBase}/preview/`); + + const [expandedSections, setExpandedSections] = useState< + Record + >({ + settings: true, + }); + + const [searchQuery, setSearchQuery] = useState(""); + + if (isPreview) { + return <>{children}; + } + + const toggleSection = (section: string) => { + setExpandedSections((prev) => ({ ...prev, [section]: !prev[section] })); + }; + + const menuItems = [ + { segment: "overview", label: "Overview", Icon: LayoutGrid }, + { segment: "users", label: "Users", Icon: Users }, + { + segment: "data", + label: "Data", + Icon: Database, + hasChildren: true, + }, + { segment: "analytics", label: "Analytics", Icon: BarChart2 }, + { + segment: "marketing", + label: "Marketing", + Icon: TrendingUp, + badge: "New", + hasChildren: true, + }, + { segment: "domains", label: "Domains", Icon: Globe }, + { segment: "integrations", label: "Integrations", Icon: Plug }, + { segment: "security", label: "Security", Icon: ShieldCheck }, + { segment: "code", label: "Code", Icon: Code2 }, + { segment: "agents", label: "Agents", Icon: Bot, badge: "New" }, + { segment: "automations", label: "Automations", Icon: Zap }, + { segment: "logs", label: "Logs", Icon: Terminal }, + { segment: "api", label: "API", Icon: FileJson }, + { + segment: "settings", + label: "Settings", + Icon: Settings, + hasChildren: true, + children: [ + { segment: "settings/app", label: "App Settings" }, + { segment: "settings/auth", label: "Authentication" }, + { segment: "settings/template", label: "App Template" }, + ], + }, + ]; + + const filteredItems = menuItems.filter( + (item) => + item.label.toLowerCase().includes(searchQuery.toLowerCase()) || + (item.children && + item.children.some((child) => + child.label.toLowerCase().includes(searchQuery.toLowerCase()), + )), + ); + + return ( +
+
+
+ Dashboard +
+ + {/* Search Bar */} +
+ + setSearchQuery(e.target.value)} + style={{ + border: "none", + background: "transparent", + outline: "none", + width: "100%", + fontSize: "0.8rem", + color: "#18181b", + }} + /> +
+ +
+ {filteredItems.map((item) => { + const isMainActive = + pathname === `${projectBase}/${item.segment}` || + pathname.startsWith(`${projectBase}/${item.segment}/`); + const isExpanded = expandedSections[item.segment]; + + return ( +
+
{ + if (item.hasChildren) { + toggleSection(item.segment); + } else { + // Navigate via link logic would happen here, we'll wrap the icon/label in a link + } + }} + > + {item.hasChildren ? ( +
+ + + {item.label} + +
+ ) : ( + + + + {item.label} + + + )} + +
+ {item.badge && ( + + {item.badge} + + )} + {item.hasChildren && + (isExpanded ? ( + + ) : ( + + ))} +
+
+ + {/* Render Children if expanded */} + {item.hasChildren && isExpanded && item.children && ( +
+ {item.children.map((child) => { + const isChildActive = + pathname === `${projectBase}/${child.segment}`; + return ( + + {child.label} + + ); + })} +
+ )} +
+ ); + })} +
+
+
+ {children} +
+
+ ); +}