import { useState, useEffect } from 'react'; import { DndContext, DragOverlay, PointerSensor, useSensor, useSensors, } from '@dnd-kit/core'; import type { DragEndEvent, DragOverEvent, DragStartEvent } from '@dnd-kit/core'; import { Column } from './components/Column'; import { TaskCard } from './components/TaskCard'; import type { Task, Column as ColumnType, ColumnId } from './types'; import './App.css'; const defaultColumns: ColumnType[] = [ { id: 'backlog', title: '📋 Backlog', tasks: [] }, { id: 'todo', title: '📝 To Do', tasks: [] }, { id: 'doing', title: '🔨 Doing', tasks: [] }, { id: 'done', title: '✅ Done', tasks: [] }, ]; function App() { const [columns, setColumns] = useState(() => { const saved = localStorage.getItem('kanban-data'); return saved ? JSON.parse(saved) : defaultColumns; }); const [activeTask, setActiveTask] = useState(null); const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 5 } }) ); useEffect(() => { localStorage.setItem('kanban-data', JSON.stringify(columns)); }, [columns]); const findTask = (id: string): { task: Task; columnId: string } | null => { for (const column of columns) { const task = column.tasks.find((t) => t.id === id); if (task) return { task, columnId: column.id }; } return null; }; const handleDragStart = (event: DragStartEvent) => { const found = findTask(event.active.id as string); if (found) setActiveTask(found.task); }; const handleDragOver = (event: DragOverEvent) => { const { active, over } = event; if (!over) return; const activeId = active.id as string; const overId = over.id as string; const activeData = findTask(activeId); if (!activeData) return; const overColumn = columns.find((c) => c.id === overId); const overTask = findTask(overId); const targetColumnId = overColumn?.id || overTask?.columnId; if (!targetColumnId || targetColumnId === activeData.columnId) return; setColumns((cols) => { const newCols = cols.map((c) => ({ ...c, tasks: [...c.tasks] })); const sourceCol = newCols.find((c) => c.id === activeData.columnId)!; const targetCol = newCols.find((c) => c.id === targetColumnId)!; const taskIndex = sourceCol.tasks.findIndex((t) => t.id === activeId); const [task] = sourceCol.tasks.splice(taskIndex, 1); targetCol.tasks.push(task); return newCols; }); }; const handleDragEnd = (_event: DragEndEvent) => { setActiveTask(null); }; const addTask = (columnId: ColumnId, title: string) => { const newTask: Task = { id: crypto.randomUUID(), title, createdAt: Date.now(), }; setColumns((cols) => cols.map((c) => c.id === columnId ? { ...c, tasks: [...c.tasks, newTask] } : c ) ); }; const deleteTask = (taskId: string) => { setColumns((cols) => cols.map((c) => ({ ...c, tasks: c.tasks.filter((t) => t.id !== taskId), })) ); }; const updateTask = (taskId: string, updates: Partial) => { setColumns((cols) => cols.map((c) => ({ ...c, tasks: c.tasks.map((t) => t.id === taskId ? { ...t, ...updates } : t ), })) ); }; return (

🐾 Clawd's Kanban

{columns.map((column) => ( addTask(column.id as ColumnId, title)} onDeleteTask={deleteTask} onUpdateTask={updateTask} /> ))}
{activeTask && }
); } export default App;