import { A, useLocation, useNavigate, useSearchParams } from '@solidjs/router'; import { For, createEffect, createMemo, createSignal, onCleanup, onMount, type JSX } from 'solid-js'; import AdminSidebar from './AdminSidebar'; import { isExternalIdentity } from '~/lib/admin-auth'; import { clearAdminSession, hasAdminSession, setAdminSession } from '~/lib/admin-session'; import { Bell } from 'lucide-solid'; type Tab = { href: string; label: string; exact?: boolean }; const TAB_SETS: Array<{ prefixes: string[]; tabs: Tab[] }> = [ { prefixes: ['/admin/roles'], tabs: [ { href: '/admin/roles', label: 'Roles', exact: true }, { href: '/admin/roles/create', label: 'Create Role' }, { href: '/admin/roles/templates', label: 'View Roles' }, ], }, { prefixes: ['/admin/runtime-roles'], tabs: [ { href: '/admin/runtime-roles', label: 'Roles', exact: true }, { href: '/admin/runtime-roles/new', label: 'Create Role' }, { href: '/admin/role-ui-configs', label: 'View Roles' }, ], }, ]; export default function AdminShell(props: { children: JSX.Element }) { const location = useLocation(); const navigate = useNavigate(); const [searchParams] = useSearchParams(); const [checkedSession, setCheckedSession] = createSignal(false); const [adminName, setAdminName] = createSignal('Admin'); const [sidebarOpen, setSidebarOpen] = createSignal(false); const [sidebarCollapsed, setSidebarCollapsed] = createSignal(false); const [tabsTrackEl, setTabsTrackEl] = createSignal(); const [tabRefs, setTabRefs] = createSignal>({}); const [tabIndicator, setTabIndicator] = createSignal({ left: 0, width: 0, ready: false }); const tabs = createMemo(() => { const path = location.pathname; for (const set of TAB_SETS) { if (set.prefixes.some((p) => path === p || path.startsWith(`${p}/`))) return set.tabs; } return []; }); const isTabActive = (tab: Tab) => tab.exact ? location.pathname === tab.href : location.pathname === tab.href || location.pathname.startsWith(`${tab.href}/`); const refreshTabIndicator = () => { const activeTab = tabs().find((tab) => isTabActive(tab)); const track = tabsTrackEl(); if (!activeTab || !track) { setTabIndicator((prev) => ({ ...prev, ready: false })); return; } const el = tabRefs()[activeTab.href]; if (!el) return; setTabIndicator({ left: el.offsetLeft, width: el.offsetWidth, ready: true }); }; createEffect(() => { tabs(); location.pathname; requestAnimationFrame(refreshTabIndicator); }); onMount(() => { const onResize = () => refreshTabIndicator(); window.addEventListener('resize', onResize); onCleanup(() => window.removeEventListener('resize', onResize)); const isLocalDev = typeof window !== 'undefined' && (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'); const isPreview = searchParams._preview === '1' || (typeof sessionStorage !== 'undefined' && sessionStorage.getItem('nxtgauge_admin_preview') === '1'); if (isPreview || isLocalDev) { if (typeof sessionStorage !== 'undefined') sessionStorage.setItem('nxtgauge_admin_preview', '1'); setAdminSession(); setCheckedSession(true); return; } const verify = async () => { if (!hasAdminSession()) { const from = encodeURIComponent(location.pathname + location.search); navigate(`/login?from=${from}`, { replace: true }); return; } try { const accessToken = typeof sessionStorage !== 'undefined' ? sessionStorage.getItem('nxtgauge_admin_access_token') || '' : ''; const response = await fetch('/api/gateway/users/auth/me', { method: 'GET', headers: { Accept: 'application/json', 'x-portal-target': 'admin', ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}), }, credentials: 'include', }); const payload = await response.json().catch(() => ({})); if (!response.ok || isExternalIdentity(payload)) throw new Error('Unauthorized'); if (payload?.full_name) setAdminName(payload.full_name); setCheckedSession(true); } catch { clearAdminSession(); const from = encodeURIComponent(location.pathname + location.search); navigate(`/login?from=${from}`, { replace: true }); } }; void verify(); }); const onLogout = async () => { await fetch('/api/gateway/users/auth/logout', { method: 'POST', headers: { Accept: 'application/json', 'x-portal-target': 'admin' }, credentials: 'include', }).catch(() => {}); clearAdminSession(); if (typeof sessionStorage !== 'undefined') { sessionStorage.removeItem('nxtgauge_admin_access_token'); sessionStorage.removeItem('nxtgauge_admin_preview'); } navigate('/login', { replace: true }); }; const adminInitials = createMemo(() => { const parts = adminName().split(' ').map((s) => s.trim()).filter(Boolean); if (parts.length === 0) return 'U'; if (parts.length === 1) return parts[0].slice(0, 1).toUpperCase(); return `${parts[0][0] || ''}`.toUpperCase(); }); const sidebarWidth = () => (sidebarCollapsed() ? 'w-20' : 'w-64'); return (
{/* ── Header ── */}
{/* Left: logo + role title */}
NXTGAUGE

Super Admin

{/* Mobile menu button */} {/* Right: bell + avatar + logout */}
{/* ── Body ── */} {checkedSession() ? (
{/* Mobile overlay */}
setSidebarOpen(false)} /> {/* Sidebar */}
setSidebarCollapsed((v) => !v)} onNavigate={() => setSidebarOpen(false)} onLogout={onLogout} />
{/* Main */}
{props.children}
) : (
)}
); } function ShowTabs(props: { tabs: Tab[]; isTabActive: (tab: Tab) => boolean; setTabsTrackEl: (el: HTMLDivElement) => void; setTabRefs: (fn: (prev: Record) => Record) => void; tabIndicator: () => { left: number; width: number; ready: boolean }; }) { if (props.tabs.length === 0) return null; return (