feat(ST-356): add solo Kanban board with DnD and Zustand store

- useSoloStore: initTasks, optimisticMove (returns prev status), rollback, updatePlan
- SoloBoard: DndContext with PointerSensor (distance:5), closestCorners, 3 columns
- SoloColumn: useDroppable per status, MD3 status-color headers, task count, empty state
- SoloTaskCard + SoloTaskCardOverlay: useDraggable (disabled for demo), priority left-border
- onDragEnd: optimisticMove → updateTaskStatusAction → rollback + toast on error
- REVIEW tasks mapped to IN_PROGRESS column

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-04-26 16:54:52 +02:00
parent 90598e9378
commit e1903bc16c
4 changed files with 291 additions and 5 deletions

32
stores/solo-store.ts Normal file
View file

@ -0,0 +1,32 @@
import { create } from 'zustand'
import type { SoloTask } from '@/components/solo/solo-board'
type TaskStatus = SoloTask['status']
interface SoloStore {
tasks: Record<string, SoloTask>
initTasks: (tasks: SoloTask[]) => void
optimisticMove: (taskId: string, toStatus: TaskStatus) => TaskStatus | null
rollback: (taskId: string, prevStatus: TaskStatus) => void
updatePlan: (taskId: string, plan: string | null) => void
}
export const useSoloStore = create<SoloStore>((set, get) => ({
tasks: {},
initTasks: (tasks) =>
set({ tasks: Object.fromEntries(tasks.map(t => [t.id, t])) }),
optimisticMove: (taskId, toStatus) => {
const prev = get().tasks[taskId]?.status ?? null
if (!prev) return null
set((s) => ({ tasks: { ...s.tasks, [taskId]: { ...s.tasks[taskId], status: toStatus } } }))
return prev
},
rollback: (taskId, prevStatus) =>
set((s) => ({ tasks: { ...s.tasks, [taskId]: { ...s.tasks[taskId], status: prevStatus } } })),
updatePlan: (taskId, plan) =>
set((s) => ({ tasks: { ...s.tasks, [taskId]: { ...s.tasks[taskId], implementation_plan: plan } } })),
}))