From 38d99834ef04f9e00c6c30227e94fdfd12fed8f0 Mon Sep 17 00:00:00 2001 From: Madhura68 Date: Sun, 10 May 2026 07:17:28 +0200 Subject: [PATCH] fix: avoid duplicate backlog hydration load --- .../stores/product-workspace/store.test.ts | 29 +++++++++++++++++++ app/(app)/products/[id]/page.tsx | 1 + app/(mobile)/m/products/[id]/page.tsx | 1 + components/shared/set-current-product.tsx | 6 ++-- stores/product-workspace/store.ts | 18 ++++++++---- 5 files changed, 47 insertions(+), 8 deletions(-) diff --git a/__tests__/stores/product-workspace/store.test.ts b/__tests__/stores/product-workspace/store.test.ts index beb61f1..bfa6cfd 100644 --- a/__tests__/stores/product-workspace/store.test.ts +++ b/__tests__/stores/product-workspace/store.test.ts @@ -257,6 +257,35 @@ describe('selection cascade', () => { expect(s.relations.taskIdsByStory).toEqual({}) expect(s.loading.loadedProductId).toBeNull() }) + + it('setActiveProduct kan alleen context zetten zonder full backlog load', () => { + useProductWorkspaceStore.getState().hydrateSnapshot( + snapshotWith( + [makePbi({ id: 'p-1' })], + { 'p-1': [makeStory({ id: 's-1', pbi_id: 'p-1' })] }, + { 's-1': [makeTask({ id: 't-1', story_id: 's-1' })] }, + { id: 'prod-1', name: 'Product 1' }, + ), + ) + useProductWorkspaceStore.setState((s) => { + s.context.activePbiId = 'p-1' + s.context.activeStoryId = 's-1' + }) + const fetchSpy = vi.spyOn(globalThis, 'fetch') + + useProductWorkspaceStore + .getState() + .setActiveProduct( + { id: 'prod-1', name: 'Product 1' }, + { load: false, preserveSelection: true }, + ) + + const s = useProductWorkspaceStore.getState() + expect(fetchSpy).not.toHaveBeenCalled() + expect(s.context.activePbiId).toBe('p-1') + expect(s.context.activeStoryId).toBe('s-1') + expect(s.entities.pbisById['p-1']).toBeDefined() + }) }) // ───────────────────────────────────────────────────────────────────────── diff --git a/app/(app)/products/[id]/page.tsx b/app/(app)/products/[id]/page.tsx index 6acf005..bf38aa4 100644 --- a/app/(app)/products/[id]/page.tsx +++ b/app/(app)/products/[id]/page.tsx @@ -151,6 +151,7 @@ export default async function ProductBacklogPage({ params, searchParams }: Props
({ id: p.id, code: p.code, title: p.title, priority: p.priority, sort_order: p.sort_order, description: p.description, created_at: p.created_at, status: pbiStatusToApi(p.status) })), storiesByPbi, diff --git a/app/(mobile)/m/products/[id]/page.tsx b/app/(mobile)/m/products/[id]/page.tsx index 7d33f06..479db27 100644 --- a/app/(mobile)/m/products/[id]/page.tsx +++ b/app/(mobile)/m/products/[id]/page.tsx @@ -91,6 +91,7 @@ export default async function MobileProductBacklogPage({ params, searchParams }:
({ id: p.id, code: p.code, title: p.title, priority: p.priority, sort_order: p.sort_order, description: p.description, created_at: p.created_at, status: pbiStatusToApi(p.status) })), storiesByPbi, diff --git a/components/shared/set-current-product.tsx b/components/shared/set-current-product.tsx index 63aa151..94d71aa 100644 --- a/components/shared/set-current-product.tsx +++ b/components/shared/set-current-product.tsx @@ -8,9 +8,11 @@ import { debugProps } from '@/lib/debug' // De voorganger (stores/product-store.ts) wordt in Story 8 (T-876) verwijderd. export function SetCurrentProduct({ id, name }: { id: string; name: string }) { useEffect(() => { - useProductWorkspaceStore.getState().setActiveProduct({ id, name }) + useProductWorkspaceStore + .getState() + .setActiveProduct({ id, name }, { load: false, preserveSelection: true }) return () => { - useProductWorkspaceStore.getState().setActiveProduct(null) + useProductWorkspaceStore.getState().setActiveProduct(null, { load: false }) } }, [id, name]) diff --git a/stores/product-workspace/store.ts b/stores/product-workspace/store.ts index 7ca381c..103eb28 100644 --- a/stores/product-workspace/store.ts +++ b/stores/product-workspace/store.ts @@ -78,7 +78,10 @@ interface State { interface Actions { hydrateSnapshot(snapshot: ProductBacklogSnapshot): void - setActiveProduct(product: ActiveProduct | null): void + setActiveProduct( + product: ActiveProduct | null, + options?: { load?: boolean; preserveSelection?: boolean }, + ): void setActivePbi(pbiId: string | null): void setActiveStory(storyId: string | null): void setActiveTask(taskId: string | null): void @@ -223,15 +226,18 @@ export const useProductWorkspaceStore = create()( }) }, - setActiveProduct(product) { + setActiveProduct(product, options) { const requestId = newRequestId() const productChanged = get().context.activeProduct?.id !== product?.id + const shouldResetSelection = productChanged || !options?.preserveSelection set((s) => { s.context.activeProduct = product - s.context.activePbiId = null - s.context.activeStoryId = null - s.context.activeTaskId = null + if (shouldResetSelection) { + s.context.activePbiId = null + s.context.activeStoryId = null + s.context.activeTaskId = null + } s.loading.activeRequestId = requestId if (productChanged) { @@ -252,7 +258,7 @@ export const useProductWorkspaceStore = create()( // selectie kan herstellen. T-857: restore-flow start na ensureProductLoaded. writeProductHint(product?.id ?? null) - if (product) { + if (product && options?.load !== false) { const productId = product.id void (async () => { await get().ensureProductLoaded(productId, requestId)