Intelligence
Artifacts
Browse the repository, read documents, and manage the governance folders. Source, runtime, and infrastructure are read-only.
Repository
assignment-dispatch-button.tsxassignment-status-badge.tsxassignments-panel.tsxcreate-mission-form.tsxgovernance-panels.tsxmission-dispatch.tsxmission-edit.tsxmission-state-actions.tsxmission-status-badge.tsxobjective-edit.tsxobjective-status-badge.tsxobjective-status-select.tsxobjectives-panel.tsxreport-list.tsxwork-orders-panel.tsx
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