'use client'; import { useState, useEffect } from 'react'; import { toast } from 'sonner'; import { Sheet, SheetContent, SheetHeader, SheetTitle, } from '@/components/ui/sheet'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { Badge } from '@/components/ui/badge'; import { Calendar, Loader2 } from 'lucide-react'; interface TaskDetail { id: string; title: string; description?: string | null; status: 'todo' | 'in_progress' | 'done' | 'cancelled'; priority: 'low' | 'medium' | 'high' | 'urgent'; domainId: string; projectId?: string | null; sectionId?: string | null; parentId?: string | null; dueDate?: string | null; completedAt?: string | null; estimatedMinutes?: number | null; trackedMinutes?: number | null; order: number; customFields?: Record | null; createdAt: string; updatedAt: string; subtasks: any[]; tags: { id: string; name: string; color: string | null }[]; dependencies: { id: string; title: string; status: string }[]; dependents: { id: string; title: string; status: string }[]; } interface TaskDetailPanelProps { taskId: string; domainId: string; open: boolean; onOpenChange: (open: boolean) => void; onUpdate: () => void; } export function TaskDetailPanel({ taskId, domainId, open, onOpenChange, onUpdate, }: TaskDetailPanelProps) { const [task, setTask] = useState(null); const [loading, setLoading] = useState(true); const [title, setTitle] = useState(''); const [description, setDescription] = useState(''); const [status, setStatus] = useState<'todo' | 'in_progress' | 'done' | 'cancelled'>('todo'); const [priority, setPriority] = useState<'low' | 'medium' | 'high' | 'urgent'>('medium'); const [dueDate, setDueDate] = useState(''); const [estimatedMinutes, setEstimatedMinutes] = useState(''); const [saving, setSaving] = useState(false); const [deleteOpen, setDeleteOpen] = useState(false); const [deleting, setDeleting] = useState(false); // Fetch task details when panel opens useEffect(() => { if (!open || !taskId || !domainId) return; setLoading(true); fetch(`/api/domains/${domainId}/tasks/${taskId}`) .then((res) => { if (!res.ok) throw new Error('Unable to load task'); return res.json(); }) .then((data: TaskDetail) => { setTask(data); setTitle(data.title); setDescription(data.description || ''); setStatus(data.status); setPriority(data.priority); setDueDate(data.dueDate ? data.dueDate.split('T')[0] : ''); setEstimatedMinutes(data.estimatedMinutes?.toString() || ''); }) .catch((err) => { console.error('Failed to load task:', err); toast.error('Unable to load task details'); }) .finally(() => setLoading(false)); }, [open, taskId, domainId]); async function handleSave() { if (!task || !domainId) return; setSaving(true); try { const body: Record = { title, status, priority, }; if (description !== (task.description || '')) body.description = description || null; if (dueDate !== (task.dueDate ? task.dueDate.split('T')[0] : '')) { body.dueDate = dueDate ? new Date(dueDate).toISOString() : null; } if (estimatedMinutes !== (task.estimatedMinutes?.toString() || '')) { body.estimatedMinutes = estimatedMinutes ? parseInt(estimatedMinutes, 10) : null; } const response = await fetch(`/api/domains/${domainId}/tasks/${task.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }); if (!response.ok) throw new Error('Unable to save task'); onUpdate(); onOpenChange(false); toast.success('Task saved'); } catch (error) { console.error('Failed to update task:', error); toast.error('Unable to save task'); } finally { setSaving(false); } } async function handleDelete() { if (!task || !domainId) return; setDeleting(true); try { const response = await fetch(`/api/domains/${domainId}/tasks/${task.id}`, { method: 'DELETE', }); if (!response.ok) throw new Error('Unable to delete task'); onUpdate(); setDeleteOpen(false); onOpenChange(false); toast.success('Task deleted'); } catch (error) { console.error('Failed to delete task:', error); toast.error('Unable to delete task'); } finally { setDeleting(false); } } return ( Task Details {loading ? (
) : !task ? (

Task not found

) : (
setTitle(e.target.value)} placeholder="Task title" />