dak.c2s/frontend/src/components/layout/Sidebar.tsx
CCS Admin 0767e1ed18 feat: auth context, login/register pages, app layout with routing
- Task 19: TypeScript types for all API entities, axios client with JWT
  refresh interceptor, auth service, AuthContext provider, ProtectedRoute
- Task 20: Login page with MFA support, Register page with invitation
  token support, German labels, zod validation
- Task 21: Responsive sidebar with role-aware navigation, header with
  user dropdown, AppLayout with Sheet for mobile, full React Router setup
  with placeholder pages for all routes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 08:21:00 +00:00

101 lines
3 KiB
TypeScript

import { NavLink } from 'react-router-dom'
import { useAuth } from '@/context/AuthContext'
import { cn } from '@/lib/utils'
import { Separator } from '@/components/ui/separator'
import {
LayoutDashboard,
FolderOpen,
Upload,
FileEdit,
ClipboardCheck,
FileBarChart,
Users,
Mail,
History,
} from 'lucide-react'
interface NavItem {
label: string
to: string
icon: React.ComponentType<{ className?: string }>
adminOnly?: boolean
mitarbeiterOnly?: boolean
}
const mainNavItems: NavItem[] = [
{ label: 'Dashboard', to: '/dashboard', icon: LayoutDashboard },
{ label: 'Faelle', to: '/cases', icon: FolderOpen },
{ label: 'Import', to: '/import', icon: Upload, adminOnly: true },
{ label: 'ICD-Eingabe', to: '/icd', icon: FileEdit, mitarbeiterOnly: true },
{ label: 'Coding', to: '/coding', icon: ClipboardCheck, adminOnly: true },
{ label: 'Berichte', to: '/reports', icon: FileBarChart },
]
const adminNavItems: NavItem[] = [
{ label: 'Benutzer', to: '/admin/users', icon: Users },
{ label: 'Einladungen', to: '/admin/invitations', icon: Mail },
{ label: 'Audit-Log', to: '/admin/audit', icon: History },
]
function NavItemLink({ item }: { item: NavItem }) {
const Icon = item.icon
return (
<NavLink
to={item.to}
className={({ isActive }) =>
cn(
'flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors',
isActive
? 'bg-primary text-primary-foreground'
: 'text-muted-foreground hover:bg-accent hover:text-accent-foreground'
)
}
>
<Icon className="h-4 w-4 shrink-0" />
<span>{item.label}</span>
</NavLink>
)
}
export function Sidebar({ className }: { className?: string }) {
const { user, isAdmin } = useAuth()
const visibleMainItems = mainNavItems.filter((item) => {
if (item.adminOnly && !isAdmin) return false
if (item.mitarbeiterOnly && user?.role !== 'dak_mitarbeiter') return false
return true
})
return (
<aside className={cn('flex h-full flex-col gap-4 p-4', className)}>
<div className="flex items-center gap-2 px-3 py-2">
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-primary text-primary-foreground font-bold text-sm">
D
</div>
<span className="font-semibold text-lg">DAK Portal</span>
</div>
<nav className="flex flex-col gap-1">
{visibleMainItems.map((item) => (
<NavItemLink key={item.to} item={item} />
))}
</nav>
{isAdmin && (
<>
<Separator />
<div className="px-3 py-1">
<span className="text-xs font-semibold uppercase text-muted-foreground tracking-wider">
Administration
</span>
</div>
<nav className="flex flex-col gap-1">
{adminNavItems.map((item) => (
<NavItemLink key={item.to} item={item} />
))}
</nav>
</>
)}
</aside>
)
}