M3: seat configurator UI
A "AI seats" page (shadcn, on the design language): manage BYOK model connections (add + test; the key is write-only), create seats on a team, and configure an agent per seat — name, the color-graded autonomy dial (draft slate / gated indigo / auto teal), a model connection, skill toggles from the registry, and docs. Navigable AppShell sidebar (Board / AI seats). Verified: client `npm run build` clean (1890 modules, tsc + vite).
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { Inbox, type LucideIcon, LayoutDashboard, LogOut, Network } from 'lucide-react'
|
||||
import { Link, useLocation } from 'react-router'
|
||||
import { Bot, Inbox, type LucideIcon, LayoutDashboard, LogOut, Network } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { cn } from '@/lib/utils'
|
||||
@@ -25,8 +26,9 @@ export function AppShell({ children }: { children: ReactNode }) {
|
||||
<Separator className="bg-sidebar-border" />
|
||||
|
||||
<nav className="flex flex-1 flex-col gap-1 p-3">
|
||||
<NavItem icon={LayoutDashboard} label="Board" active />
|
||||
<NavItem icon={Inbox} label="Cartable" />
|
||||
<NavItem icon={LayoutDashboard} label="Board" to="/" />
|
||||
<NavItem icon={Bot} label="AI seats" to="/seats" />
|
||||
<NavItem icon={Inbox} label="Cartable" muted />
|
||||
<NavItem icon={Network} label="Org chart" muted />
|
||||
</nav>
|
||||
|
||||
@@ -54,25 +56,38 @@ export function AppShell({ children }: { children: ReactNode }) {
|
||||
function NavItem({
|
||||
icon: Icon,
|
||||
label,
|
||||
active,
|
||||
to,
|
||||
muted,
|
||||
}: {
|
||||
icon: LucideIcon
|
||||
label: string
|
||||
active?: boolean
|
||||
to?: string
|
||||
muted?: boolean
|
||||
}) {
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
'flex items-center gap-3 rounded-md px-3 py-2 text-sm',
|
||||
active ? 'bg-sidebar-accent font-medium text-sidebar-accent-foreground' : 'text-sidebar-foreground/80',
|
||||
muted && 'opacity-50',
|
||||
)}
|
||||
>
|
||||
const location = useLocation()
|
||||
const active = to ? location.pathname === to : false
|
||||
|
||||
const className = cn(
|
||||
'flex items-center gap-3 rounded-md px-3 py-2 text-sm',
|
||||
active ? 'bg-sidebar-accent font-medium text-sidebar-accent-foreground' : 'text-sidebar-foreground/80',
|
||||
muted ? 'opacity-50' : 'hover:bg-sidebar-accent/60',
|
||||
)
|
||||
|
||||
const content = (
|
||||
<>
|
||||
<Icon className="size-4" />
|
||||
{label}
|
||||
{muted && <span className="ml-auto text-[10px] uppercase tracking-wide opacity-70">soon</span>}
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
|
||||
if (!to || muted) {
|
||||
return <span className={className}>{content}</span>
|
||||
}
|
||||
|
||||
return (
|
||||
<Link to={to} className={className}>
|
||||
{content}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user