import { redirect } from "next/navigation"; import { DashboardShell } from "@/components/dashboard/dashboard-shell"; import { getCurrentAccountingPeriod } from "@/lib/accounting-periods"; import { getAppSettings, toApprovalThresholdNumber } from "@/lib/app-settings"; import { getRollbackMetadata } from "@/lib/audit-log"; import type { DashboardAccountingPeriod, DashboardAuditLog, DashboardManagedUser, DashboardViewer, DashboardWorkingGroup } from "@/lib/dashboard-types"; import { canManageUsers, normalizeApprovalPermissions } from "@/lib/domain"; import prisma from "@/lib/prisma"; import { buildRecurringOccurrences, getExpensePeriodAmount } from "@/lib/recurring-expenses"; import { getCurrentViewer } from "@/lib/session"; export const dynamic = "force-dynamic"; export default async function DashboardPage() { const viewer = await getCurrentViewer(); if (!viewer) { redirect("/login"); } const [currentPeriod, appSettings] = await Promise.all([getCurrentAccountingPeriod(), getAppSettings()]); if (!currentPeriod) { throw new Error("Kein Abrechnungszeitraum gefunden."); } const accountingPeriods = await prisma.accountingPeriod.findMany({ orderBy: { startsAt: "desc" } }); const workingGroups = await prisma.workingGroup.findMany({ include: { members: { select: { id: true, name: true, username: true, role: true } }, budgets: { where: { periodId: currentPeriod.id }, orderBy: { name: "asc" }, include: { expenses: { orderBy: { createdAt: "desc" }, include: { creator: { select: { id: true, username: true } }, approvals: { orderBy: { timestamp: "asc" }, include: { user: { select: { id: true, username: true } } } } } } } } }, orderBy: { name: "asc" } }); const managedUsers = canManageUsers(viewer.role) ? await prisma.user.findMany({ include: { workingGroup: { select: { name: true } }, _count: { select: { approvals: true, createdExpenses: true } } }, orderBy: [{ role: "asc" }, { username: "asc" }] }) : []; const auditLogs = canManageUsers(viewer.role) ? await prisma.auditLog.findMany({ orderBy: { createdAt: "desc" }, take: 120, include: { actor: { select: { id: true, name: true, username: true, role: true } } } }) : []; const serializedViewer: DashboardViewer = { id: viewer.id, name: viewer.username, username: viewer.username, role: viewer.role, workingGroupId: viewer.workingGroupId, approvalPermissions: normalizeApprovalPermissions(viewer.role, viewer.approvalPermissions, viewer.approvalPreference) }; const serializedGroups: DashboardWorkingGroup[] = workingGroups.map((workingGroup) => ({ id: workingGroup.id, name: workingGroup.name, totalBudget: workingGroup.budgets.reduce((sum, budget) => sum + Number(budget.totalBudget), 0), members: workingGroup.members.map((member) => ({ id: member.id, name: member.username, username: member.username, role: member.role })), budgets: workingGroup.budgets.map((budget) => ({ id: budget.id, name: budget.name, totalBudget: Number(budget.totalBudget), colorCode: budget.colorCode, periodId: budget.periodId, expenses: budget.expenses.map((expense) => { const amount = Number(expense.amount); const recurrenceStartAt = expense.recurrence === "MONTHLY" ? (expense.recurrenceStartAt ?? expense.createdAt).toISOString() : null; const occurrences = expense.recurrence === "MONTHLY" && recurrenceStartAt ? buildRecurringOccurrences({ expenseId: expense.id, amount, recurrenceStartAt, periodStartsAt: currentPeriod.startsAt, periodEndsAt: currentPeriod.endsAt }) : []; return { id: expense.id, title: expense.title, description: expense.description, amount, periodAmount: getExpensePeriodAmount(amount, expense.recurrence, occurrences.length), occurrenceCount: expense.recurrence === "MONTHLY" ? occurrences.length : 1, occurrences, budgetId: expense.budgetId, periodId: expense.periodId, approvalStatus: expense.approvalStatus, recurrence: expense.recurrence, recurrenceStartAt, paidAt: expense.paidAt?.toISOString() ?? null, documentedAt: expense.documentedAt?.toISOString() ?? null, proofUrl: expense.proofUrl, createdAt: expense.createdAt.toISOString(), creator: { id: expense.creator.id, name: expense.creator.username }, approvals: expense.approvals.map((approval) => ({ id: approval.id, approvalType: approval.approvalType, timestamp: approval.timestamp.toISOString(), user: { id: approval.user.id, name: approval.user.username } })) }; }) })) })); const serializedUsers: DashboardManagedUser[] = managedUsers.map((user) => ({ id: user.id, name: user.username, username: user.username, role: user.role, workingGroupId: user.workingGroupId, workingGroupName: user.workingGroup?.name ?? null, approvalPermissions: normalizeApprovalPermissions(user.role, user.approvalPermissions, user.approvalPreference), createdExpensesCount: user._count.createdExpenses, approvalsCount: user._count.approvals })); const serializedPeriods: DashboardAccountingPeriod[] = accountingPeriods.map((period) => ({ id: period.id, name: period.name, startsAt: period.startsAt.toISOString(), endsAt: period.endsAt.toISOString(), isCurrent: period.isCurrent })); const serializedAuditLogs: DashboardAuditLog[] = auditLogs.map((entry) => ({ id: entry.id, action: entry.action, entityType: entry.entityType, entityId: entry.entityId, entityLabel: entry.entityLabel, summary: entry.summary, canRestore: Boolean(getRollbackMetadata(entry.metadata)), createdAt: entry.createdAt.toISOString(), actor: entry.actor ? { id: entry.actor.id, name: entry.actor.username, username: entry.actor.username, role: entry.actor.role } : null })); return ( ); }