nxtgauge-admin-solid/src/components/AdminShell.tsx

134 lines
4 KiB
TypeScript
Raw Normal View History

import { A, useLocation, useNavigate } from '@solidjs/router';
import { createMemo, createSignal, onMount, type JSX } from 'solid-js';
import AdminSidebar from './AdminSidebar';
import { clearAdminSession, hasAdminSession } from '~/lib/admin-session';
import { sidebarCollapsed } from '~/lib/sidebar-state';
type Tab = { href: string; label: string; exact?: boolean };
const TAB_SETS: Array<{ prefixes: string[]; tabs: Tab[] }> = [
{
prefixes: ['/admin/roles'],
tabs: [
{ href: '/admin/roles', label: 'Internal Roles', exact: true },
{ href: '/admin/roles/create', label: 'Create Role' },
],
},
{
prefixes: ['/admin/runtime-roles'],
tabs: [
{ href: '/admin/runtime-roles', label: 'External Roles', exact: true },
{ href: '/admin/runtime-roles/new', label: 'Create External Role' },
],
},
{
prefixes: ['/admin/onboarding-schemas'],
tabs: [
{ href: '/admin/onboarding-schemas', label: 'Onboarding Flows', exact: true },
{ href: '/admin/onboarding-schemas/new', label: 'Create Flow' },
],
},
{
prefixes: ['/admin/internal-dashboard-management'],
tabs: [
{ href: '/admin/internal-dashboard-management', label: 'Internal Dashboards' },
],
},
{
prefixes: ['/admin/external-dashboard-management'],
tabs: [
{ href: '/admin/external-dashboard-management', label: 'External Dashboards' },
],
},
{
prefixes: ['/admin/role-ui-configs'],
tabs: [
{ href: '/admin/role-ui-configs', label: 'Config Inspector', exact: true },
{ href: '/admin/role-ui-configs/new', label: 'Create Config' },
],
},
];
export default function AdminShell(props: { children: JSX.Element }) {
const location = useLocation();
const navigate = useNavigate();
const [checkedSession, setCheckedSession] = createSignal(false);
const tabs = createMemo<Tab[]>(() => {
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) => {
if (tab.exact) return location.pathname === tab.href;
return location.pathname === tab.href || location.pathname.startsWith(`${tab.href}/`);
};
onMount(() => {
if (!hasAdminSession()) {
const from = encodeURIComponent(location.pathname + location.search);
navigate(`/login?from=${from}`, { replace: true });
return;
}
setCheckedSession(true);
});
const onLogout = () => {
clearAdminSession();
navigate('/login', { replace: true });
};
return (
<div class="admin-root">
<header class="admin-header">
<div class="admin-header-inner">
<div class="admin-brand">
<img src="/nxtgauge-logo.png" alt="NXTGAUGE" />
<div>
<p class="admin-brand-kicker">Administration</p>
<h1>NXTGAUGE Admin Panel</h1>
</div>
</div>
<div class="admin-header-actions">
<p class="admin-role-chip">Super Admin</p>
<button class="btn" type="button" onClick={onLogout}>Logout</button>
</div>
</div>
</header>
{checkedSession() ? (
<div class={`shell${sidebarCollapsed() ? ' sidebar-collapsed' : ''}`}>
<AdminSidebar />
<main class="main">
{tabs().length > 0 ? (
<div class="admin-tab-wrap">
<nav class="admin-tabs">
{tabs().map((tab) => (
<A href={tab.href} class={`admin-tab ${isTabActive(tab) ? 'active' : ''}`}>
{tab.label}
</A>
))}
</nav>
</div>
) : null}
<div class="main-inner">{props.children}</div>
</main>
</div>
) : (
<div class="shell">
<main class="main">
<div class="card">
<p class="notice">Checking session...</p>
</div>
</main>
</div>
)}
</div>
);
}