import { useActionMutation, useActionQuery, } from "@agent-native/core/client/hooks"; import { useT } from "@agent-native/core/client/i18n"; import { IconAlertTriangle, IconClipboardList, IconDownload, IconLoader2, IconShieldCheck, IconX, } from "@tabler/icons-react"; import { useMemo, useState } from "react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Separator } from "@/components/ui/separator"; import { Sheet, SheetContent } from "@/components/ui/sheet"; import { Skeleton } from "@/components/ui/skeleton"; import { IMAGE_MODELS, VIDEO_MODELS, type ImageModel, type VideoModel, } from "../../shared/api"; const RUN_SOURCES = ["chat", "ui", "a2a"] as const; const RUN_STATUSES = [ "pending", "running", "processing", "completed", "failed", ] as const; type SourceFilter = (typeof RUN_SOURCES)[number] | "all"; type StatusFilter = (typeof RUN_STATUSES)[number] | "all"; type ModelFilter = ImageModel | VideoModel | "all"; interface AuditRun { runId: string; libraryId: string; libraryTitle: string; ownerEmail?: string | null; source: string; callerAppId?: string | null; model: string; mediaType?: string | null; aspectRatio?: string | null; imageSize?: string | null; durationSeconds?: number | null; resolution?: string | null; userPrompt: string; status: string; errorMessage?: string | null; childCount: number; savedCount: number; createdAt: string; completedAt?: string | null; } export default function AuditPage() { const t = useT(); const [dateFrom, setDateFrom] = useState(""); const [dateTo, setDateTo] = useState(""); const [ownerEmail, setOwnerEmail] = useState(""); const [model, setModel] = useState("all"); const [status, setStatus] = useState("all"); const [source, setSource] = useState("all"); const [callerAppId, setCallerAppId] = useState(""); const [promptSearch, setPromptSearch] = useState(""); const [openRunId, setOpenRunId] = useState(null); // First admin check — gates the whole page. const { data: adminCheck, isLoading: adminLoading } = useActionQuery( "is-audit-admin", {}, ) as { data: { allowed?: boolean } | undefined; isLoading: boolean }; const queryArgs = useMemo( () => ({ dateFrom: dateFrom || undefined, dateTo: dateTo || undefined, ownerEmail: ownerEmail.trim() || undefined, model: model === "all" ? undefined : model, status: status === "all" ? undefined : status, source: source === "all" ? undefined : source, callerAppId: callerAppId.trim() || undefined, promptSearch: promptSearch.trim() || undefined, limit: 50, }), [ dateFrom, dateTo, ownerEmail, model, status, source, callerAppId, promptSearch, ], ); const { data, isLoading, error } = useActionQuery( "list-audit-runs", queryArgs as any, { enabled: adminCheck?.allowed === true, } as any, ) as { data: | { count: number; runs: AuditRun[]; scope: { orgScoped: boolean; ownerScoped: boolean }; hasMore: boolean; } | undefined; isLoading: boolean; error: any; }; const exportCsv = useActionMutation("export-audit-csv"); if (adminLoading) { return (
); } if (!adminCheck?.allowed) { return ; } function clearFilters() { setDateFrom(""); setDateTo(""); setOwnerEmail(""); setModel("all"); setStatus("all"); setSource("all"); setCallerAppId(""); setPromptSearch(""); } function handleExport() { const from = dateFrom || isoDaysAgo(90); const to = dateTo || nowIsoDateOnly(); exportCsv.mutate( { ...queryArgs, dateFrom: from, dateTo: to, } as any, { onSuccess: (result: any) => { if (result?.downloadUrl) { window.open(result.downloadUrl, "_blank", "noopener"); } }, }, ); } const runs = data?.runs ?? []; const ownerScoped = data?.scope?.ownerScoped; return (

{t("audit.title")}

{ownerScoped ? ( {t("audit.ownerOnlyFallback")} ) : ( {t("audit.orgWide")} )}

{ownerScoped ? t("audit.ownerScopedDescription") : t("audit.orgScopedDescription")}

setDateFrom(e.target.value)} /> setDateTo(e.target.value)} /> setOwnerEmail(e.target.value)} /> setPromptSearch(e.target.value)} /> setCallerAppId(e.target.value)} />
{hasAnyFilter(queryArgs) && (
)}
{error ? ( ) : isLoading ? ( ) : runs.length === 0 ? ( ) : ( )}
!v && setOpenRunId(null)} > {openRunId && }
); } function FilterField({ label, children, }: { label: string; children: React.ReactNode; }) { return (
{children}
); } function RunTable({ runs, onSelect, }: { runs: AuditRun[]; onSelect: (runId: string) => void; }) { const t = useT(); return (
{runs.map((run) => ( onSelect(run.runId)} className="cursor-pointer hover:bg-muted/30" > ))}
{t("audit.when")} {t("audit.owner")} {t("audit.brandKit")} {t("audit.source")} {t("audit.model")} {t("audit.prompt")} {t("audit.status")} {t("audit.savedTotal")}
{formatRelative(run.createdAt, t)} {run.ownerEmail ?? "—"} {run.libraryTitle} {run.model}
{run.userPrompt}
{run.savedCount} / {run.childCount}
); } function RunDetail({ runId }: { runId: string }) { const t = useT(); const { data, isLoading, error } = useActionQuery("get-audit-run", { runId, } as any) as { data: any; isLoading: boolean; error: any; }; if (isLoading) { return (
); } if (error) return ; if (!data) return null; const run = data.run; const references: any[] = data.references ?? []; const children: any[] = data.children ?? []; const parentRun = data.parentRun; return (

{run.libraryTitle}

{t("audit.runSummary", { id: run.runId.slice(0, 12), time: formatRelative(run.createdAt, t), })}

{run.model} {run.aspectRatio && ( {run.aspectRatio} )} {run.mediaType === "video" ? ( {t("audit.videoBadge", { duration: run.durationSeconds || "?", resolution: run.resolution || run.imageSize, })} ) : ( run.imageSize && {run.imageSize} )} {run.ownerEmail && ( {t("audit.byOwner", { email: run.ownerEmail })} )}

{run.userPrompt}

{run.compiledPrompt && (
            {run.compiledPrompt}
          
)} {run.errorMessage && (
{t("audit.failed")}

{run.errorMessage}

)} {parentRun && (
{parentRun.prompt}
{parentRun.model} · {formatRelative(parentRun.createdAt, t)}
)} {references.length > 0 && (
{references.map((ref) => ( ))}
)}
{children.length === 0 ? (

{t("audit.noChildrenProduced")}

) : (
{children.map((c) => ( ))}
)}
); } function AssetThumb({ asset, label }: { asset: any; label?: string }) { return (
{asset.mediaType === "video" || asset.mimeType?.startsWith("video/") ? (
{label && (
{label}
)}
); } function Section({ title, children, }: { title: string; children: React.ReactNode; }) { return (

{title}

{children}
); } function SourceBadge({ source, callerAppId, }: { source: string; callerAppId?: string | null; }) { const t = useT(); if (source === "a2a") { return ( {t("audit.viaCaller", { caller: callerAppId || "a2a" })} ); } return ( {source} ); } function StatusPill({ status }: { status: string }) { const variant: "secondary" | "outline" | "destructive" = status === "completed" ? "secondary" : status === "failed" ? "destructive" : "outline"; return ( {status} ); } function SkeletonRows() { return (
{Array.from({ length: 6 }).map((_, i) => ( ))}
); } function EmptyState() { const t = useT(); return (

{t("audit.noRunsMatch")}

{t("audit.noRunsDescription")}

); } function ErrorBlock({ error }: { error: any }) { const t = useT(); return (
{t("audit.loadFailed")}

{error?.message || t("audit.unknownError")}

); } function ForbiddenPage() { const t = useT(); return (

{t("audit.adminOnlyTitle")}

{t("audit.adminOnlyDescription")}

); } function nowIsoDateOnly(): string { return new Date().toISOString().slice(0, 10); } function isoDaysAgo(days: number): string { const t = Date.now() - days * 24 * 60 * 60 * 1000; return new Date(t).toISOString().slice(0, 10); } function formatRelative(iso: string, t: ReturnType): string { const ts = Date.parse(iso); if (Number.isNaN(ts)) return iso; const diffMs = Date.now() - ts; const sec = Math.round(diffMs / 1000); if (sec < 60) return t("audit.secondsAgo", { count: sec }); const min = Math.round(sec / 60); if (min < 60) return t("audit.minutesAgo", { count: min }); const hr = Math.round(min / 60); if (hr < 24) return t("audit.hoursAgo", { count: hr }); const day = Math.round(hr / 24); if (day < 30) return t("audit.daysAgo", { count: day }); return iso.slice(0, 10); } function hasAnyFilter(args: Record): boolean { return Object.entries(args).some( ([k, v]) => k !== "limit" && v !== undefined && v !== "", ); }