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

Viewing files at commit 61a3d5a92277Commit detailsView latest

apps/dashboard/src/components/app-sidebar.tsx 185 lines · 5.4 KB · tsx
1
import {
2
  BarChart3,
3
  Bot,
4
  History,
5
  Inbox,
6
  LayoutDashboard,
7
  LogOut,
8
  Moon,
9
  Plus,
10
  Settings,
11
  Sun,
12
} from 'lucide-react';
13
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
14
import {
15
  DropdownMenu,
16
  DropdownMenuContent,
17
  DropdownMenuItem,
18
  DropdownMenuSeparator,
19
  DropdownMenuTrigger,
20
} from '@/components/ui/dropdown-menu';
21
import {
22
  Sidebar,
23
  SidebarContent,
24
  SidebarFooter,
25
  SidebarGroup,
26
  SidebarGroupContent,
27
  SidebarHeader,
28
  SidebarMenu,
29
  SidebarMenuBadge,
30
  SidebarMenuButton,
31
  SidebarMenuItem,
32
  SidebarRail,
33
  SidebarSeparator,
34
  useSidebar,
35
} from '@/components/ui/sidebar';
36
import { ClaspMark } from '@/components/clasp-mark';
37
import { pageMeta, primaryNav, type Page } from '@/lib/navigation';
38
import { initials } from '@/lib/utils';
39
40
const navIcons = {
41
  dashboard: LayoutDashboard,
42
  history: History,
43
  conversations: Inbox,
44
  agents: Bot,
45
  insights: BarChart3,
46
  connect: Plus,
47
  settings: Settings,
48
} as const;
49
50
export type ShellUser = {
51
  name?: string;
52
  email?: string;
53
  picture?: string;
54
};
55
56
export type NavBadges = {
57
  dashboard: number;
58
  conversations: number;
59
  connect: number;
60
};
61
62
export function AppSidebar({
63
  page,
64
  navigate,
65
  user,
66
  theme,
67
  onToggleTheme,
68
  onSignOut,
69
  badges,
70
}: {
71
  page: Page;
72
  navigate: (page: Page) => void;
73
  user: ShellUser;
74
  theme: 'dark' | 'light';
75
  onToggleTheme: () => void;
76
  onSignOut: () => void;
77
  badges: NavBadges;
78
}) {
79
  const { setOpenMobile } = useSidebar();
80
  const go = (next: Page) => {
81
    navigate(next);
82
    setOpenMobile(false);
83
  };
84
  const name = user.name || 'OpenClasp user';
85
  const email = user.email || 'Signed in';
86
87
  return (
88
    <Sidebar collapsible="icon">
89
      <SidebarHeader>
90
        <SidebarMenu>
91
          <SidebarMenuItem>
92
            <SidebarMenuButton size="lg" tooltip="OpenClasp" onClick={() => go('dashboard')}>
93
              <ClaspMark className="sidebarBrandMark" size={18} />
94
              <span className="sidebarBrandText">
95
                <strong>openclasp</strong>
96
              </span>
97
            </SidebarMenuButton>
98
          </SidebarMenuItem>
99
        </SidebarMenu>
100
      </SidebarHeader>
101
      <SidebarContent>
102
        <SidebarGroup>
103
          <SidebarGroupContent>
104
            <SidebarMenu>
105
              {primaryNav.map((item) => {
106
                const Icon = navIcons[item];
107
                const count = item in badges ? badges[item as keyof NavBadges] : 0;
108
                return (
109
                  <SidebarMenuItem key={item}>
110
                    <SidebarMenuButton
111
                      isActive={page === item}
112
                      tooltip={pageMeta[item].label}
113
                      onClick={() => go(item)}
114
                    >
115
                      <Icon />
116
                      <span>{pageMeta[item].label}</span>
117
                    </SidebarMenuButton>
118
                    {count > 0 ? (
119
                      <SidebarMenuBadge>{count}</SidebarMenuBadge>
120
                    ) : null}
121
                  </SidebarMenuItem>
122
                );
123
              })}
124
            </SidebarMenu>
125
          </SidebarGroupContent>
126
        </SidebarGroup>
127
        <SidebarSeparator />
128
        <SidebarGroup>
129
          <SidebarGroupContent>
130
            <SidebarMenu>
131
              <SidebarMenuItem>
132
                <SidebarMenuButton
133
                  isActive={page === 'settings'}
134
                  tooltip="settings"
135
                  onClick={() => go('settings')}
136
                >
137
                  <Settings />
138
                  <span>settings</span>
139
                </SidebarMenuButton>
140
              </SidebarMenuItem>
141
            </SidebarMenu>
142
          </SidebarGroupContent>
143
        </SidebarGroup>
144
      </SidebarContent>
145
      <SidebarFooter>
146
        <SidebarMenu>
147
          <SidebarMenuItem>
148
            <DropdownMenu>
149
              <DropdownMenuTrigger asChild>
150
                <SidebarMenuButton size="lg" className="data-[state=open]:bg-sidebar-accent">
151
                  <Avatar size="sm" className="rounded-none">
152
                    {user.picture ? <AvatarImage src={user.picture} alt="" /> : null}
153
                    <AvatarFallback className="rounded-none bg-primary text-[10px] font-medium text-primary-foreground">
154
                      {initials(name)}
155
                    </AvatarFallback>
156
                  </Avatar>
157
                  <span className="grid min-w-0 flex-1 text-left text-sm leading-tight">
158
                    <span className="truncate font-medium">{name}</span>
159
                    <span className="truncate text-[11px] text-muted-foreground">{email}</span>
160
                  </span>
161
                </SidebarMenuButton>
162
              </DropdownMenuTrigger>
163
              <DropdownMenuContent className="min-w-56" side="top" align="start" sideOffset={6}>
164
                <DropdownMenuItem onClick={onToggleTheme}>
165
                  {theme === 'dark' ? <Sun /> : <Moon />}
166
                  {theme === 'dark' ? 'Light theme' : 'Dark theme'}
167
                </DropdownMenuItem>
168
                <DropdownMenuItem onClick={() => go('settings')}>
169
                  <Settings />
170
                  Settings
171
                </DropdownMenuItem>
172
                <DropdownMenuSeparator />
173
                <DropdownMenuItem variant="destructive" onClick={onSignOut}>
174
                  <LogOut />
175
                  Sign out
176
                </DropdownMenuItem>
177
              </DropdownMenuContent>
178
            </DropdownMenu>
179
          </SidebarMenuItem>
180
        </SidebarMenu>
181
      </SidebarFooter>
182
      <SidebarRail />
183
    </Sidebar>
184
  );
185
}