From a98e60fcc728e616d7c1a2b3c4fe539d1fb6783d Mon Sep 17 00:00:00 2001 From: Madhura68 Date: Sun, 10 May 2026 01:00:25 +0200 Subject: [PATCH] feat(PBI-74): dual-dispatch hydratie + realtime naar workspace-store (Story 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Story 2 — schaduw-fase: BacklogHydrationWrapper en useBacklogRealtime voeden nu ook de nieuwe product-workspace-store, terwijl de oude useBacklogStore / useProductStore leidend blijft voor componenten. Story 3 verschuift consumers één voor één; Story 8 ruimt de oude stores op. - T-844: BacklogHydrationWrapper roept naast useBacklogStore.setInitialData ook useProductWorkspaceStore.hydrateSnapshot aan. Productname-prop optioneel toegevoegd voor activeProduct-context. - T-845: useBacklogRealtime onmessage dispatcht events naar zowel oude store (applyChange) als nieuwe store (applyRealtimeEvent). Geen wijziging aan reconnect/visibility — Story 5. - T-846: dev-only logWorkspaceFingerprint helper vergelijkt counts tussen oude en nieuwe store na hydrate en na elk realtime-event. console.warn bij mismatch; opt-in debug log via NEXT_PUBLIC_DEBUG_WORKSPACE_FINGERPRINT=1. Bestand TODO-marked voor verwijdering in Story 8 (T-878). - T-847: SetCurrentProduct schrijft naast oude useProductStore ook useProductWorkspaceStore.setActiveProduct({id, name}); cleanup cleart beide. setActiveProduct triggert ensureProductLoaded — fetch-stub tot Story 7 (T-870) de LIST-endpoints toevoegt. Verify: lint+typecheck clean, 636/636 tests groen (geen UI-regressie omdat oude store leidend blijft). Refs: PBI-74, ST-1319, T-844..T-847 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../backlog/backlog-hydration-wrapper.tsx | 41 ++++++++++- components/shared/set-current-product.tsx | 12 +++- lib/realtime/dev-workspace-fingerprint.ts | 71 +++++++++++++++++++ lib/realtime/use-backlog-realtime.ts | 13 ++++ 4 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 lib/realtime/dev-workspace-fingerprint.ts diff --git a/components/backlog/backlog-hydration-wrapper.tsx b/components/backlog/backlog-hydration-wrapper.tsx index f2c6cf0..10b996a 100644 --- a/components/backlog/backlog-hydration-wrapper.tsx +++ b/components/backlog/backlog-hydration-wrapper.tsx @@ -3,6 +3,14 @@ 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' +import { useProductWorkspaceStore } from '@/stores/product-workspace/store' +import type { + BacklogPbi as WorkspacePbi, + BacklogStory as WorkspaceStory, + BacklogTask as WorkspaceTask, + ProductBacklogSnapshot, +} from '@/stores/product-workspace/types' +import { logWorkspaceFingerprint } from '@/lib/realtime/dev-workspace-fingerprint' interface InitialData { pbis: BacklogPbi[] @@ -13,6 +21,7 @@ interface InitialData { interface BacklogHydrationWrapperProps { initialData: InitialData productId: string + productName?: string children: React.ReactNode } @@ -27,7 +36,30 @@ function fingerprint(data: InitialData): string { return `${pbiPart}|${storyPart}|${taskPart}` } -export function BacklogHydrationWrapper({ initialData, productId, children }: BacklogHydrationWrapperProps) { +// PBI-74 / T-844: dual-dispatch — naast de oude useBacklogStore vullen we nu +// ook de nieuwe product-workspace-store. De oude store blijft tijdelijk +// leidend voor componenten; in Story 3 verschuiven consumers één voor één. +// De runtime-payload bevat sort_order op PBI/Story (Prisma schema), ook al +// staat het niet op het oude InitialData type — daarom de cast hieronder. +function toWorkspaceSnapshot( + data: InitialData, + productId: string, + productName: string | undefined, +): ProductBacklogSnapshot { + return { + product: { id: productId, name: productName ?? '' }, + pbis: data.pbis as unknown as WorkspacePbi[], + storiesByPbi: data.storiesByPbi as unknown as Record, + tasksByStory: data.tasksByStory as unknown as Record, + } +} + +export function BacklogHydrationWrapper({ + initialData, + productId, + productName, + children, +}: BacklogHydrationWrapperProps) { const setInitialData = useBacklogStore((s) => s.setInitialData) const lastFingerprint = useRef('') @@ -36,8 +68,13 @@ export function BacklogHydrationWrapper({ initialData, productId, children }: Ba if (fp !== lastFingerprint.current) { lastFingerprint.current = fp setInitialData(initialData) + // Dual-dispatch: nieuwe workspace-store schaduwt mee. + useProductWorkspaceStore + .getState() + .hydrateSnapshot(toWorkspaceSnapshot(initialData, productId, productName)) + logWorkspaceFingerprint('hydrate') } - }, [initialData, setInitialData]) + }, [initialData, productId, productName, setInitialData]) useBacklogRealtime(productId) diff --git a/components/shared/set-current-product.tsx b/components/shared/set-current-product.tsx index 1ff4684..3f007e9 100644 --- a/components/shared/set-current-product.tsx +++ b/components/shared/set-current-product.tsx @@ -2,14 +2,24 @@ import { useEffect } from 'react' import { useProductStore } from '@/stores/product-store' +import { useProductWorkspaceStore } from '@/stores/product-workspace/store' import { debugProps } from '@/lib/debug' +// PBI-74 / T-847: zet zowel oude useProductStore.setCurrentProduct als de +// nieuwe workspace-store.setActiveProduct. setActiveProduct triggert +// ensureProductLoaded met een requestId-guard; de fetch-stub levert tijdens +// Story 2 nog geen echte data — echte LIST-endpoints komen in Story 7 +// (T-870). Restore-hint flow volgt in Story 4 (T-857). export function SetCurrentProduct({ id, name }: { id: string; name: string }) { const { setCurrentProduct, clearCurrentProduct } = useProductStore() useEffect(() => { setCurrentProduct(id, name) - return () => clearCurrentProduct() + useProductWorkspaceStore.getState().setActiveProduct({ id, name }) + return () => { + clearCurrentProduct() + useProductWorkspaceStore.getState().setActiveProduct(null) + } }, [id, name, setCurrentProduct, clearCurrentProduct]) return