"use client"; import { useMemo, useState } from "react"; import { useMutation, useQuery } from "convex/react"; import { FunctionReturnType } from "convex/server"; import { Activity, Files, FileSearch, RotateCcw, SquarePen } from "lucide-react"; import Link from "next/link"; import { api } from "@/convex/_generated/api"; import type { Id } from "@/convex/_generated/dataModel"; import { Skeleton } from "@/components/ui/skeleton"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; type AuditDashboardRowsResult = FunctionReturnType; type AuditRow = Extract< NonNullable[number], { kind: "audit" } >; type AuditDashboardRow = NonNullable[number]; type AuditStatusFilter = "all" | "audit" | "generation" | "failed"; const statusText: Record = { draft: "Entwurf", approved: "Freigegeben", published: "Veröffentlicht", deactivated: "Deaktiviert", }; const fallbackStatus = "Unbekannt"; const generationStageText: Record = { audit_generation: "Audit-Generierung", classification: "Klassifikation", multimodalAudit: "Multimodale Analyse", germanCopy: "Deutsche Texte", qualityReview: "Qualitätsprüfung", }; function formatPageCount(pageCount: number) { return `${pageCount} Seite${pageCount === 1 ? "" : "n"}`; } function getStatusLabel(status: AuditRow["status"]) { return statusText[status] ?? fallbackStatus; } function getGenerationStatusLabel( row: Extract, ) { if (row.status === "pending") { return "Wartet auf Start"; } if (row.status === "failed") { return "Fehlgeschlagen"; } if (row.status === "canceled") { return "Abgebrochen"; } if (row.status === "succeeded") { return "Wartet auf finales Audit"; } return "Generierung läuft"; } function getStageLabel(stage: string) { return generationStageText[stage] ?? stage; } function AuditsBoardLoading() { return (

Evidence Dossier

Audits

Audits werden geladen...

{Array.from({ length: 4 }, (_, index) => ( ))}
); } export function AuditsBoard() { const dashboardRows = useQuery(api.audits.listDashboardRows, { limit: 100 }); const retryAuditRun = useMutation(api.audits.retryAuditRun); const [activeFilter, setActiveFilter] = useState("all"); const [retryingRunId, setRetryingRunId] = useState(null); const rows = useMemo(() => { if (!dashboardRows) { return []; } return [...dashboardRows].sort((a, b) => b.updatedAt - a.updatedAt); }, [dashboardRows]); const statusCounts = useMemo(() => { return { all: rows.length, audit: rows.filter((row) => row.kind === "audit").length, generation: rows.filter((row) => row.kind === "generation").length, failed: rows.filter( (row) => row.kind === "generation" && row.status === "failed", ).length, }; }, [rows]); const visibleRows = useMemo(() => { if (activeFilter === "audit") { return rows.filter((row) => row.kind === "audit"); } if (activeFilter === "generation") { return rows.filter((row) => row.kind === "generation"); } if (activeFilter === "failed") { return rows.filter( (row) => row.kind === "generation" && row.status === "failed", ); } return rows; }, [activeFilter, rows]); const auditStatusFilters: Array<{ label: string; value: AuditStatusFilter; count: number; }> = [ { label: "Alle", value: "all", count: statusCounts.all }, { label: "Audits", value: "audit", count: statusCounts.audit }, { label: "Pipeline", value: "generation", count: statusCounts.generation }, { label: "Fehlgeschlagen", value: "failed", count: statusCounts.failed }, ]; const handleRetryAudit = async (runId: Id<"agentRuns">) => { setRetryingRunId(runId); try { await retryAuditRun({ runId }); } finally { setRetryingRunId(null); } }; if (dashboardRows === undefined) { return ; } if (rows.length === 0) { return (

Evidence Dossier

Audits

Noch keine Audits

Sobald neue Audits oder laufende Audit-Generierungen angelegt wurden, erscheinen sie hier als kompakte Cards.
); } return (

Evidence Dossier

Audits

Laufende Generierungen, veröffentlichbare Audits und Fehlerzustände als Prüfmappe statt lose Datensatzliste.

{auditStatusFilters.map((filter) => ( ))}
{visibleRows.map((row: AuditDashboardRow) => { const rowTitleId = `audit-row-title-${row.id}`; return (
{row.title}
{row.kind === "audit" ? getStatusLabel(row.status) : getGenerationStatusLabel(row)}

Domain

{row.checkedDomain}

{row.kind === "audit" ? "Seiten" : "Phase"}

{row.kind === "audit" ? ( <>

Slug

{row.kind === "generation" ? `Run ${row.runId}` : row.title}

{row.kind === "generation" && row.errorSummary ? (

{row.errorSummary}

) : null} {row.kind === "generation" ? (
{row.progress.step}/{row.progress.total} · {row.progress.label} {row.progress.percent}%
{row.retry.isRetrying ? (

Versuch {row.retry.attempt}/{row.retry.maxAttempts} läuft

) : null} {row.retry.isRetrying && row.retry.lastRetryReason ? (

{row.retry.lastRetryReason}

) : null}
) : null}
{row.kind === "audit" ? (
); })} {visibleRows.length === 0 ? ( Keine Treffer Für diesen Filter gibt es aktuell keine Audit-Einträge. ) : null}
); }