import { useEffect, useMemo, useState } from 'react' import api from '@/services/api' import type { Milestone, Project, Task } from '@/types' const TASK_TYPES = [ { value: 'story', label: 'Story', subtypes: ['improvement', 'refactor'] }, // P9.6: 'feature' removed — must come from propose accept { value: 'issue', label: 'Issue', subtypes: ['infrastructure', 'performance', 'regression', 'security', 'user_experience', 'defect'] }, { value: 'task', label: 'Task', subtypes: ['defect'] }, { value: 'test', label: 'Test', subtypes: ['regression', 'security', 'smoke', 'stress'] }, { value: 'maintenance', label: 'Maintenance', subtypes: ['deploy'] }, // P9.6: 'release' removed — controlled via milestone flow { value: 'research', label: 'Research', subtypes: [] }, { value: 'review', label: 'Review', subtypes: ['code_review', 'decision_review', 'function_review'] }, { value: 'resolution', label: 'Resolution', subtypes: [] }, ] type Props = { isOpen: boolean onClose: () => void onCreated?: (task: Task) => void | Promise onSaved?: (task: Task) => void | Promise initialProjectId?: number initialMilestoneId?: number lockProject?: boolean lockMilestone?: boolean task?: Task | null } type FormState = { title: string description: string project_id: number milestone_id: number task_type: string task_subtype: string priority: string tags: string reporter_id: number } const makeInitialForm = (projectId = 0, milestoneId = 0): FormState => ({ title: '', description: '', project_id: projectId, milestone_id: milestoneId, task_type: 'task', task_subtype: '', priority: 'medium', tags: '', reporter_id: 1, }) export default function CreateTaskModal({ isOpen, onClose, onCreated, onSaved, initialProjectId, initialMilestoneId, lockProject = false, lockMilestone = false, task, }: Props) { const [projects, setProjects] = useState([]) const [milestones, setMilestones] = useState([]) const [saving, setSaving] = useState(false) const [form, setForm] = useState(makeInitialForm(initialProjectId, initialMilestoneId)) const isEdit = Boolean(task) const currentType = useMemo( () => TASK_TYPES.find((t) => t.value === form.task_type) || TASK_TYPES[2], [form.task_type] ) const subtypes = currentType.subtypes || [] const loadMilestones = async (projectId: number, preferredMilestoneId?: number) => { if (!projectId) { setMilestones([]) setForm((f) => ({ ...f, milestone_id: 0 })) return } const { data } = await api.get(`/milestones?project_id=${projectId}`) setMilestones(data) const hasPreferred = preferredMilestoneId && data.some((m) => m.id === preferredMilestoneId) const nextMilestoneId = hasPreferred ? preferredMilestoneId! : (data[0]?.id || 0) setForm((f) => ({ ...f, milestone_id: nextMilestoneId })) } useEffect(() => { if (!isOpen) return const init = async () => { const { data } = await api.get('/projects') setProjects(data) const chosenProjectId = task?.project_id || initialProjectId || data[0]?.id || 0 const chosenMilestoneId = task?.milestone_id || initialMilestoneId || 0 setForm(task ? { title: task.title, description: task.description || '', project_id: task.project_id, milestone_id: task.milestone_id || 0, task_type: task.task_type, task_subtype: task.task_subtype || '', priority: task.priority, tags: task.tags || '', reporter_id: task.reporter_id, } : makeInitialForm(chosenProjectId, chosenMilestoneId)) await loadMilestones(chosenProjectId, chosenMilestoneId) } init().catch(console.error) }, [isOpen, initialProjectId, initialMilestoneId, task]) const handleProjectChange = async (projectId: number) => { setForm((f) => ({ ...f, project_id: projectId, milestone_id: 0 })) await loadMilestones(projectId) } const handleTypeChange = (taskType: string) => { setForm((f) => ({ ...f, task_type: taskType, task_subtype: '' })) } const submit = async (e: React.FormEvent) => { e.preventDefault() if (!form.milestone_id) { alert('Please select a milestone') return } const payload: any = { ...form, tags: form.tags || null } if (!form.task_subtype) delete payload.task_subtype setSaving(true) try { const { data } = isEdit ? await api.patch(`/tasks/${task!.id}`, payload) : await api.post('/tasks', payload) await onCreated?.(data) await onSaved?.(data) onClose() } finally { setSaving(false) } } if (!isOpen) return null return (
e.stopPropagation()}>

{isEdit ? 'Edit Task' : 'Create Task'}