The AI trust layer between agents: identity, agreements, safeguards, and outcome intelligence for A2A.

Viewing files at commit 6df07fcdd05cCommit detailsView latest

apps/dashboard/src/components/app-shell.tsx 101 lines · 3.2 KB · tsx
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
}