Intelligence

Artifacts

Browse the repository, read documents, and manage the governance folders. Source, runtime, and infrastructure are read-only.

Repository
.gitignoreDockerfilenext-env.d.tsnext.config.mjspackage-lock.jsonpackage.jsonpostcss.config.mjsREADME.mdtailwind.config.tstsconfig.jsontsconfig.tsbuildinfo
README.md
CONSTITUTION_COMPLIANCE_AUDIT_V1.mdREADME.md
repositories/aaf-holdings/hq01/components/assets/asset-register-row.tsx
3.1 KB
"use client";

import { useState } from "react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import { Box, Loader2, Plus, CheckCircle2, ArrowRight } from "lucide-react";
import { Button } from "@/components/ui/button";
import type { AssetType } from "@/lib/assets/types";

const TYPES: AssetType[] = [
  "Code", "Document", "PDF", "Image", "Video", "Research",
  "Prompt", "Template", "Architecture", "Configuration", "Policy", "Other",
];

/**
 * A produced-artifact row on the report detail: register it into the Asset
 * Ledger, or link to the asset if already registered.
 */
export function AssetRegisterRow({
  missionId,
  assignmentId,
  reportId,
  relativePath,
  registeredAssetId,
}: {
  missionId: string;
  assignmentId: string;
  reportId: string;
  relativePath: string;
  registeredAssetId: string | null;
}) {
  const router = useRouter();
  const [type, setType] = useState<string>("");
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  async function register() {
    setBusy(true);
    setError(null);
    try {
      const res = await fetch("/api/assets/register", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          mission_id: missionId,
          assignment_id: assignmentId,
          report_id: reportId,
          relative_path: relativePath,
          type: type || undefined,
        }),
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok) throw new Error(data.error ?? "Registration failed.");
      router.refresh();
    } catch (err) {
      setError(err instanceof Error ? err.message : "Registration failed.");
    } finally {
      setBusy(false);
    }
  }

  return (
    <li className="flex flex-wrap items-center gap-2 py-2">
      <Box className="h-4 w-4 shrink-0 text-muted-foreground" />
      <span className="min-w-0 flex-1 truncate font-mono text-[12px] text-foreground">
        {relativePath}
      </span>
      {registeredAssetId ? (
        <Link
          href={`/assets/${registeredAssetId}`}
          className="inline-flex items-center gap-1 text-[12px] font-medium text-emerald-700 hover:underline"
        >
          <CheckCircle2 className="h-3.5 w-3.5" /> {registeredAssetId.replace("ASSET-", "A-")}
          <ArrowRight className="h-3 w-3" />
        </Link>
      ) : (
        <>
          <select
            value={type}
            onChange={(e) => setType(e.target.value)}
            className="h-7 rounded-md border border-input bg-background px-2 text-[12px]"
          >
            <option value="">Auto-detect</option>
            {TYPES.map((t) => (
              <option key={t} value={t}>
                {t}
              </option>
            ))}
          </select>
          <Button type="button" size="sm" variant="outline" onClick={register} disabled={busy}>
            {busy ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Plus className="h-3.5 w-3.5" />}
            Register
          </Button>
        </>
      )}
      {error && <span className="w-full text-[11px] text-destructive">{error}</span>}
    </li>
  );
}

root · /srv/aaf