| 1 | import type { ReactNode } from 'react';
|
| 2 | import { ArrowRight, LogOut, Moon, Sun } from 'lucide-react';
|
| 3 | import {
|
| 4 | DropdownMenu,
|
| 5 | DropdownMenuContent,
|
| 6 | DropdownMenuItem,
|
| 7 | DropdownMenuSeparator,
|
| 8 | DropdownMenuTrigger,
|
| 9 | } from '@/components/ui/dropdown-menu';
|
| 10 | import type { ShellUser } from '@/components/app-sidebar';
|
| 11 | import { ClaspMark } from '@/components/clasp-mark';
|
| 12 | import { initials } from '@/lib/utils';
|
| 13 | import type { Page } from '@/lib/navigation';
|
| 14 |
|
| 15 | const views = [
|
| 16 | { page: 'dashboard' as const, label: 'agents' },
|
| 17 | { page: 'shield' as const, label: 'shield' },
|
| 18 | { page: 'marketplace' as const, label: 'marketplace' },
|
| 19 | { page: 'settings' as const, label: 'settings' },
|
| 20 | ];
|
| 21 |
|
| 22 | export function AppShell({
|
| 23 | page,
|
| 24 | navigate,
|
| 25 | user,
|
| 26 | theme,
|
| 27 | onToggleTheme,
|
| 28 | onSignOut,
|
| 29 | children,
|
| 30 | }: {
|
| 31 | page: Page;
|
| 32 | navigate: (page: Page) => void;
|
| 33 | user: ShellUser;
|
| 34 | theme: 'dark' | 'light';
|
| 35 | onToggleTheme: () => void;
|
| 36 | onSignOut: () => void;
|
| 37 | children: ReactNode;
|
| 38 | }) {
|
| 39 | const current =
|
| 40 | page === 'marketplace' || page === 'settings' || page === 'shield' ? page : 'dashboard';
|
| 41 | const name = user.name || 'OpenClasp user';
|
| 42 |
|
| 43 | return (
|
| 44 | <div className="landingPage simpleShell">
|
| 45 | <a className="skipLink" href="#main">
|
| 46 | Skip to content
|
| 47 | </a>
|
| 48 | <header className="landingNav dashHeader">
|
| 49 | <button className="landingBrand" type="button" onClick={() => navigate('dashboard')}>
|
| 50 | <ClaspMark className="landingMark" size={28} />
|
| 51 | <strong>openclasp</strong>
|
| 52 | </button>
|
| 53 | <nav className="dashNav" aria-label="Dashboard">
|
| 54 | {views.map((item) => (
|
| 55 | <button
|
| 56 | key={item.page}
|
| 57 | type="button"
|
| 58 | aria-current={current === item.page ? 'page' : undefined}
|
| 59 | onClick={() => navigate(item.page)}
|
| 60 | >
|
| 61 | {item.label}
|
| 62 | </button>
|
| 63 | ))}
|
| 64 | </nav>
|
| 65 | <div className="landingNavActions">
|
| 66 | <button
|
| 67 | className="themeButton"
|
| 68 | type="button"
|
| 69 | onClick={onToggleTheme}
|
| 70 | aria-label={`Switch to ${theme === 'dark' ? 'light' : 'dark'} theme`}
|
| 71 | >
|
| 72 | {theme === 'dark' ? <Sun /> : <Moon />}
|
| 73 | </button>
|
| 74 | <button className="navCta dashCta" type="button" onClick={() => navigate('connect')}>
|
| 75 | add agent <ArrowRight />
|
| 76 | </button>
|
| 77 | <DropdownMenu>
|
| 78 | <DropdownMenuTrigger asChild>
|
| 79 | <button className="accountMark" type="button" aria-label="Account menu">
|
| 80 | {initials(name)}
|
| 81 | </button>
|
| 82 | </DropdownMenuTrigger>
|
| 83 | <DropdownMenuContent align="end" sideOffset={8} className="dashMenu">
|
| 84 | <DropdownMenuItem onClick={onToggleTheme}>
|
| 85 | {theme === 'dark' ? <Sun /> : <Moon />}
|
| 86 | {theme === 'dark' ? 'Light theme' : 'Dark theme'}
|
| 87 | </DropdownMenuItem>
|
| 88 | <DropdownMenuSeparator />
|
| 89 | <DropdownMenuItem variant="destructive" onClick={onSignOut}>
|
| 90 | <LogOut /> Sign out
|
| 91 | </DropdownMenuItem>
|
| 92 | </DropdownMenuContent>
|
| 93 | </DropdownMenu>
|
| 94 | </div>
|
| 95 | </header>
|
| 96 | <main id="main" className="simpleMain">
|
| 97 | {children}
|
| 98 | </main>
|
| 99 | </div>
|
| 100 | );
|
| 101 | }
|