* docs(cleanup): archief verouderde plannen, backlog en root-duplicaten
- 6 plans naar docs/old/plans/ (PBI-11/75/78, user-settings-store, Local github setup, lees-de-readme — laatste was verkeerde repo)
- docs/backlog/ naar docs/old/backlog/ (pre-MCP statische registry; live werk loopt via Scrum4Me-MCP)
- 6 root-level duplicaten naar docs/old/ (functional, {pbi,story,task}-dialog, product-backlog, backlog)
- 2 landing plans (niet uitgevoerd) krijgen archived: true frontmatter — blijven op plek maar uit INDEX
- scripts/generate-docs-index.mjs: skip docs/old/** + skip archived: true
- CLAUDE.md: rijen docs/backlog/, docs/plans/<key>-*.md, docs/manual/ weg; Track B-sectie verwijderd
- README.md / CHANGELOG.md / docs/plans/v1-readiness.md: link-fixes naar nieuwe locaties
Verify groen (lint + typecheck + 718 tests). docs/INDEX.md geregenereerd.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(cleanup): registreer handmatige verplaatsingen en fix referenties
- 4 plans verplaatst naar docs/old/plans/ (M10-qr-pairing-login, auto-pr-deploy-sync, docs-restructure-ai-lookup, v1-readiness)
- 3 archive-plans verplaatst naar docs/old/plans/ (archive-map nu leeg)
- ST-1114-copilot-reviews + 3 research-docs naar nieuwe docs/Ideas/ map
- Duplicaat docs/old/2026-04-27-m8-realtime-solo.md verwijderd (origineel zit in docs/old/plans/)
- Link-fixes naar nieuwe locaties:
- CHANGELOG.md → docs/old/plans/v1-readiness.md
- docs/runbooks/deploy-control.md → docs/old/plans/auto-pr-deploy-sync.md (2x)
- docs/runbooks/worker-idempotency.md → docs/old/plans/auto-pr-deploy-sync.md
- docs/plans/docs-restructure-pbi-spec.md → docs/old/plans/docs-restructure-ai-lookup.md (4x text + 2x href)
- docs/INDEX.md geregenereerd (96 docs, was 100)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.2 KiB
PBI-75 — Sprint task-edit client-side via workspace-store
Context
In het Sprint-scherm (/products/<id>/sprint/<sprintId>) duurt het bewerken van een taak onevenredig lang. Klik op een taakregel of het potlood-icoon roept router.push(?editTask=<id>) aan vanuit components/sprint/task-list.tsx:226. Dat triggert:
- Volledige server re-render van
app/(app)/products/[id]/sprint/[sprintId]/page.tsxmet zware queries (sprint, alle sprintStories+tasks, productMembers, alle PBIs+stories voor het backlog-paneel) - Tweede Prisma-query in
EditTaskLoadervoor task-detail (incl.implementation_plan) - Na save:
router.push(closePath)+revalidatePath→ opnieuw alle queries
De sprint-workspace-store (sinds PBI-74 Story 9) bevat al alles voor een client-side edit-flow:
setActiveTask(taskId)(regel 337) — zetcontext.activeTaskId+ roeptensureTaskLoaded()aanensureTaskLoaded(taskId)(regel 414) —GET /api/tasks/{id}→ upsert met_detail: trueselectActiveTask(regel 91) — bestaat, nog geen consumerapplyTaskEvent(regel 748) — SSE-events propageren idempotent na server-save- Optimistic-mutation primitives (
applyOptimisticMutation/settleMutation/rollbackMutation)
Het patroon "URL-param → store" bestaat al voor product-workspace in components/backlog/url-task-sync.tsx — we volgen dat als precedent.
Doel: klik op een taak opent de edit-dialoog client-side via store-state binnen ~100ms. Geen URL-navigatie, geen server re-render, alleen GET /api/tasks/{id} voor het detail.
Aanpak
Architectuur: store-mounted dialog + URL-sync component voor deeplinks.
- Klik-flow:
TaskList.openEditDialogroeptsetActiveTask(taskId)aan op de store. Geenrouter.push. - Render-flow: nieuwe client-component
SprintTaskDialogMountzit binnenSprintHydrationWrapper, subscribetselectActiveTask, en rendert<TaskDialog>zodra de active task_detail === trueis. - Save-flow:
TaskDialogkrijgt optioneleonClose/onSavedcallbacks (backwards compatible met bestaandeclosePath). Mount geeftonClose = () => setActiveTask(null). Server actionsaveTaskblijftrevalidatePathdoen voor server-cache; SSE-event update store viaapplyTaskEvent. - Deeplink-flow: nieuwe
SprintUrlTaskSyncleest?editTask=<id>en roeptsetActiveTask(id)aan (analoog aanurl-task-sync.tsx).
Bestanden + wijzigingen
Nieuw — components/sprint/sprint-task-dialog-mount.tsx
Client component. Subscribet selectActiveTask (single-value, geen useShallow). Wanneer task aanwezig is en isDetail(task) true, mappt naar TaskDialogTask-shape:
status: viataskStatusFromApiuitlib/task-status.ts(lowercase API → Prisma UPPER_SNAKE)implementation_plan: task.implementation_plan ?? nullcreated_at: new Date(task.created_at)
Rendert <TaskDialog task={mapped} productId={productId} onClose={() => setActiveTask(null)} isDemo={isDemo} />. Geen render tussen setActiveTask en _detail: true (detail-fetch <100ms).
Nieuw — components/sprint/sprint-url-task-sync.tsx
Kopie van components/backlog/url-task-sync.tsx maar tegen useSprintWorkspaceStore en writeTaskHint uit stores/sprint-workspace/restore.
Wijziging — components/sprint/task-list.tsx (regels 225-227)
Vervang:
function openEditDialog(taskId: string) {
router.push(`${pathname}?editTask=${taskId}`)
}
door:
function openEditDialog(taskId: string) {
useSprintWorkspaceStore.getState().setActiveTask(taskId)
}
openCreateDialog (regel 222) blijft URL-gebaseerd — out-of-scope.
Wijziging — app/(app)/products/[id]/sprint/[sprintId]/page.tsx
- Verwijder
editTaskuit searchParams-destructuring (regel 36) - Verwijder
editTask &&-block met<Suspense><EditTaskLoader>(regels 250-260) - Verwijder ongebruikte imports (
EditTaskLoader,TaskDialogSkeleton, evt.Suspense) - Mount binnen
SprintHydrationWrapper:<SprintHydrationWrapper ...> <SprintBoardClient ... /> <SprintTaskDialogMount productId={id} isDemo={isDemo} /> <SprintUrlTaskSync /> </SprintHydrationWrapper> newTask-block (regels 241-248) blijft ongemoeid — out-of-scope.
Wijziging — app/_components/tasks/task-dialog.tsx
Maak closePath optioneel + voeg onClose/onSaved toe (backwards compatible):
interface TaskDialogProps {
task?: TaskDialogTask
storyId?: string
productId: string
closePath?: string
onClose?: () => void
onSaved?: (taskId: string) => void
isDemo?: boolean
}
Refactor de drie router.push(closePath)-calls (regels 104, 120, 155) naar één helper:
function close() {
if (onClose) { onClose(); return }
if (closePath) router.push(closePath)
}
Bestaande callers (EditTaskLoader, mobile, product-page, sprint newTask-block) blijven werken via closePath. Nieuwe SprintTaskDialogMount gebruikt onClose.
Geen wijziging
stores/sprint-workspace/selectors.ts—selectActiveTaskbestaat alapp/_components/tasks/edit-task-loader.tsx— nog gebruikt door product-page en mobile
Edge cases
- Status-enum mapping: store API-lowercase → Prisma UPPER_SNAKE via
taskStatusFromApi, fallback'TO_DO' _detail: truerace: mount rendert pas wanneerisDetail(task)true is — geen flash met undefined velden- Demo-mode: prop blijft via server doorlopen, dialog respecteert al
isDemo - Dirty-close-guard: ingebouwd in dialog (regels 107, 172) — werkt via
onClose - SSE na save:
applyTaskEventupdatet store automatisch - Deeplink + task niet bestaat:
GET /api/tasks/{id}404 → store doet niets, dialog opent niet (huidigeredirect()verdwijnt — acceptabel)
Verificatie
- Browser (
npm run dev): klik op task in takenlijst → dialog opent <100ms, geen URL-verandering, alleenGET /api/tasks/<id>in Network - Save: wijzig titel → Opslaan → dialog sluit → store toont nieuwe titel via SSE
- Deeplink:
?editTask=<id>→ dialog opent viaSprintUrlTaskSync - Bestaande flows ongebroken: product-page edit, mobile edit, sprint
?newTask=1 npm run verify && npm run build- Vitest:
__tests__/components/sprint/sprint-task-dialog-mount.test.tsx— hydreer store, mock fetch,setActiveTask(id), assert UPPER_SNAKE status +onCloseclear
Risico's
- Andere mounts (mobile, product-backlog, sprint
newTask) blijven URL-gebaseerd —closePath?optional houdt ze werkend - Geen
redirect()bij not-found-deeplink (klein UX-verschil) - SSE-latency 100-500ms na save — eventueel later mitigeren via
applyOptimisticMutationinonSaved-callback
Out-of-scope (follow-up PBIs)
?newTask=1-flow naar store- Mobile + product-backlog mounts
EditTaskLoaderverwijderen wanneer alle callers over zijn