learning_ai_common_plat/dashboards/tracker-web/src/app/dashboard/layout.tsx
Saravana Kumar ccfbfd194a
Some checks failed
Publish @bytelyst/* packages / publish (push) Failing after 11s
CI — Common Platform / Build, Test & Typecheck (push) Successful in 38s
feat(tracker-web): add admin settings page
2026-05-30 19:53:16 +00:00

124 lines
3.7 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import { useRouter, usePathname } from 'next/navigation';
import Link from 'next/link';
import {
AppShell,
AppShellSkipLink,
AppShellMobileToggle,
AppShellOverlay,
AppShellSidebar,
AppShellNav,
AppShellNavItem,
AppShellMain,
Button,
} from '@/components/ui/Primitives';
import { useAuth } from '@/lib/auth-context';
import { useTheme } from '@/lib/theme-context';
import { ProductSwitcher } from '@/components/product-switcher';
import { SystemBanners } from '@/components/system-banners';
const NAV_ITEMS = [
{ href: '/dashboard', label: 'Overview' },
{ href: '/dashboard/items', label: 'Items' },
{ href: '/dashboard/board', label: 'Board' },
{ href: '/dashboard/fleet', label: 'Fleet' },
{ href: '/dashboard/settings', label: 'Settings' },
];
/** Open the ⌘K command palette by replaying the global hotkey. */
function openCommandPalette() {
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'k', metaKey: true, ctrlKey: true }));
}
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
const { user, loading, logout } = useAuth();
const { theme, setTheme } = useTheme();
const router = useRouter();
const pathname = usePathname();
const [navOpen, setNavOpen] = useState(false);
useEffect(() => {
if (!loading && !user) {
router.replace('/login');
}
}, [user, loading, router]);
if (loading || !user) {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="text-muted-foreground">Loading...</div>
</div>
);
}
const go = (href: string) => (e: React.MouseEvent) => {
e.preventDefault();
setNavOpen(false);
router.push(href);
};
return (
<AppShell>
<AppShellSkipLink />
<AppShellMobileToggle open={navOpen} onClick={() => setNavOpen(o => !o)} />
<AppShellOverlay open={navOpen} onClick={() => setNavOpen(false)} />
<AppShellSidebar open={navOpen} label="Primary">
<div className="flex h-full flex-col gap-6 p-4">
<Link href="/dashboard" className="px-1 text-lg font-bold tracking-tight">
Tracker
</Link>
<AppShellNav>
{NAV_ITEMS.map(item => (
<AppShellNavItem
key={item.href}
href={item.href}
active={
item.href === '/dashboard'
? pathname === item.href
: pathname.startsWith(item.href)
}
onClick={go(item.href)}
>
{item.label}
</AppShellNavItem>
))}
</AppShellNav>
<div className="mt-auto space-y-3">
<ProductSwitcher />
<Button
variant="secondary"
size="sm"
className="w-full justify-start"
onClick={openCommandPalette}
>
Search K
</Button>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
>
{theme === 'dark' ? 'Light mode' : 'Dark mode'}
</Button>
<div className="truncate px-1 text-sm text-muted-foreground">{user.email}</div>
<Button variant="ghost" size="sm" className="w-full justify-start" onClick={logout}>
Sign out
</Button>
</div>
</div>
</AppShellSidebar>
<AppShellMain>
<SystemBanners />
{children}
</AppShellMain>
</AppShell>
);
}