"use client"; import { useState } from "react"; import Link from "next/link"; import { usePathname, useRouter } from "next/navigation"; import { useUnconfiguredNodes } from "@/lib/useNodes"; import { useUnacknowledgedAlerts } from "@/lib/useAlerts"; import { useAuth } from "@/components/AuthProvider"; import { useTheme } from "@/components/ThemeProvider"; import { FOUNDING_ORG_ID } from "@/lib/tenancy"; // Links visible to all authenticated roles (viewer+) const viewerLinks = [ { href: "/dashboard", label: "Dashboard" }, { href: "/calls", label: "Calls" }, { href: "/incidents", label: "Incidents" }, { href: "/map", label: "Map" }, { href: "/alerts", label: "Alerts" }, ]; // Trips is an internal utility feature, not a tenant-scoped product surface // (see [[trips-feature-intentional]] and SAAS_PLAN.md B7) — shown only to // the founding org, matching routers/trips.py's own gating. const tripsLink = { href: "/trips", label: "Trips" }; // Additional links for operators and admins const operatorLinks = [ { href: "/nodes", label: "Nodes" }, { href: "/systems", label: "Systems" }, { href: "/tokens", label: "Tokens" }, ]; // Platform-admin-only link. Settings is handled separately below — it's // customer-facing for org owners too, not admin-only (SAAS_PLAN.md B7). const adminLinks = [ { href: "/admin", label: "Admin" }, ]; function SunIcon() { return ( ); } function MoonIcon() { return ( ); } export function Nav() { const { user, isAdmin, isOperator, isOrgOwner, orgId, signOut, refreshClaims } = useAuth(); const pathname = usePathname(); const router = useRouter(); const { nodes: pending } = useUnconfiguredNodes(); const unackedAlerts = useUnacknowledgedAlerts(); const { theme, toggle } = useTheme(); const [mobileOpen, setMobileOpen] = useState(false); const [profileMenuOpen, setProfileMenuOpen] = useState(false); const [refreshing, setRefreshing] = useState(false); if (!user) return null; async function handleSignOut() { setProfileMenuOpen(false); await signOut(); router.push("/login"); } // Re-fetches the ID token so a claims change made server-side (e.g. an // admin granting a role, or org_id being provisioned) takes effect without // a full sign-out/sign-in. See AuthProvider.refreshClaims. async function handleRefreshClaims() { setRefreshing(true); try { await refreshClaims(); } finally { setRefreshing(false); setProfileMenuOpen(false); } } const allLinks = [ ...viewerLinks, ...(orgId === FOUNDING_ORG_ID || isAdmin ? [tripsLink] : []), ...(isAdmin || isOperator ? operatorLinks : []), ...(isAdmin ? adminLinks : []), ...(isAdmin || isOrgOwner ? [{ href: "/settings", label: "Settings" }] : []), ]; function navLinkClass(href: string) { return `text-sm font-mono transition-colors shrink-0 ${ pathname.startsWith(href) ? "text-white" : "text-gray-500 hover:text-gray-300" }`; } return ( ); }