import { useState, useEffect, useCallback } from "react"; import { useNavigate } from "react-router-dom"; import { useVault } from "../App"; import { listTasks, toggleTask, type TaskItem } from "../lib/commands"; type Column = "todo" | "in-progress" | "done"; const COLUMNS: { id: Column; label: string; icon: string }[] = [ { id: "todo", label: "To Do", icon: "☐" }, { id: "in-progress", label: "In Progress", icon: "◐" }, { id: "done", label: "Done", icon: "✓" }, ]; /** * KanbanView — Visual board extracted from `- [ ]` / `- [/]` / `- [x]` items across vault. */ export function KanbanView() { const { vaultPath, refreshNotes } = useVault(); const navigate = useNavigate(); const [tasks, setTasks] = useState([]); const [loading, setLoading] = useState(true); const [dragItem, setDragItem] = useState(null); const loadTasks = useCallback(async () => { if (!vaultPath) return; setLoading(true); try { const items = await listTasks(vaultPath); setTasks(items); } catch { setTasks([]); } setLoading(false); }, [vaultPath]); useEffect(() => { loadTasks(); }, [loadTasks]); const handleDrop = async (column: Column) => { if (!dragItem || dragItem.state === column) return; try { await toggleTask(vaultPath, dragItem.source_path, dragItem.line_number, column); setTasks(prev => prev.map(t => t.source_path === dragItem.source_path && t.line_number === dragItem.line_number ? { ...t, state: column } : t )); refreshNotes(); } catch (e) { console.error("Toggle failed:", e); } setDragItem(null); }; const openSource = (task: TaskItem) => { navigate(`/note/${encodeURIComponent(task.source_path)}`); }; if (loading) { return (
Loading tasks…
); } return (

📋 Task Board

{tasks.length} tasks
{COLUMNS.map(col => { const colTasks = tasks.filter(t => t.state === col.id); return (
e.preventDefault()} onDrop={() => handleDrop(col.id)} >
{col.icon} {col.label} {colTasks.length}
{colTasks.map((task, i) => (
setDragItem(task)} onDragEnd={() => setDragItem(null)} >
{task.text}
))} {colTasks.length === 0 && (
No tasks
)}
); })}
); }