From f7602d258249178779d2e7b2023a3204d4e4188e Mon Sep 17 00:00:00 2001 From: Madhura68 Date: Sun, 10 May 2026 12:54:01 +0200 Subject: [PATCH] feat(PBI-76): migrate pbi-list to user-settings store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same pattern as sprint-backlog: replaces local useState + localStorage hydration/persist with selectors from useUserSettingsStore. filterPopoverOpen blijft lokaal — die was nooit gepersisteerd in pbi-list. Co-Authored-By: Claude Opus 4.7 (1M context) --- components/backlog/pbi-list.tsx | 61 +++++++++------------------------ 1 file changed, 16 insertions(+), 45 deletions(-) diff --git a/components/backlog/pbi-list.tsx b/components/backlog/pbi-list.tsx index 896fa9f..48b007e 100644 --- a/components/backlog/pbi-list.tsx +++ b/components/backlog/pbi-list.tsx @@ -1,6 +1,6 @@ 'use client' -import { useState, useTransition, useEffect } from 'react' +import { useState, useTransition } from 'react' import { DndContext, DragEndEvent, @@ -30,7 +30,7 @@ import { type SortDir, } from '@/components/shared/backlog-filter-popover' import { useShallow } from 'zustand/react/shallow' -import { readLocalStoragePref } from '@/lib/use-local-storage-pref' +import { useUserSettingsStore } from '@/stores/user-settings/store' import { useProductWorkspaceStore } from '@/stores/product-workspace/store' import { selectVisiblePbis } from '@/stores/product-workspace/selectors' import type { BacklogPbi as WorkspacePbi } from '@/stores/product-workspace/types' @@ -199,12 +199,21 @@ export function PbiList({ productId, isDemo }: PbiListProps) { // voorkomt re-render op ongerelateerde store-mutaties (G2). const pbis = useProductWorkspaceStore(useShallow(selectVisiblePbis)) as WorkspacePbi[] const selectedPbiId = useProductWorkspaceStore((s) => s.context.activePbiId) - const [filterPriority, setFilterPriority] = useState('all') - const [filterStatus, setFilterStatus] = useState('all') - const [sortMode, setSortMode] = useState('priority') - const [sortDir, setSortDir] = useState('asc') + const prefs = useUserSettingsStore( + useShallow((s) => s.entities.settings.views?.pbiList ?? {}), + ) + const setPref = useUserSettingsStore((s) => s.setPref) + const filterPriority = prefs.filterPriority ?? 'all' + const filterStatus: PbiStatusFilter = prefs.filterStatus ?? 'all' + const sortMode: SortMode = prefs.sort ?? 'priority' + const sortDir: SortDir = prefs.sortDir ?? 'asc' + const setFilterPriority = (v: number | 'all') => + void setPref(['views', 'pbiList', 'filterPriority'], v) + const setFilterStatus = (v: PbiStatusFilter) => + void setPref(['views', 'pbiList', 'filterStatus'], v) + const setSortMode = (v: SortMode) => void setPref(['views', 'pbiList', 'sort'], v) + const setSortDir = (v: SortDir) => void setPref(['views', 'pbiList', 'sortDir'], v) const [filterPopoverOpen, setFilterPopoverOpen] = useState(false) - const [prefsLoaded, setPrefsLoaded] = useState(false) const [dialogState, setDialogState] = useState(null) const [activeDragId, setActiveDragId] = useState(null) const [selectionMode, setSelectionMode] = useState(false) @@ -226,44 +235,6 @@ export function PbiList({ productId, isDemo }: PbiListProps) { }) } - // Hydrate prefs post-mount; SSR + first client render use defaults so no - // hydration mismatch. Users with saved == default see no change; others see - // one filter update right after hydration. - useEffect(() => { - /* eslint-disable react-hooks/set-state-in-effect */ - setSortMode(readLocalStoragePref( - 'scrum4me:pbi_sort', - (raw) => (raw === 'priority' || raw === 'code' || raw === 'date') ? raw : null, - 'priority', - )) - setFilterPriority(readLocalStoragePref( - 'scrum4me:pbi_filter_priority', - (raw) => { - if (raw === 'all') return 'all' - const n = parseInt(raw, 10) - return Number.isInteger(n) && n >= 1 && n <= 4 ? n : null - }, - 'all', - )) - setFilterStatus(readLocalStoragePref( - 'scrum4me:pbi_filter_status', - (raw) => (raw === 'ready' || raw === 'blocked' || raw === 'done' || raw === 'all') ? raw : null, - 'all', - )) - setSortDir(readLocalStoragePref( - 'scrum4me:pbi_sort_dir', - (raw) => (raw === 'asc' || raw === 'desc') ? raw : null, - 'asc', - )) - setPrefsLoaded(true) - /* eslint-enable react-hooks/set-state-in-effect */ - }, []) - - useEffect(() => { if (prefsLoaded) localStorage.setItem('scrum4me:pbi_sort', sortMode) }, [sortMode, prefsLoaded]) - useEffect(() => { if (prefsLoaded) localStorage.setItem('scrum4me:pbi_filter_priority', String(filterPriority)) }, [filterPriority, prefsLoaded]) - useEffect(() => { if (prefsLoaded) localStorage.setItem('scrum4me:pbi_filter_status', filterStatus) }, [filterStatus, prefsLoaded]) - useEffect(() => { if (prefsLoaded) localStorage.setItem('scrum4me:pbi_sort_dir', sortDir) }, [sortDir, prefsLoaded]) - // pbis komen al gesorteerd binnen via selectVisiblePbis (priority + sort_order). // Geen aparte order/priority maps meer — workspace-store entities zijn de waarheid. const pbiMap = Object.fromEntries(pbis.map(p => [p.id, p]))