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