"use client"; import { useState } from "react"; import { useCreateTask } from "@/hooks/use-tasks"; import { useProducts } from "@/hooks/use-products"; import { Team, Complexity, TaskStatus, TaskNature, TaskType } from "@/types"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogDescription, } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; import { Plus, ChevronDown, ChevronRight, GitBranch } from "lucide-react"; import { toast } from "sonner"; import { AcceptanceCriteriaEditor } from "./acceptance-criteria-editor"; import { MarkdownEditor } from "./markdown-editor"; import { DependencySelector } from "./dependency-selector"; import { TaskSelector } from "./task-selector"; import { AgentSelector } from "@/components/agents/agent-selector"; import { ProjectSelector } from "@/components/projects/project-selector"; // Priority options (0=P0 highest, 3=P3 lowest) const PRIORITY_OPTIONS = [ { value: 0, label: "P0 - Highest" }, { value: 1, label: "P1 - High" }, { value: 2, label: "P2 - Medium" }, { value: 3, label: "P3 - Low" }, ]; // Complexity options const COMPLEXITY_OPTIONS = [ { value: Complexity.LOW, label: "Low" }, { value: Complexity.MEDIUM, label: "Medium" }, { value: Complexity.HIGH, label: "High" }, ]; // Nature options (technical vs non-technical) const NATURE_OPTIONS = [ { value: TaskNature.TECHNICAL, label: "Technical" }, { value: TaskNature.NON_TECHNICAL, label: "Non-Technical" }, ]; // Task type options const TASK_TYPE_OPTIONS = [ { value: TaskType.CODE, label: "Code" }, { value: TaskType.DOCUMENTATION, label: "Documentation" }, { value: TaskType.RESEARCH, label: "Research" }, { value: TaskType.PLANNING, label: "Planning" }, { value: TaskType.DESIGN, label: "Design" }, { value: TaskType.ADMINISTRATIVE, label: "Administrative" }, ]; // Initial status options (only PENDING and BACKLOG for creation) const STATUS_OPTIONS = [ { value: TaskStatus.PENDING, label: "Pending (Ready for work)" }, { value: TaskStatus.BACKLOG, label: "Backlog (PM setup)" }, ]; interface FormErrors { title?: string; description?: string; acceptance_criteria?: string; project_id?: string; } export function CreateTaskDialog() { const [open, setOpen] = useState(false); const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); const [team, setTeam] = useState(Team.BACKEND); const [priority, setPriority] = useState(2); const [complexity, setComplexity] = useState(Complexity.MEDIUM); const [nature, setNature] = useState(TaskNature.TECHNICAL); const [status, setStatus] = useState(TaskStatus.PENDING); const [acceptanceCriteria, setAcceptanceCriteria] = useState([]); const [dependencyIds, setDependencyIds] = useState([]); const [parentTaskId, setParentTaskId] = useState(null); const [assignedTo, setAssignedTo] = useState(null); const [taskType, setTaskType] = useState(TaskType.CODE); const [projectId, setProjectId] = useState(""); const [productId, setProductId] = useState(""); const [advancedOpen, setAdvancedOpen] = useState(false); const [errors, setErrors] = useState({}); const createTask = useCreateTask(); const { data: products = [] } = useProducts(); const validate = (): boolean => { const newErrors: FormErrors = {}; if (!title.trim() || title.trim().length < 5) { newErrors.title = "Title must be at least 5 characters"; } if (title.trim().length > 200) { newErrors.title = "Title must be less than 200 characters"; } if (!description.trim() || description.trim().length < 20) { newErrors.description = "Description must be at least 20 characters"; } if (acceptanceCriteria.length === 0) { newErrors.acceptance_criteria = "At least one acceptance criterion is required"; } if (!projectId && !productId) { newErrors.project_id = "Pick a Project (the repo) or a Product (cell→project map for a fan-out task)"; } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!validate()) { return; } try { await createTask.mutateAsync({ title: title.trim(), description: description.trim(), team, priority, status, acceptance_criteria: acceptanceCriteria.map((c) => c.trim()).filter(Boolean), estimated_complexity: complexity, nature, task_type: taskType, ...(projectId && { project_id: projectId }), ...(productId && { product_id: productId }), ...(dependencyIds.length > 0 && { dependency_ids: dependencyIds }), ...(parentTaskId && { parent_task_id: parentTaskId }), ...(assignedTo && { assigned_to: assignedTo }), }); toast.success("Task created successfully"); setOpen(false); resetForm(); } catch { toast.error("Failed to create task"); } }; const resetForm = () => { setTitle(""); setDescription(""); setTeam(Team.BACKEND); setPriority(2); setComplexity(Complexity.MEDIUM); setNature(TaskNature.TECHNICAL); setStatus(TaskStatus.PENDING); setAcceptanceCriteria([]); setDependencyIds([]); setParentTaskId(null); setAssignedTo(null); setTaskType(TaskType.CODE); setProjectId(""); setProductId(""); setAdvancedOpen(false); setErrors({}); }; return ( Create New Task Create a new task with clear requirements and acceptance criteria.
{/* Title */}
setTitle(e.target.value)} placeholder="Enter task title (5-200 characters)" className={errors.title ? "border-destructive" : ""} /> {errors.title &&

{errors.title}

}
{/* Description */} {/* Team, Priority, Complexity, Status, Nature */}
{/* Acceptance Criteria */} {/* Dependencies */} {/* Advanced Options */} {/* Parent Task */}

Make this task a subtask of an existing task

{/* Assign To */}

Leave unassigned to let the orchestrator route automatically, or manually assign to a specific agent

{/* Git Configuration Section */}
Git & Work Configuration
{/* Task Type */}

Type of work: code, documentation, research, etc.

{/* Project — required UNLESS a Product is picked (fan-out task) */}
setProjectId(value || "")} placeholder="Select project..." /> {errors.project_id && (

{errors.project_id}

)}

The repo this task targets. Optional if you pick a Product below — a fan-out task routes each cell's subtask via the Product instead.

{/* Product (optional) — drives per-cell project routing of subtasks */}

Optional. When set, delegated subtasks route to each cell's mapped project (manage these in Products).

{/* Actions */}
); }