import type { Ref } from "react"; import { AlertTriangleIcon, CheckCircle2Icon, CircleSlashIcon, EyeIcon, XCircleIcon, } from "lucide-react"; import type { SessionImportReceipt, SessionUnitReceipt } from "./api"; import { cn } from "../../lib/utils"; import { Badge } from "../ui/badge"; const STATUS_COPY: Record< SessionImportReceipt["status"], { label: string; tone: string; Icon: typeof CheckCircle2Icon } > = { complete: { label: "Complete", tone: "border-emerald-500/40 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300", Icon: CheckCircle2Icon, }, partial: { label: "Partial — some units were not fully read", tone: "border-amber-500/50 bg-amber-500/10 text-amber-800 dark:text-amber-200", Icon: AlertTriangleIcon, }, failed: { label: "Failed", tone: "border-destructive/50 bg-destructive/10 text-destructive", Icon: XCircleIcon, }, nothing_to_do: { label: "Nothing to do — everything is current", tone: "border-border bg-muted/40 text-muted-foreground", Icon: CircleSlashIcon, }, }; /** Unit outcomes worth listing individually (unchanged units are noise). */ const NOTABLE_OUTCOMES = new Set([ "imported", "updated", "skipped_policy", "unsupported", "incomplete", "failed", ]); const OUTCOME_TONE: Partial> = { incomplete: "text-amber-700 dark:text-amber-300", failed: "text-destructive", unsupported: "text-destructive", skipped_policy: "text-amber-700 dark:text-amber-300", }; function Stat({ label, value }: { label: string; value: number | string }) { return (
{label}
{value}
); } interface ImportReceiptProps { receipt: SessionImportReceipt; /** Focus target after a preview/import completes (region is tabIndex -1). */ ref?: Ref; } export function ImportReceipt({ receipt, ref }: ImportReceiptProps) { const status = STATUS_COPY[receipt.status]; const { counts, turns } = receipt; const notable = receipt.units.filter((unit) => NOTABLE_OUTCOMES.has(unit.outcome) ); const destinations = [ ...new Set(receipt.units.flatMap((unit) => unit.collections)), ].sort(); return (
{receipt.dryRun && ( Preview only — nothing was written )} Sources: {receipt.sourceIds.join(", ") || "—"} · index{" "} {receipt.index}

Threads and units

Turns and redaction

Destination collections: {destinations.length > 0 ? ( {destinations.join(", ")} ) : ( none in this run )}

{!receipt.dryRun && (

Lexical search: {receipt.lexical.status} {receipt.lexical.error && ( {receipt.lexical.error} )} {" "} · embedding backlog: {receipt.embedding.backlog ?? "n/a"}

)} {receipt.warnings.length > 0 && (
    {receipt.warnings.map((warning) => (
  • {warning}
  • ))}
)} {notable.length > 0 && (

Unit outcomes

    {notable.map((unit) => (
  • {unit.outcome.replace("_", " ")} {unit.locator}
    {unit.threads} thread{unit.threads === 1 ? "" : "s"} ·{" "} {unit.turns} turn{unit.turns === 1 ? "" : "s"} {unit.collections.length > 0 && ` → ${unit.collections.join(", ")}`} {unit.reason && ` · reason: ${unit.reason}`}
    {unit.warnings?.map((warning) => (
    {warning}
    ))}
  • ))}
{receipt.unitsTruncated && (

More units were processed than are listed here.

)}
)}
); }