import Link from "next/link"; import Image from "next/image"; import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; import { LayoutDashboard, Boxes, GitBranch, ShieldAlert, ClipboardCheck, KanbanSquare, Siren, BookOpenText, MessagesSquare, Network, FolderCheck, Truck, LineChart, Search, Settings, } from "lucide-react"; import { auth, signOut } from "@/server/auth"; import { dbForTenant } from "@/server/db"; import { hasPermission } from "@/server/rbac"; import { HREF_TO_MODULE } from "@/lib/modules"; import { Button } from "@/components/ui/button"; import { NavLink } from "@/components/nav-link"; export default async function AppLayout({ children, }: Readonly<{ children: React.ReactNode }>) { const session = await auth(); if (!session?.user) redirect("/login"); // Kontostatus (autoritativ aus DB, nicht aus dem JWT): deaktivierte Nutzer werden // abgemeldet, ein offener Passwortwechsel wird erzwungen (Force-Change). const account = await dbForTenant(session.user.tenantId).user.findUnique({ where: { id: session.user.id }, select: { status: true, mustChangePassword: true }, }); if (!account || account.status !== "ACTIVE") { return (

Konto deaktiviert

Ihr Zugang wurde deaktiviert. Bitte wenden Sie sich an Ihre Administration.

{ "use server"; await signOut({ redirectTo: "/login" }); }} className="mt-5">
); } if (account.mustChangePassword) redirect("/change-password"); const t = await getTranslations("nav"); const tc = await getTranslations("common"); const nav = [ { href: "/dashboard", label: t("dashboard"), icon: LayoutDashboard, enabled: true }, { href: "/assets", label: t("assets"), icon: Boxes, enabled: true }, { href: "/processes", label: t("bia"), icon: GitBranch, enabled: true }, { href: "/risks", label: t("risks"), icon: ShieldAlert, enabled: true }, { href: "/soa", label: t("soa"), icon: ClipboardCheck, enabled: false }, { href: "/measures", label: t("measures"), icon: KanbanSquare, enabled: true }, { href: "/incidents", label: t("incidents"), icon: Siren, enabled: false }, { href: "/policies", label: t("policies"), icon: BookOpenText, enabled: true }, { href: "/chat", label: t("chat"), icon: MessagesSquare, enabled: false }, { href: "/dependencies", label: t("dependencies"), icon: Network, enabled: true }, { href: "/evidence", label: t("evidence"), icon: FolderCheck, enabled: false }, { href: "/suppliers", label: t("suppliers"), icon: Truck, enabled: true }, { href: "/review", label: t("review"), icon: LineChart, enabled: false }, ]; // Modul-Gating: deaktivierte Module werden ausgeblendet (§3.4) const moduleRows = await dbForTenant(session.user.tenantId).tenantModule.findMany(); const disabledModules = new Set(moduleRows.filter((m) => !m.enabled).map((m) => m.moduleKey)); const moduleEnabled = (href: string) => { const key = HREF_TO_MODULE[href]; return !key || !disabledModules.has(key); }; const visibleNav = nav.filter((item) => !item.enabled || moduleEnabled(item.href)); const canManageTenant = hasPermission(session, "tenant:manage"); const initials = (session.user.name ?? "?") .split(/\s+/) .map((p) => p[0]) .slice(0, 2) .join("") .toUpperCase(); async function logout() { "use server"; await signOut({ redirectTo: "/login" }); } return (

{session.user.name}

{session.user.tenantSlug}

{initials}
{children}
); }