Spaces:
Running
Running
Download apps/admin/src/components/shell/AdminSidebar.tsx from Benaah/MicProtocol: direct link, hf CLI and curl.
- Browser
- Download file 8.78 kB
-
https://huggingface.co/spaces/Benaah/MicProtocol/resolve/main/apps/admin/src/components/shell/AdminSidebar.tsx
- Command line
-
hf download hf://spaces/Benaah/MicProtocol/apps/admin/src/components/shell/AdminSidebar.tsx
-
curl -L -o AdminSidebar.tsx https://huggingface.co/spaces/Benaah/MicProtocol/resolve/main/apps/admin/src/components/shell/AdminSidebar.tsx
8.78 kB
| "use client"; | |
| import React from "react"; | |
| import Link from "next/link"; | |
| import { usePathname } from "next/navigation"; | |
| import { AdminPermission } from "@micprotocol/shared-types"; | |
| import { trpc } from "@/lib/trpc"; | |
| import { | |
| LayoutDashboard, | |
| Server, | |
| Radio, | |
| Layers, | |
| BrainCircuit, | |
| Plug, | |
| Webhook, | |
| Activity, | |
| Gauge, | |
| TerminalSquare, | |
| Bug, | |
| Building2, | |
| ShieldCheck, | |
| ShieldAlert, | |
| Users, | |
| MessageSquare, | |
| FlaskConical, | |
| Microscope, | |
| ChevronDown, | |
| BarChart, | |
| FileText, | |
| Sliders, | |
| } from "lucide-react"; | |
| interface NavItem { | |
| href: string; | |
| label: string; | |
| icon: React.ComponentType<{ className?: string }>; | |
| badge?: string; | |
| /** Platform permission required to see this entry (absent = always visible). */ | |
| permission?: AdminPermission; | |
| children?: NavItem[]; | |
| } | |
| const primaryNav: NavItem[] = [ | |
| { href: "/", label: "Overview", icon: LayoutDashboard, permission: AdminPermission.ANALYTICS_READ }, | |
| { href: "/infrastructure", label: "Infrastructure", icon: Server, permission: AdminPermission.OPS_READ }, | |
| { href: "/realtime", label: "Realtime Engine", icon: Radio, permission: AdminPermission.OPS_READ }, | |
| { href: "/queues", label: "Queues", icon: Layers, permission: AdminPermission.OPS_READ }, | |
| { | |
| href: "/ai-engines", | |
| label: "AI Engines", | |
| icon: BrainCircuit, | |
| permission: AdminPermission.OPS_READ, | |
| children: [ | |
| { href: "/ai-engines", label: "Overview", icon: Microscope, permission: AdminPermission.OPS_READ }, | |
| { href: "/ai-engines/clip-scorer", label: "CLIP Scorer", icon: FlaskConical, permission: AdminPermission.OPS_READ }, | |
| ], | |
| }, | |
| { | |
| href: "/integrations", | |
| label: "Integrations", | |
| icon: Plug, | |
| permission: AdminPermission.SETTINGS_READ, | |
| children: [ | |
| { href: "/integrations", label: "Services", icon: Plug, permission: AdminPermission.SETTINGS_READ }, | |
| { href: "/integrations/credentials", label: "Credentials", icon: ShieldCheck, permission: AdminPermission.SETTINGS_READ }, | |
| ], | |
| }, | |
| { href: "/webhooks", label: "Webhooks", icon: Webhook, permission: AdminPermission.OPS_READ }, | |
| { | |
| href: "/observability", | |
| label: "Observability", | |
| icon: Activity, | |
| permission: AdminPermission.OPS_READ, | |
| children: [ | |
| { href: "/observability/traces", label: "Traces", icon: Activity, permission: AdminPermission.OPS_READ }, | |
| { href: "/observability/metrics", label: "Metrics", icon: Gauge, permission: AdminPermission.OPS_READ }, | |
| { href: "/observability/logs", label: "Live Logs", icon: TerminalSquare, permission: AdminPermission.OPS_READ }, | |
| { href: "/observability/errors", label: "Errors", icon: Bug, permission: AdminPermission.OPS_READ }, | |
| ], | |
| }, | |
| { href: "/tenants", label: "Tenants", icon: Building2, permission: AdminPermission.WORKSPACES_READ }, | |
| { href: "/verification", label: "Verification", icon: ShieldCheck, permission: AdminPermission.MODERATION_READ }, | |
| { | |
| href: "/crm", | |
| label: "CRM", | |
| icon: Users, | |
| permission: AdminPermission.USERS_READ, | |
| children: [ | |
| { href: "/crm/users", label: "Users", icon: Users, permission: AdminPermission.USERS_READ }, | |
| { href: "/crm/guests", label: "Guests", icon: Users, permission: AdminPermission.WORKSPACES_READ }, | |
| { href: "/crm/inquiries", label: "Inquiries", icon: MessageSquare, permission: AdminPermission.SUPPORT_READ }, | |
| ], | |
| }, | |
| { | |
| href: "/users", | |
| label: "Platform Users", | |
| icon: Users, | |
| permission: AdminPermission.USERS_READ, | |
| }, | |
| { | |
| href: "/workspaces", | |
| label: "Platform Workspaces", | |
| icon: Building2, | |
| permission: AdminPermission.WORKSPACES_READ, | |
| }, | |
| { href: "/settings", label: "Settings & Flags", icon: Sliders, permission: AdminPermission.SETTINGS_READ }, | |
| { href: "/moderation", label: "Moderation", icon: ShieldAlert, permission: AdminPermission.MODERATION_READ }, | |
| { href: "/supervision", label: "Supervision", icon: Gauge, permission: AdminPermission.SUPERVISION_READ }, | |
| { href: "/synthetic-agents", label: "Synthetic Agents", icon: FlaskConical, permission: AdminPermission.OPS_READ }, | |
| { href: "/audit-logs", label: "Audit Logs", icon: FileText, permission: AdminPermission.AUDIT_READ }, | |
| { href: "/security-policy", label: "Security Policy", icon: ShieldAlert, permission: AdminPermission.SECURITY_POLICY_READ }, | |
| { href: "/ratings", label: "Ratings", icon: BarChart, permission: AdminPermission.MODERATION_READ }, | |
| { href: "/surveys", label: "Surveys", icon: FileText, permission: AdminPermission.ANNOUNCEMENTS_READ }, | |
| { href: "/support", label: "Support", icon: MessageSquare, permission: AdminPermission.SUPPORT_READ }, | |
| ]; | |
| function filterNav(items: NavItem[], has: (p: AdminPermission) => boolean): NavItem[] { | |
| const out: NavItem[] = []; | |
| for (const item of items) { | |
| const ownOk = !item.permission || has(item.permission); | |
| if (item.children) { | |
| const children = filterNav(item.children, has); | |
| if (children.length > 0) { | |
| out.push({ ...item, children }); | |
| } else if (ownOk) { | |
| const { children: _dropped, ...rest } = item; | |
| out.push(rest); | |
| } | |
| } else if (ownOk) { | |
| out.push(item); | |
| } | |
| } | |
| return out; | |
| } | |
| function NavItemLink({ item, depth = 0 }: { item: NavItem; depth?: number }) { | |
| const pathname = usePathname(); | |
| const [open, setOpen] = React.useState( | |
| () => (item.children?.some((c) => pathname.startsWith(c.href)) ?? false), | |
| ); | |
| const active = pathname === item.href || (item.href !== "/" && pathname.startsWith(item.href)); | |
| if (item.children) { | |
| const hasActiveChild = item.children.some((c) => pathname.startsWith(c.href)); | |
| return ( | |
| <div className={hasActiveChild || open ? "" : ""}> | |
| <button | |
| onClick={() => setOpen((o) => !o)} | |
| className={`group flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors ${ | |
| hasActiveChild | |
| ? "bg-[var(--bg-tertiary)] text-[var(--color-primary)]" | |
| : "text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)] hover:text-[var(--text-primary)]" | |
| }`} | |
| > | |
| <item.icon className="size-4 shrink-0" /> | |
| <span className="flex-1 text-left">{item.label}</span> | |
| <ChevronDown className={`size-4 transition-transform ${open ? "rotate-180" : ""}`} /> | |
| </button> | |
| {open && ( | |
| <div className="mt-1 space-y-1 border-l border-[var(--border-color)] pl-3 ml-3"> | |
| {item.children.map((child) => ( | |
| <NavItemLink key={child.href} item={child} depth={depth + 1} /> | |
| ))} | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <Link | |
| href={item.href} | |
| className={`flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors ${ | |
| active | |
| ? "bg-[var(--bg-tertiary)] text-[var(--color-primary)]" | |
| : "text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)] hover:text-[var(--text-primary)]" | |
| }`} | |
| > | |
| <item.icon className="size-4 shrink-0" /> | |
| <span className="flex-1">{item.label}</span> | |
| </Link> | |
| ); | |
| } | |
| export function AdminSidebar() { | |
| const meQuery = trpc.admin.me.useQuery(undefined, { retry: false }); | |
| const permissions = meQuery.data?.permissions; | |
| const permsSet = React.useMemo( | |
| () => (permissions ? new Set<string>(permissions) : null), | |
| [permissions], | |
| ); | |
| const nav = React.useMemo( | |
| () => | |
| permsSet | |
| ? filterNav(primaryNav, (p) => permsSet.has(p)) | |
| : primaryNav, | |
| [permsSet], | |
| ); | |
| return ( | |
| <aside className="flex h-full w-60 shrink-0 flex-col border-r border-[var(--border-color)] bg-[var(--bg-secondary)]"> | |
| <div className="flex h-16 items-center gap-2.5 border-b border-[var(--border-color)] px-5"> | |
| <img src="/favicon.svg" alt="MicProtocol logo" className="size-7" /> | |
| <div> | |
| <div className="font-display text-sm font-semibold text-[var(--text-primary)]">MicProtocol</div> | |
| <div className="text-[11px] uppercase tracking-widest text-[var(--color-primary)]">Admin</div> | |
| </div> | |
| </div> | |
| <nav className="flex-1 space-y-1 overflow-y-auto px-3 py-4"> | |
| {nav.map((item) => ( | |
| <NavItemLink key={item.href} item={item} /> | |
| ))} | |
| {permsSet && nav.length === 0 && ( | |
| <p className="px-3 py-2 text-xs text-[var(--text-tertiary)]"> | |
| Your role has no console permissions assigned. Contact a SUPER_ADMIN. | |
| </p> | |
| )} | |
| </nav> | |
| <div className="border-t border-[var(--border-color)] p-4 text-xs text-[var(--text-tertiary)]"> | |
| MicProtocol Admin Panel | |
| <br /> | |
| v0.1.0 | |
| {meQuery.data?.role && ( | |
| <> | |
| <br /> | |
| Role: {meQuery.data.role} | |
| </> | |
| )} | |
| </div> | |
| </aside> | |
| ); | |
| } | |