Olympus/frontend/athena/components/Sidebar.tsx
2026-07-03 10:25:52 +02:00

139 lines
2.8 KiB
TypeScript

"use client";
import { useCallback, useEffect, useState } from "react";
import Link from "next/link";
import {
BookOpen,
ClipboardList,
FileText,
LayoutDashboard,
Package,
Settings,
Shield,
ShoppingCart,
Users,
Wrench,
} from "lucide-react";
import { api } from "@/lib/api";
import { hasPermission } from "@/lib/permissions";
import type { CurrentUser, PermissionName } from "@/types/rbac";
const menu = [
{
icon: LayoutDashboard,
name: "Dashboard",
href: "/dashboard",
permission: "dashboard.read",
},
{
icon: ClipboardList,
name: "Audit Logs",
href: "/audit-logs",
permission: "audit_logs.read",
},
{
icon: Users,
name: "Benutzer",
href: "/users",
permission: "users.read",
},
{
icon: Shield,
name: "Rollen & Rechte",
href: "/roles",
permission: "roles.read",
},
{
icon: Users,
name: "Kunden",
href: "/customers",
permission: "customers.read",
},
{
icon: BookOpen,
name: "Wissensdatenbank",
href: "/knowledge",
permission: "knowledge.read",
},
{
icon: Wrench,
name: "Reparaturen",
href: "/repairs",
},
{
icon: Package,
name: "Lager",
href: "/inventory",
},
{
icon: ShoppingCart,
name: "eBay",
href: "/ebay",
},
{
icon: FileText,
name: "Dokumente",
href: "/documents",
},
{
icon: Settings,
name: "Einstellungen",
href: "/settings",
},
] satisfies Array<{
icon: typeof LayoutDashboard;
name: string;
href: string;
permission?: PermissionName;
}>;
export default function Sidebar() {
const [currentUser, setCurrentUser] = useState<CurrentUser | null>(null);
const loadCurrentUser = useCallback(async () => {
try {
const response = await api.get<CurrentUser>("/me");
setCurrentUser(response.data);
} catch {
setCurrentUser(null);
}
}, []);
useEffect(() => {
queueMicrotask(() => {
void loadCurrentUser();
});
}, [loadCurrentUser]);
const visibleMenu = menu.filter((item) => (
!item.permission || hasPermission(currentUser, item.permission)
));
return (
<aside className="w-64 h-screen bg-slate-900 text-white flex flex-col">
<div className="p-6 border-b border-slate-800">
<h1 className="text-3xl font-bold">
Olympus
</h1>
</div>
<nav className="flex-1 p-4 space-y-2">
{visibleMenu.map((item) => (
<Link
key={item.name}
href={item.href}
className="flex items-center gap-3 rounded-lg px-4 py-3 hover:bg-slate-800 transition-colors"
>
<item.icon size={20} />
<span>{item.name}</span>
</Link>
))}
</nav>
<div className="border-t border-slate-800 p-4 text-sm text-slate-400">
Olympus CRM v0.1
</div>
</aside>
);
}