From b6dcecb672df62b27d9e03b08b281916d47a48ad Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Fri, 22 May 2026 08:13:02 -0400 Subject: [PATCH] =?UTF-8?q?feat(ui):=20Dragonflight=20redesign=20=E2=80=94?= =?UTF-8?q?=20foundation=20JSX=20files:=20shell.jsx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/web-ui/public/shell.jsx | 167 +++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 services/web-ui/public/shell.jsx diff --git a/services/web-ui/public/shell.jsx b/services/web-ui/public/shell.jsx new file mode 100644 index 0000000..58345d6 --- /dev/null +++ b/services/web-ui/public/shell.jsx @@ -0,0 +1,167 @@ +// shell.jsx - app shell: sidebar nav, topbar, route container + +const NAV_TREE = [ + { id: "home", label: "Home", icon: "home" }, + { id: "library", label: "Library", icon: "library" }, + { id: "projects", label: "Projects", icon: "folder" }, + { + id: "ingest", label: "Ingest", icon: "upload", group: true, + children: [ + { id: "upload", label: "Upload", icon: "upload" }, + { id: "recorders", label: "Recorders", icon: "record", badge: { kind: "live", text: "4" } }, + { id: "capture", label: "Capture", icon: "capture" }, + { id: "monitors", label: "Monitors", icon: "monitor" }, + ], + }, + { id: "jobs", label: "Jobs", icon: "jobs", badge: { kind: "neutral", text: "3" } }, + { id: "editor", label: "Editor", icon: "editor", badge: { kind: "dev", text: "DEV" } }, +]; + +const ADMIN_TREE = [ + { id: "users", label: "Users", icon: "users" }, + { id: "tokens", label: "Tokens", icon: "token" }, + { id: "containers", label: "Containers", icon: "container" }, + { id: "cluster", label: "Cluster", icon: "cluster" }, + { id: "settings", label: "Settings", icon: "settings" }, +]; + +function NavItem({ item, active, onSelect, depth = 0, openGroups, toggleGroup }) { + const isGroup = item.group && item.children; + const isOpen = isGroup && openGroups.has(item.id); + const isActive = active === item.id || (isGroup && item.children.some(c => c.id === active)); + + return ( + <> +
{ + if (isGroup) toggleGroup(item.id); + else onSelect(item.id); + }} + > + + {item.label} + {item.badge && ( + {item.badge.text} + )} + {isGroup && } +
+ {isGroup && isOpen && ( +
+ {item.children.map(c => ( +
onSelect(c.id)} + > + + {c.label} + {c.badge && {c.badge.text}} +
+ ))} +
+ )} + + ); +} + +function Sidebar({ active, onNavigate }) { + const [openGroups, setOpenGroups] = React.useState(new Set(["ingest"])); + const toggleGroup = (id) => { + setOpenGroups(prev => { + const next = new Set(prev); + if (next.has(id)) next.delete(id); + else next.add(id); + return next; + }); + }; + + React.useEffect(() => { + const ingestChildren = ["upload", "recorders", "capture", "monitors"]; + if (ingestChildren.includes(active)) { + setOpenGroups(prev => prev.has("ingest") ? prev : new Set([...prev, "ingest"])); + } + }, [active]); + + return ( + + ); +} + +function Topbar({ crumbs, onNavigate, right }) { + return ( +
+
+ {crumbs.map((c, i) => ( + + {i > 0 && } + c.to && onNavigate && onNavigate(c.to)} + > + {c.label} + + + ))} +
+
+
+ + + ⌘K +
+
+ + cluster healthy +
+ + {right} +
+ ); +} + +window.Sidebar = Sidebar; +window.Topbar = Topbar; +window.NAV_TREE = NAV_TREE;