import Link from "next/link"; import { Suspense } from "react"; import { AuditFilters } from "@/components/audit/AuditFilters"; import { Pagination } from "@/components/ui/Pagination"; import { actionBadgeStyle } from "@/lib/colors"; import { sanitizeIlikeTerm } from "@/lib/supabase/query"; import { createClient } from "@/lib/supabase/server"; const PAGE_SIZE = 25; type SearchParams = { q?: string; action?: string; page?: string }; function pageHref(params: SearchParams, page: number): string { const sp = new URLSearchParams(); if (params.q) sp.set("q", params.q); if (params.action) sp.set("action", params.action); sp.set("page", String(page)); return `/audit?${sp.toString()}`; } // Pinned to Vienna and built once: audit_log.occurred_at is a timestamptz, and // an unpinned formatter renders it in the *server's* zone — UTC in Docker and // on Vercel — so every entry would read an hour or two early for the people // the log is for. const dateTimeFormatter = new Intl.DateTimeFormat("de-AT", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", timeZone: "Europe/Vienna", }); export default async function AuditPage({ searchParams }: { searchParams: Promise }) { const params = await searchParams; const supabase = await createClient(); const page = Math.max(1, Number(params.page ?? "1") || 1); const from = (page - 1) * PAGE_SIZE; const to = from + PAGE_SIZE - 1; let query = supabase .from("audit_log") .select("id, occurred_at, actor_name, action, target_label, target_employee_id, details", { count: "exact" }) .order("occurred_at", { ascending: false }) .range(from, to); if (params.action) query = query.eq("action", params.action); if (params.q) { const q = sanitizeIlikeTerm(params.q.trim()); query = query.or(`target_label.ilike.%${q}%,details.ilike.%${q}%,actor_name.ilike.%${q}%`); } const { data: entries, count } = await query; const totalPages = Math.max(1, Math.ceil((count ?? 0) / PAGE_SIZE)); return (

{count ?? 0} Einträge

{(entries ?? []).map((entry) => { return ( ); })} {(entries ?? []).length === 0 && ( )}
Zeitpunkt Benutzer:in Aktion Objekt Details
{dateTimeFormatter.format(new Date(entry.occurred_at))} {entry.actor_name} {entry.action} {entry.target_employee_id ? ( {entry.target_label} ) : ( entry.target_label )} {entry.details ?? "–"}
Keine Einträge gefunden.
pageHref(params, p)} label="Audit-Log" />

Alle Änderungen an Personal-Stammdaten werden automatisch protokolliert und sind unveränderbar.

); }