- StartSprintButton dialog toont 3-state banner: info met accurate vrije-
stories count + PBI-context, of waarschuwing als geen PBI geselecteerd
is, of waarschuwing als de geselecteerde PBI 0 vrije stories heeft
- Voeg sprint_id toe aan BacklogStory/Story/SprintStory + select in PB-
pagina's en sprint-board mappings, zodat de banner accuraat kan tellen
- createSprintAction: revalidatePath met 'layout' flag voor consistency
met createSprintWithPbisAction (top-nav 'Sprint' link ververst direct)
Sprint-switch data-refresh op alle relevante pagina's:
- BacklogHydrationWrapper: fingerprint-based re-hydratie zodat PB-data
na router.refresh opnieuw uit nieuwe initialData komt (was: useEffect
met lege deps draaide alleen 1x)
- SprintBoardClient: key={sprint.id} forceert remount bij sprint-switch
zodat lokale sprintStories/sprintStoryIds-state vers ge-init wordt
- Solo (desktop + mobile): gebruik resolveActiveSprint(id) ipv eerste
OPEN-sprint, plus key={sprint.id} op SoloBoard voor remount
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
45 lines
1.4 KiB
TypeScript
45 lines
1.4 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useRef } from 'react'
|
|
import { useBacklogStore, type BacklogPbi, type BacklogStory, type BacklogTask } from '@/stores/backlog-store'
|
|
import { useBacklogRealtime } from '@/lib/realtime/use-backlog-realtime'
|
|
|
|
interface InitialData {
|
|
pbis: BacklogPbi[]
|
|
storiesByPbi: Record<string, BacklogStory[]>
|
|
tasksByStory: Record<string, BacklogTask[]>
|
|
}
|
|
|
|
interface BacklogHydrationWrapperProps {
|
|
initialData: InitialData
|
|
productId: string
|
|
children: React.ReactNode
|
|
}
|
|
|
|
function fingerprint(data: InitialData): string {
|
|
const pbiPart = data.pbis.map((p) => `${p.id}:${p.status}:${p.priority}`).join(',')
|
|
const storyPart = Object.entries(data.storiesByPbi)
|
|
.flatMap(([, list]) => list.map((s) => `${s.id}:${s.status}:${s.sprint_id ?? 'null'}`))
|
|
.join(',')
|
|
const taskPart = Object.entries(data.tasksByStory)
|
|
.flatMap(([, list]) => list.map((t) => `${t.id}:${t.status}`))
|
|
.join(',')
|
|
return `${pbiPart}|${storyPart}|${taskPart}`
|
|
}
|
|
|
|
export function BacklogHydrationWrapper({ initialData, productId, children }: BacklogHydrationWrapperProps) {
|
|
const setInitialData = useBacklogStore((s) => s.setInitialData)
|
|
const lastFingerprint = useRef<string>('')
|
|
|
|
useEffect(() => {
|
|
const fp = fingerprint(initialData)
|
|
if (fp !== lastFingerprint.current) {
|
|
lastFingerprint.current = fp
|
|
setInitialData(initialData)
|
|
}
|
|
}, [initialData, setInitialData])
|
|
|
|
useBacklogRealtime(productId)
|
|
|
|
return <>{children}</>
|
|
}
|