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/workers/worker-instantiate.tsx
2.6 KB
"use client";

import { useState } from "react";
import { useRouter } from "next/navigation";
import { HardHat, Loader2, AlertTriangle } from "lucide-react";
import { Button } from "@/components/ui/button";
import type { AssignmentStatus } from "@/lib/missions/types";

export interface TemplateOption {
  id: string;
  name: string;
}

/**
 * Instantiate a worker from a template for an assignment. The worker runs
 * through the existing engine and terminates; no worker persists.
 */
export function WorkerInstantiate({
  missionId,
  aid,
  templates,
  status,
}: {
  missionId: string;
  aid: string;
  templates: TemplateOption[];
  status: AssignmentStatus;
}) {
  const router = useRouter();
  const [templateId, setTemplateId] = useState(templates[0]?.id ?? "");
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const canInstantiate = status === "Pending" || status === "Failed";

  async function instantiate() {
    setBusy(true);
    setError(null);
    try {
      const res = await fetch(
        `/api/missions/${missionId}/assignments/${aid}/workers`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ templateId }),
        },
      );
      const data = await res.json().catch(() => ({}));
      if (!res.ok) throw new Error(data.error ?? "Instantiation failed.");
      router.refresh();
    } catch (err) {
      setError(err instanceof Error ? err.message : "Instantiation failed.");
      setBusy(false);
    }
  }

  if (!canInstantiate) {
    return (
      <span className="text-[12px] text-muted-foreground">
        Worker already dispatched ({status.toLowerCase()}).
      </span>
    );
  }

  return (
    <div className="flex flex-col items-end gap-1">
      <div className="flex items-center gap-2">
        <select
          value={templateId}
          onChange={(e) => setTemplateId(e.target.value)}
          className="h-9 rounded-md border border-input bg-background px-2 text-sm"
        >
          {templates.map((t) => (
            <option key={t.id} value={t.id}>
              {t.name}
            </option>
          ))}
        </select>
        <Button type="button" size="sm" onClick={instantiate} disabled={busy || !templateId}>
          {busy ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <HardHat className="h-3.5 w-3.5" />}
          Instantiate Worker
        </Button>
      </div>
      {error && (
        <span className="flex items-center gap-1 text-[11px] text-destructive">
          <AlertTriangle className="h-3 w-3" /> {error}
        </span>
      )}
    </div>
  );
}

root · /srv/aaf