Files
Teamup/client/src/components/AppShell.tsx
T

95 lines
3.0 KiB
TypeScript
Raw Normal View History

2026-06-09 15:15:35 +03:30
import type { ReactNode } from 'react'
2026-06-10 00:02:59 +03:30
import { Link, useLocation } from 'react-router'
import { Bot, Inbox, type LucideIcon, LayoutDashboard, LogOut, Network, ShieldCheck } from 'lucide-react'
2026-06-09 15:15:35 +03:30
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'
import { cn } from '@/lib/utils'
import { useAuth } from '@/store/auth'
export function AppShell({ children }: { children: ReactNode }) {
const email = useAuth((s) => s.email)
const logout = useAuth((s) => s.logout)
return (
<div className="flex min-h-screen bg-background text-foreground">
<aside className="flex w-60 shrink-0 flex-col bg-sidebar text-sidebar-foreground">
<div className="flex items-center gap-3 px-5 py-4">
<span className="grid size-8 place-items-center rounded-md bg-sidebar-primary font-bold text-sidebar-primary-foreground">
T
</span>
<div className="leading-tight">
<div className="font-semibold tracking-tight">TeamUp.AI</div>
<div className="text-xs text-sidebar-foreground/60">command center</div>
</div>
</div>
<Separator className="bg-sidebar-border" />
<nav className="flex flex-1 flex-col gap-1 p-3">
2026-06-10 00:02:59 +03:30
<NavItem icon={LayoutDashboard} label="Board" to="/" />
<NavItem icon={Bot} label="AI seats" to="/seats" />
<NavItem icon={ShieldCheck} label="Review inbox" to="/reviews" />
2026-06-10 00:02:59 +03:30
<NavItem icon={Inbox} label="Cartable" muted />
2026-06-09 15:15:35 +03:30
<NavItem icon={Network} label="Org chart" muted />
</nav>
<Separator className="bg-sidebar-border" />
<div className="flex items-center justify-between gap-2 p-3">
<span className="truncate text-xs text-sidebar-foreground/70">{email ?? 'signed in'}</span>
<Button
variant="ghost"
size="sm"
onClick={logout}
className="text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
>
<LogOut data-icon="inline-start" />
Sign out
</Button>
</div>
</aside>
<main className="flex-1 overflow-auto">{children}</main>
</div>
)
}
function NavItem({
icon: Icon,
label,
2026-06-10 00:02:59 +03:30
to,
2026-06-09 15:15:35 +03:30
muted,
}: {
icon: LucideIcon
label: string
2026-06-10 00:02:59 +03:30
to?: string
2026-06-09 15:15:35 +03:30
muted?: boolean
}) {
2026-06-10 00:02:59 +03:30
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 = (
<>
2026-06-09 15:15:35 +03:30
<Icon className="size-4" />
{label}
{muted && <span className="ml-auto text-[10px] uppercase tracking-wide opacity-70">soon</span>}
2026-06-10 00:02:59 +03:30
</>
)
if (!to || muted) {
return <span className={className}>{content}</span>
}
return (
<Link to={to} className={className}>
{content}
</Link>
2026-06-09 15:15:35 +03:30
)
}