"use client"; import { useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; import { CardDescription, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Select } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; import type { FleetAgent, TaskPriority, TaskTemplate } from "@/lib/types"; const PRIORITIES: TaskPriority[] = ["Low", "Medium", "High", "Critical"]; export function TaskIntakeModal({ agents, templates, open, onClose, onCreated, }: { agents: FleetAgent[]; templates: TaskTemplate[]; open: boolean; onClose: () => void; onCreated: () => Promise; }) { const [formState, setFormState] = useState({ templateKey: "", title: "", description: "", assignee: "", priority: "Medium" as TaskPriority, tags: "", repoSlug: "", baseBranch: "main", preferredAgent: "codex", reasoningEffort: "high", modelHint: "", }); const selectedTemplate = templates.find((template) => template.key === formState.templateKey) || null; const selectedAgent = agents.find((agent) => agent.assignmentKey === formState.assignee) || null; useEffect(() => { if (!open) { return; } function onKeyDown(event: KeyboardEvent) { if (event.key === "Escape") { onClose(); } } window.addEventListener("keydown", onKeyDown); document.body.style.overflow = "hidden"; return () => { window.removeEventListener("keydown", onKeyDown); document.body.style.overflow = ""; }; }, [onClose, open]); function applyTemplate(templateKey: string) { const template = templates.find((entry) => entry.key === templateKey) || null; if (!template) { setFormState((current) => ({ ...current, templateKey })); return; } setFormState((current) => ({ ...current, templateKey, title: current.title || template.title, priority: template.defaults.priority, tags: template.tags.join(", "), repoSlug: template.defaults.repoSlug || current.repoSlug, baseBranch: template.defaults.baseBranch || current.baseBranch, preferredAgent: template.defaults.preferredAgent || current.preferredAgent, reasoningEffort: template.defaults.reasoningEffort || current.reasoningEffort, })); } async function createTask(event: React.FormEvent) { event.preventDefault(); const tags = formState.tags .split(",") .map((tag) => tag.trim()) .filter(Boolean); await fetch("/api/tasks", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title: formState.title, description: formState.description, assignee: formState.assignee, priority: formState.priority, tags, template_key: formState.templateKey || null, repo_slug: formState.repoSlug || null, base_branch: formState.baseBranch || null, preferred_agent: formState.preferredAgent || null, reasoning_effort: formState.reasoningEffort || null, model_hint: formState.modelHint || null, family: selectedAgent?.family || selectedTemplate?.family || null, target_host: selectedAgent?.host || selectedTemplate?.defaults.targetHost || "", target_channel: selectedAgent?.channels[0]?.value || selectedTemplate?.defaults.targetChannel || "", dispatch_method: selectedAgent?.defaultDispatchMethod || selectedTemplate?.defaults.dispatchMethod || "manual", }), }); setFormState({ templateKey: "", title: "", description: "", assignee: "", priority: "Medium", tags: "", repoSlug: "", baseBranch: "main", preferredAgent: "codex", reasoningEffort: "high", modelHint: "", }); await onCreated(); onClose(); } if (!open) { return null; } return (
New Task Create a typed task and route it to the right execution family without leaving the board.
setFormState((current) => ({ ...current, title: event.target.value }))} /> setFormState((current) => ({ ...current, repoSlug: event.target.value }))} /> setFormState((current) => ({ ...current, baseBranch: event.target.value }))} /> setFormState((current) => ({ ...current, preferredAgent: event.target.value }))} /> setFormState((current) => ({ ...current, reasoningEffort: event.target.value }))} /> setFormState((current) => ({ ...current, tags: event.target.value }))} /> setFormState((current) => ({ ...current, modelHint: event.target.value }))} />