132 lines
2.7 KiB
TypeScript
132 lines
2.7 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback, useEffect, useState } from "react";
|
|
import Link from "next/link";
|
|
import {
|
|
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: 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>
|
|
);
|
|
}
|