b6167bcb-7363-4695-9481-addd81de4367
This commit is contained in:
1 parent
fdc3b82d10
commit
d226dabba3
136 files changed
+13904
-2
No files matched your search
@@ -0,0 +1,150 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
LayoutDashboard,
|
||||
Users,
|
||||
Radar,
|
||||
FolderKanban,
|
||||
KeyRound,
|
||||
Receipt,
|
||||
Settings2,
|
||||
Command,
|
||||
Search,
|
||||
Bot,
|
||||
} from 'lucide-react'
|
||||
import { useAos, type ViewId } from '@/store/aos'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
|
||||
const NAV: {
|
||||
group: string
|
||||
items: { id: ViewId; label: string; icon: typeof LayoutDashboard; permission: string }[]
|
||||
}[] = [
|
||||
{
|
||||
group: 'Übersicht',
|
||||
items: [{ id: 'dashboard', label: 'Dashboard', icon: LayoutDashboard, permission: '*' }],
|
||||
},
|
||||
{
|
||||
group: 'Vertrieb',
|
||||
items: [
|
||||
{ id: 'crm', label: 'CRM', icon: Users, permission: 'crm.company:read' },
|
||||
{ id: 'leads', label: 'Leads Finder', icon: Radar, permission: 'leads:read' },
|
||||
],
|
||||
},
|
||||
{
|
||||
group: 'Lieferung',
|
||||
items: [{ id: 'projects', label: 'Projekte', icon: FolderKanban, permission: 'projects:read' }],
|
||||
},
|
||||
{
|
||||
group: 'Betrieb',
|
||||
items: [
|
||||
{ id: 'licenses', label: 'Lizenzen', icon: KeyRound, permission: 'licenses:read' },
|
||||
{ id: 'finance', label: 'Finanzen', icon: Receipt, permission: 'finance:read' },
|
||||
],
|
||||
},
|
||||
{
|
||||
group: 'System',
|
||||
items: [{ id: 'settings', label: 'Erweiterungen', icon: Settings2, permission: 'admin' }],
|
||||
},
|
||||
]
|
||||
|
||||
export function SidebarNav() {
|
||||
const { view, setView, setSidebarOpen, setCommandOpen } = useAos()
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col bg-sidebar text-sidebar-foreground">
|
||||
{/* Brand */}
|
||||
<div className="flex h-16 items-center gap-2.5 px-5 border-b border-sidebar-border">
|
||||
<div className="flex size-9 items-center justify-center rounded-md bg-primary text-primary-foreground font-bold tracking-tight">
|
||||
A
|
||||
</div>
|
||||
<div className="flex flex-col leading-tight">
|
||||
<span className="text-sm font-semibold">AOS</span>
|
||||
<span className="text-[11px] text-muted-foreground">Nordlicht Digital</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Search trigger */}
|
||||
<div className="px-3 pt-3">
|
||||
<button
|
||||
onClick={() => setCommandOpen(true)}
|
||||
className="flex w-full items-center gap-2 rounded-md border border-sidebar-border bg-background/50 px-3 py-2 text-sm text-muted-foreground transition hover:bg-background"
|
||||
>
|
||||
<Search className="size-3.5" />
|
||||
<span>Suchen…</span>
|
||||
<kbd className="ml-auto inline-flex items-center gap-0.5 rounded border border-border bg-muted px-1.5 py-0.5 text-[10px] font-mono">
|
||||
<Command className="size-2.5" />K
|
||||
</kbd>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Nav */}
|
||||
<nav className="flex-1 overflow-y-auto px-2 py-3 [scrollbar-width:thin]">
|
||||
{NAV.map((section) => (
|
||||
<div key={section.group} className="mb-4">
|
||||
<p className="px-3 pb-1.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/70">
|
||||
{section.group}
|
||||
</p>
|
||||
<div className="space-y-0.5">
|
||||
{section.items.map((item) => {
|
||||
const Icon = item.icon
|
||||
const active = view === item.id
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => {
|
||||
setView(item.id)
|
||||
setSidebarOpen(false)
|
||||
}}
|
||||
className={cn(
|
||||
'group flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition',
|
||||
active
|
||||
? 'bg-sidebar-accent text-sidebar-accent-foreground'
|
||||
: 'text-muted-foreground hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground'
|
||||
)}
|
||||
aria-current={active ? 'page' : undefined}
|
||||
>
|
||||
<Icon className={cn('size-4 shrink-0', active ? 'text-foreground' : 'text-muted-foreground group-hover:text-foreground')} />
|
||||
<span className="truncate">{item.label}</span>
|
||||
{item.id === 'leads' && (
|
||||
<Badge variant="secondary" className="ml-auto h-5 px-1.5 text-[10px]">
|
||||
AI
|
||||
</Badge>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* AI status */}
|
||||
<div className="mx-3 mb-3 rounded-md border border-sidebar-border bg-background/40 p-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex size-7 items-center justify-center rounded bg-muted">
|
||||
<Bot className="size-3.5 text-muted-foreground" />
|
||||
</div>
|
||||
<div className="flex flex-col leading-tight">
|
||||
<span className="text-xs font-medium">AI-Ebene</span>
|
||||
<span className="text-[10px] text-muted-foreground">Optional · Daten im Server</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* User */}
|
||||
<div className="border-t border-sidebar-border px-3 py-3">
|
||||
<Button variant="ghost" className="h-auto w-full justify-start gap-3 px-2 py-2">
|
||||
<div className="flex size-8 items-center justify-center rounded-full bg-primary text-primary-foreground text-xs font-semibold">
|
||||
MB
|
||||
</div>
|
||||
<div className="flex flex-col items-start leading-tight">
|
||||
<span className="text-sm font-medium">Max Brandt</span>
|
||||
<span className="text-[11px] text-muted-foreground">Owner</span>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user