feat(PBI-76): migrate pbi-list to user-settings store

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) <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-05-10 12:54:01 +02:00
parent c10def601b
commit f7602d2582

View file

@ -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<number | 'all'>('all')
const [filterStatus, setFilterStatus] = useState<PbiStatusFilter>('all')
const [sortMode, setSortMode] = useState<SortMode>('priority')
const [sortDir, setSortDir] = useState<SortDir>('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<PbiDialogState | null>(null)
const [activeDragId, setActiveDragId] = useState<string | null>(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<SortMode>(
'scrum4me:pbi_sort',
(raw) => (raw === 'priority' || raw === 'code' || raw === 'date') ? raw : null,
'priority',
))
setFilterPriority(readLocalStoragePref<number | 'all'>(
'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<PbiStatusFilter>(
'scrum4me:pbi_filter_status',
(raw) => (raw === 'ready' || raw === 'blocked' || raw === 'done' || raw === 'all') ? raw : null,
'all',
))
setSortDir(readLocalStoragePref<SortDir>(
'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]))