'use client'; import { useState, useMemo } from 'react'; import { X, AlertTriangle, Link2, ChevronRight, Clock, User, Tag, Play, Trash2, ArrowRightLeft, CalendarRange } from 'lucide-react'; import { StatusBadge } from './StatusBadge'; import { CategoryChip } from './CategoryChip'; import { useDevTaskStore } from '@/stores/useDevTaskStore'; import { useTaskCategoryStore } from '@/stores/useTaskCategoryStore'; import { useRequirementStore } from '@/stores/useRequirementStore'; import { useMemberStore } from '@/stores/useMemberStore'; import { ALLOWED_TRANSITIONS, DEV_TASK_STATUS_LABEL, DEV_TASK_STATUS_COLOR, formatHours, getEstimateHours, getActualHours, } from '@/lib/dev-task'; import { needsDelayReason } from '@/lib/dev-task-transitions'; import { formatShortTime } from '@/lib/work-hours'; import type { DevTaskStatus } from '@/lib/dev-task'; interface Props { taskId: string; allTaskIds: string[]; onClose: () => void; contextLabel?: string; } export function DevTaskDetailDrawer({ taskId, allTaskIds, onClose, contextLabel }: Props) { const { tasks, changeStatus, setBlocked, deleteTask, updateTask } = useDevTaskStore(); const { categories } = useTaskCategoryStore(); const { requirements } = useRequirementStore(); const { members } = useMemberStore(); const [showTransfer, setShowTransfer] = useState(false); const [transferTo, setTransferTo] = useState(''); const [showDelayInput, setShowDelayInput] = useState(false); const [delayReason, setDelayReason] = useState(''); const task = tasks.find((t) => t.id === taskId); if (!task) return null; const category = categories.find((c) => c.id === task.categoryId); const requirement = requirements.find((r) => r.id === task.requirementId); const allTasks = tasks.filter((t) => allTaskIds.includes(t.id)); const predecessors = (task.predecessorIds || []).map((id) => allTasks.find((t) => t.id === id)).filter(Boolean); const nextStatuses = ALLOWED_TRANSITIONS[task.status] || []; const [blockReason, setBlockReason] = useState(task.blockReason || ''); const [showBlockInput, setShowBlockInput] = useState(false); const estimate = useMemo(() => getEstimateHours(task), [task]); const executorEstimate = typeof task.estimateHours === 'number' && task.estimateHours > 0 ? task.estimateHours : undefined; const aiEstimate = typeof task.aiEstimateHours === 'number' && task.aiEstimateHours > 0 ? task.aiEstimateHours : undefined; const actual = useMemo(() => getActualHours(task), [task]); const overrun = actual > estimate && estimate > 0; const requireDelay = task.status === 'todo' && needsDelayReason(task); const handleTransition = (to: DevTaskStatus) => { if (to === 'in_progress' && requireDelay && !showDelayInput) { setShowDelayInput(true); return; } const opts = to === 'in_progress' && delayReason.trim() ? { delayReason: delayReason.trim() } : undefined; const result = changeStatus(task.id, to, opts); if (!result.ok) { alert(result.message); return; } if (to === 'in_progress') { setShowDelayInput(false); setDelayReason(''); } }; const handleBlock = () => { if (!blockReason.trim()) return; setBlocked(task.id, true, blockReason.trim()); setShowBlockInput(false); }; const handleUnblock = () => { setBlocked(task.id, false); setBlockReason(''); }; return (
{task.description}