import { Suspense } from "react"; import { ReportsPageClient } from "@/components/reports/ReportsPageClient"; import { aggregateEvents, aggregateReport, parseEventDateParam, parseEventGroupDimension, parseEventSplitDimension, parseEventType, parseGroupDimension, parseIsoDateParam, parseMeasure, parseMode, parseSplitDimension, sumValues, totalForRows, } from "@/lib/reports"; import { loadEventHistory, loadOrgLookups, loadSnapshotEmployees } from "@/lib/reports-data"; import { currentUserId } from "@/lib/auth/session"; import { withUser } from "@/lib/db"; type SearchParams = { mode?: string; measure?: string; group?: string; split?: string; division?: string; location?: string; status?: string; employment?: string; asOf?: string; eventType?: string; from?: string; to?: string; }; export default async function ReportsPage({ searchParams }: { searchParams: Promise }) { const params = await searchParams; const mode = parseMode(params.mode); // Both modes are parsed up front so the data load can start before // anything is awaited. They group by different dimension sets, so the // parsed values stay separate rather than being widened into one type. const asOf = parseIsoDateParam(params.asOf); const eventGroup = parseEventGroupDimension(params.group); const eventSplit = parseEventSplitDimension(params.split); const eventType = parseEventType(params.eventType); const from = parseEventDateParam(params.from); const to = parseEventDateParam(params.to); const measure = parseMeasure(params.measure); const group = parseGroupDimension(params.group); const split = parseSplitDimension(params.split); // The report data depends on neither the org lookups nor on who is signed // in, so all three go out together. Against a hosted database a round trip // costs about as much as the query itself, which made this page's three // sequential waves its dominant cost. const userId = await currentUserId(); // Alles in einer Transaktion — dort gilt der Sitzungskontext, und der // Lesestand ist über alle Abfragen hinweg derselbe. Vorher waren es drei // Wellen nacheinander, was gegen eine entfernte Datenbank der teuerste // Teil dieser Seite war. const { lookups, divisions, locations, events, employees, savedReports } = await withUser(userId, async (tx) => { const [{ lookups, divisions, locations }, events, employees, savedReports] = await Promise.all([ loadOrgLookups(tx), mode === "events" ? loadEventHistory(tx, { eventType: eventType ?? undefined, division: params.division, location: params.location, from, to, }) : Promise.resolve([]), mode === "snapshot" ? loadSnapshotEmployees(tx, { division: params.division, location: params.location, status: params.status, employment: params.employment, asOf, }) : Promise.resolve([]), userId ? tx .selectFrom("saved_reports") .select(["id", "name", "config"]) .where("created_by", "=", userId) .orderBy("created_at", "desc") .execute() : Promise.resolve([]), ]); return { lookups, divisions, locations, events, employees, savedReports }; }); if (mode === "events") { const rows = aggregateEvents(events, eventGroup, eventSplit, lookups); return ( ); } const rows = aggregateReport(employees, measure, group, split, lookups, asOf); return ( ); }