"use client"; import Link from "next/link"; import { useMemo, useState } from "react"; import { Badge, Button, EmptyState, RelativeTime, ScopeBadge, SemanticsLabel, SkeletonBlock, SkeletonRows, Tab, TabList, TabPanel, Tabs, Table, TBody, Td, Th, THead, Tr } from "@/components/ui"; import { cn } from "@/components/ui/cn"; import { DASH, fmtInt, fmtMw, fmtUsd } from "@/lib/format"; import { entityHref, routes } from "@/lib/routes"; import type { QualityFlagDTO, QualityOverview } from "@/lib/admin-api"; import { ErrorNote, Input, JsonView, Mono, Pager, Panel, RefreshButton, Segmented, Select, Stat } from "./primitives"; import { AdminPageHeader } from "./shell"; import { useToast } from "./toast"; import { useAdminMutation, useAdminQuery } from "./use-admin-query"; import { useUrlState } from "./use-url-state"; /* ------------------------------------------------------------------------------------------ /admin/quality — QualityOverview counters, largest published values (with scope / semantics so a portfolio figure is never read as a site), the review queue (resolve / dismiss) and system alerts. ------------------------------------------------------------------------------------------ */ const FLAG_STATUSES = ["open", "resolved", "dismissed", "all"] as const; type FlagStatus = (typeof FLAG_STATUSES)[number]; const PER_PAGE = 30; const SEV_TONE = { info: "neutral", warn: "warn", critical: "danger" } as const; function SeverityBadge({ s }: { s: QualityFlagDTO["severity"] }) { return ( {s} ); } function codeLabel(code: string, labels: Map): string { return labels.get(code) ?? code.replace(/_/g, " "); } function LargestTable({ rows, href, value, scope, semantics, unit }: { rows: T[]; href: (r: T) => string; value: (r: T) => string; scope?: (r: T) => string | null | undefined; semantics?: (r: T) => string | null | undefined; unit: string }) { if (!rows.length) return ; return ( {scope && } {semantics && } {rows.map((r) => ( {scope && ( )} {semantics && ( )} ))}
RecordScopeSemantics{unit}
{r.name}
{r.id}
{scope(r) ? : {DASH}} {semantics(r) ? : {DASH}} {value(r)}
); } export function QualityDashboard() { const q = useAdminQuery("quality", { refreshMs: 120_000, timeoutMs: 30_000 }); const [params, setParams] = useUrlState(); const status = (FLAG_STATUSES.includes(params.status as FlagStatus) ? params.status : "open") as FlagStatus; const code = params.code ?? ""; const entityType = params.entity_type ?? ""; const minPriority = params.min_priority ?? ""; const page = Math.max(1, Number(params.page ?? 1) || 1); const flags = useAdminQuery("quality/flags", { query: { status, code: code || undefined, entity_type: entityType || undefined, min_priority: minPriority || undefined, page, per_page: PER_PAGE }, timeoutMs: 30_000 }); const { busy, run } = useAdminMutation(); const toast = useToast(); const [gone, setGone] = useState>({}); const d = q.data; const methodology = typeof q.meta?.methodology === "string" ? q.meta.methodology : null; const labels = useMemo(() => new Map((d?.byCode ?? []).map((c) => [c.code, c.label])), [d]); const codes = useMemo(() => (d?.byCode ?? []).map((c) => c.code).sort(), [d]); const queue = useMemo(() => [...(flags.data ?? [])].sort((a, b) => b.priority - a.priority || (a.createdAt < b.createdAt ? 1 : -1)), [flags.data]); const total = Number(flags.meta?.total ?? queue.length); const decide = async (f: QualityFlagDTO, action: "resolve" | "dismiss") => { let text: string | null = null; if (action === "resolve") { text = window.prompt(`Resolution for “${f.message.slice(0, 80)}” — what was fixed or verified?`, ""); if (text === null) return; if (!text.trim()) return toast({ tone: "warn", title: "A resolution note is required" }); } else { text = window.prompt("Dismiss reason (optional)", "") ?? ""; } setGone((g) => ({ ...g, [f.id]: action === "resolve" ? "resolved" : "dismissed" })); const r = await run(`${action}:${f.id}`, `quality/flags/${encodeURIComponent(f.id)}/${action}`, { body: action === "resolve" ? { resolution: text.trim() } : text.trim() ? { reason: text.trim() } : {} }); if (r.ok) { toast({ tone: "ok", title: action === "resolve" ? "Flag resolved" : "Flag dismissed", description: f.message.slice(0, 100) }); setTimeout(() => { flags.mutate((prev) => prev?.filter((x) => x.id !== f.id) ?? prev); q.mutate((prev) => (prev ? { ...prev, openFlags: Math.max(0, prev.openFlags - 1) } : prev)); }, 500); } else { setGone((g) => { const n = { ...g }; delete n[f.id]; return n; }); toast({ tone: "danger", title: `Could not ${action}`, description: r.error }); } }; return (
void q.refresh()} busy={q.refreshing} updatedAt={q.updatedAt} />} /> void q.refresh()} className="mb-3" /> {!d ? ( ) : (
0 ? "warn" : "ok"} note={Object.entries(d.bySeverity).map(([k, v]) => `${fmtInt(v)} ${k}`).join(" · ") || undefined} /> pending matches} /> 0 ? "warn" : undefined} note="single-site > 1 000 MW, density, 5× jumps" /> 0 ? "warn" : undefined} note="> $50 B on one site, deal/capex as project" /> 0 ? "danger" : undefined} note={review projects} />
)} {methodology &&

{methodology}

}
{!d ? (
) : d.byCode.length === 0 ? ( ) : (
    {d.byCode.map((c) => { const max = d.byCode[0]?.count ?? 1; return (
  • ); })}
)}
{!d ? (
) : ( Operational facility MW Construction MW Project MW Investment Operator pipeline Metro pipeline routes.facility(r.slug)} value={(r) => fmtMw(r.mw)} scope={(r) => r.scope} semantics={(r) => r.semantics} unit="MW" /> routes.facility(r.slug)} value={(r) => fmtMw(r.mw)} unit="MW" /> routes.project(r.slug)} value={(r) => `≈ ${fmtMw(r.mw)}`} scope={(r) => r.scope} unit="Planned MW" /> routes.project(r.slug)} value={(r) => fmtUsd(r.investmentUsd)} scope={(r) => r.scope} unit="USD" /> routes.operator(r.slug)} value={(r) => `≈ ${fmtMw(r.mw)}`} unit="Pipeline MW" /> routes.market(r.slug)} value={(r) => `≈ ${fmtMw(r.mw)}`} unit="Pipeline MW" /> )}
void flags.refresh()} busy={flags.refreshing} updatedAt={flags.updatedAt} />} >
setParams({ status: v === "open" ? null : v, page: null })} options={FLAG_STATUSES.map((s) => ({ value: s, label: s[0]!.toUpperCase() + s.slice(1), count: s === status ? total : null }))} />
setParams({ min_priority: e.target.value || null, page: null })} placeholder="min priority" className="w-[130px]" mono aria-label="Minimum priority" />
void flags.refresh()} className="m-3" /> {flags.loading && !flags.data ? (
) : queue.length === 0 ? ( ) : (
    {queue.map((f) => { const decided = gone[f.id]; const href = f.entity ? entityHref(f.entityType, f.entity.slug, f.entity.name) : null; return (
  • {f.priority}
    {f.code} {f.entityType} {f.field && {f.field}} {f.claimId && ( {f.claimId} )}
    {href && f.entity ? ( {f.entity.name} ) : ( {f.entityId} )}

    {f.message}

    {f.status !== "open" && (

    {f.status} {f.resolvedBy && `by ${f.resolvedBy}`} {f.resolvedAt && } {f.resolution && <> — {f.resolution}}

    )} {f.details && Object.keys(f.details).length > 0 && (
    )}
    {f.status === "open" && !decided ? ( <> ) : ( {decided ?? f.status} )}
  • ); })}
)}
setParams({ page: p === 1 ? null : p })} />
{!d ? (
) : d.alerts.length === 0 ? ( ) : (
    {d.alerts.map((a) => (
  • {a.level} {a.component} {a.message}
  • ))}
)}
); }