'use client' import { useState, useTransition } from 'react' import { useRouter } from 'next/navigation' import { DndContext, DragEndEvent, DragOverlay, DragStartEvent, KeyboardSensor, PointerSensor, useSensor, useSensors, closestCenter, } from '@dnd-kit/core' import { SortableContext, useSortable, rectSortingStrategy, arrayMove, sortableKeyboardCoordinates, } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' import { toast } from 'sonner' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { PanelNavBar } from '@/components/shared/panel-nav-bar' import { DemoTooltip } from '@/components/shared/demo-tooltip' import { useSelectionStore } from '@/stores/selection-store' import { useBacklogStore, type BacklogTask } from '@/stores/backlog-store' import { reorderTasksAction } from '@/actions/tasks' import { BacklogCard } from './backlog-card' import { debugProps } from '@/lib/debug' import { EmptyPanel } from './empty-panel' import { cn } from '@/lib/utils' const STATUS_COLORS: Record = { TO_DO: 'bg-status-todo/15 text-status-todo border-status-todo/30', IN_PROGRESS: 'bg-status-in-progress/15 text-status-in-progress border-status-in-progress/30', REVIEW: 'bg-status-review/15 text-status-review border-status-review/30', DONE: 'bg-status-done/15 text-status-done border-status-done/30', } const STATUS_LABELS: Record = { TO_DO: 'To Do', IN_PROGRESS: 'Bezig', REVIEW: 'Review', DONE: 'Klaar', } function SortableTaskCard({ task, isDemo, onClick, }: { task: BacklogTask isDemo: boolean onClick: () => void }) { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: task.id }) const style = { transform: CSS.Transform.toString(transform), transition, } return ( {STATUS_LABELS[task.status] ?? task.status} } /> ) } interface TaskPanelProps { productId: string isDemo: boolean closePath: string } export function TaskPanel({ isDemo, closePath }: TaskPanelProps) { const router = useRouter() const [, startTransition] = useTransition() const selectedStoryId = useSelectionStore((s) => s.selectedStoryId) const tasksByStory = useBacklogStore((s) => s.tasksByStory) const [activeDragId, setActiveDragId] = useState(null) const [localOrder, setLocalOrder] = useState(null) const rawTasks = selectedStoryId ? (tasksByStory[selectedStoryId] ?? []) : null // Merge local order with rawTasks for optimistic reorder const tasks: BacklogTask[] | null = rawTasks === null ? null : localOrder ? localOrder.map((id) => rawTasks.find((t) => t.id === id)).filter(Boolean) as BacklogTask[] : rawTasks const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 5 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }), ) function handleDragStart(event: DragStartEvent) { setActiveDragId(event.active.id as string) } function handleDragEnd(event: DragEndEvent) { setActiveDragId(null) if (!selectedStoryId || !tasks) return const { active, over } = event if (!over || active.id === over.id) return const ids = tasks.map((t) => t.id) const oldIndex = ids.indexOf(active.id as string) const newIndex = ids.indexOf(over.id as string) if (oldIndex === -1 || newIndex === -1) return const newOrder = arrayMove(ids, oldIndex, newIndex) setLocalOrder(newOrder) startTransition(async () => { const result = await reorderTasksAction(selectedStoryId, newOrder) if (result?.error) { setLocalOrder(null) toast.error(result.error) } }) } const navActions = ( ) const dp = debugProps('task-panel', 'TaskPanel', 'components/backlog/task-panel.tsx') if (tasks === null) { return (
) } if (tasks.length === 0) { return (
router.push(`${closePath}?newTask=1&storyId=${selectedStoryId}`), disabled: isDemo, }} />
) } const activeTask = activeDragId ? tasks.find((t) => t.id === activeDragId) : null return (
t.id)} strategy={rectSortingStrategy}>
{tasks.map((task) => ( router.push(`${closePath}?editTask=${task.id}`)} /> ))}
{activeTask && ( )}
) }