fix(audit): H15 (saved-view sort) + H14 (back/forward URL resync) in usePaginatedQuery

H15: new applyView({filters,sort}) atomic mutator (one URL write) restores a
saved view's sort, threaded through all six list components instead of being
discarded. H14: a guarded effect resyncs page/sort/filters FROM the URL on
Back/Forward; the resync setStates carry a scoped, justified
set-state-in-effect disable (loop-guarded external-URL sync).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-02 12:26:10 +02:00
parent 808e80744b
commit 29fb882478
7 changed files with 152 additions and 27 deletions

View File

@@ -85,7 +85,7 @@ export function BerthList() {
setSort,
filters,
setFilter,
setAllFilters,
applyView,
clearFilters,
setPage,
setPageSize,
@@ -183,8 +183,8 @@ export function BerthList() {
<div className="flex items-center gap-2 ml-auto">
<SavedViewsDropdown
entityType="berths"
onApplyView={(savedFilters, _savedSort) => {
setAllFilters(savedFilters);
onApplyView={(savedFilters, savedSort) => {
applyView({ filters: savedFilters, sort: savedSort });
}}
/>
<Button

View File

@@ -115,7 +115,7 @@ export function ClientList() {
setPageSize,
filters,
setFilter,
setAllFilters,
applyView,
clearFilters,
} = usePaginatedQuery<ClientRow>({
queryKey: ['clients'],
@@ -189,12 +189,12 @@ export function ClientList() {
/>
<SavedViewsDropdown
entityType="clients"
onApplyView={(savedFilters, _savedSort) => {
// Atomic replace - sequential setFilter() calls dropped all
// but the last value (each one read stale `filters` from
// closure and overwrote). setAllFilters writes the whole
// saved view in one setState.
setAllFilters(savedFilters);
onApplyView={(savedFilters, savedSort) => {
// Atomic replace of filters AND sort in one URL write. Passing
// both args fixes H15 (the saved sort was previously dropped);
// applyView also avoids the two-write race that a setAllFilters
// + setSort pair would hit.
applyView({ filters: savedFilters, sort: savedSort });
}}
/>
<ColumnPicker

View File

@@ -97,7 +97,7 @@ export function CompanyList() {
setPageSize,
filters,
setFilter,
setAllFilters,
applyView,
clearFilters,
} = usePaginatedQuery<CompanyRow>({
queryKey: ['companies'],
@@ -155,8 +155,8 @@ export function CompanyList() {
<div className="ml-auto flex items-center gap-2">
<SavedViewsDropdown
entityType="companies"
onApplyView={(savedFilters, _savedSort) => {
setAllFilters(savedFilters);
onApplyView={(savedFilters, savedSort) => {
applyView({ filters: savedFilters, sort: savedSort });
}}
/>
<ColumnPicker columns={COMPANY_COLUMN_OPTIONS} hidden={hidden} onChange={setHidden} />

View File

@@ -110,7 +110,7 @@ export function InterestList() {
setPageSize,
filters,
setFilter,
setAllFilters,
applyView,
clearFilters,
} = usePaginatedQuery<InterestRow>({
queryKey: ['interests'],
@@ -266,8 +266,8 @@ export function InterestList() {
<>
<SavedViewsDropdown
entityType="interests"
onApplyView={(savedFilters) => {
setAllFilters(savedFilters);
onApplyView={(savedFilters, savedSort) => {
applyView({ filters: savedFilters, sort: savedSort });
}}
/>
<ColumnPicker

View File

@@ -83,7 +83,7 @@ export function ResidentialInterestsList() {
setPageSize,
filters,
setFilter,
setAllFilters,
applyView,
clearFilters,
} = usePaginatedQuery<ResidentialInterestRow>({
queryKey: ['residential-interests'],
@@ -193,7 +193,9 @@ export function ResidentialInterestsList() {
<div className="ml-auto flex flex-wrap items-center gap-2">
<SavedViewsDropdown
entityType="residential_interests"
onApplyView={(savedFilters) => setAllFilters(savedFilters)}
onApplyView={(savedFilters, savedSort) =>
applyView({ filters: savedFilters, sort: savedSort })
}
/>
<ColumnPicker
columns={RESIDENTIAL_INTEREST_COLUMN_OPTIONS}

View File

@@ -99,7 +99,7 @@ export function YachtList() {
setPageSize,
filters,
setFilter,
setAllFilters,
applyView,
clearFilters,
} = usePaginatedQuery<YachtRow>({
queryKey: ['yachts'],
@@ -153,8 +153,8 @@ export function YachtList() {
/>
<SavedViewsDropdown
entityType="yachts"
onApplyView={(savedFilters, _savedSort) => {
setAllFilters(savedFilters);
onApplyView={(savedFilters, savedSort) => {
applyView({ filters: savedFilters, sort: savedSort });
}}
/>
</div>