feat(PBI-75): sprint task-edit client-side via workspace-store (#183)
Klik op een taak in het sprint-scherm opent de edit-dialog nu
client-side via setActiveTask op de sprint-workspace-store.
Geen URL-navigatie, geen volledige server re-render — alleen
GET /api/tasks/{id} voor het detail. SSE propageert server-saves
automatisch terug.
- TaskDialog: optionele onClose/onSaved callbacks (closePath
optional gemaakt — backwards compatible)
- SprintTaskDialogMount: nieuwe client-component die
selectActiveTask consumeert en TaskDialog rendert
- SprintUrlTaskSync: deeplink (?editTask=<id>) → store
- Sprint page: mounts toegevoegd, editTask searchParam +
EditTaskLoader-Suspense verwijderd
- TaskList.openEditDialog roept setActiveTask aan ipv router.push
- Vitest integratie-test voor SprintTaskDialogMount
Out-of-scope (follow-up PBIs): newTask-flow, mobile, product-backlog.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3b5cee823c
commit
a9b53dedf0
8 changed files with 335 additions and 25 deletions
41
components/sprint/sprint-task-dialog-mount.tsx
Normal file
41
components/sprint/sprint-task-dialog-mount.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
'use client'
|
||||
|
||||
import { useSprintWorkspaceStore } from '@/stores/sprint-workspace/store'
|
||||
import { selectActiveTask } from '@/stores/sprint-workspace/selectors'
|
||||
import { isDetail } from '@/stores/sprint-workspace/types'
|
||||
import { TaskDialog } from '@/app/_components/tasks/task-dialog'
|
||||
import { taskStatusFromApi } from '@/lib/task-status'
|
||||
import type { TaskStatus } from '@prisma/client'
|
||||
|
||||
interface Props {
|
||||
productId: string
|
||||
isDemo: boolean
|
||||
}
|
||||
|
||||
export function SprintTaskDialogMount({ productId, isDemo }: Props) {
|
||||
const task = useSprintWorkspaceStore(selectActiveTask)
|
||||
const setActiveTask = useSprintWorkspaceStore((s) => s.setActiveTask)
|
||||
|
||||
if (!task || !isDetail(task)) return null
|
||||
|
||||
const status = (taskStatusFromApi(String(task.status)) ?? 'TO_DO') as TaskStatus
|
||||
const createdAt = task.created_at instanceof Date ? task.created_at : new Date(task.created_at)
|
||||
|
||||
return (
|
||||
<TaskDialog
|
||||
task={{
|
||||
id: task.id,
|
||||
code: task.code,
|
||||
title: task.title,
|
||||
description: task.description,
|
||||
implementation_plan: task.implementation_plan ?? null,
|
||||
priority: task.priority,
|
||||
status,
|
||||
created_at: createdAt,
|
||||
}}
|
||||
productId={productId}
|
||||
onClose={() => setActiveTask(null)}
|
||||
isDemo={isDemo}
|
||||
/>
|
||||
)
|
||||
}
|
||||
29
components/sprint/sprint-url-task-sync.tsx
Normal file
29
components/sprint/sprint-url-task-sync.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
'use client'
|
||||
|
||||
// PBI-75: URL-deeplink → store sync voor sprint task-edit.
|
||||
//
|
||||
// Patroon spiegelt components/backlog/url-task-sync.tsx: zodra de route
|
||||
// `?editTask=<id>` draagt, schrijven we de taak-hint en roepen we
|
||||
// setActiveTask aan op de sprint-workspace-store. De dialog wordt
|
||||
// vervolgens client-side gemount door SprintTaskDialogMount.
|
||||
|
||||
import { useEffect } from 'react'
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
import { useSprintWorkspaceStore } from '@/stores/sprint-workspace/store'
|
||||
import { writeTaskHint } from '@/stores/sprint-workspace/restore'
|
||||
|
||||
export function SprintUrlTaskSync() {
|
||||
const searchParams = useSearchParams()
|
||||
const editTask = searchParams.get('editTask')
|
||||
|
||||
useEffect(() => {
|
||||
if (!editTask) return
|
||||
const sprintId = useSprintWorkspaceStore.getState().context.activeSprintId
|
||||
if (sprintId) {
|
||||
writeTaskHint(sprintId, editTask)
|
||||
}
|
||||
useSprintWorkspaceStore.getState().setActiveTask(editTask)
|
||||
}, [editTask])
|
||||
|
||||
return null
|
||||
}
|
||||
|
|
@ -223,7 +223,7 @@ export function TaskList({ sprintId: _sprintId, productId: _productId, isDemo }:
|
|||
}
|
||||
|
||||
function openEditDialog(taskId: string) {
|
||||
router.push(`${pathname}?editTask=${taskId}`)
|
||||
useSprintWorkspaceStore.getState().setActiveTask(taskId)
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue