Skip to content

feat: session-persistent list filters (issue #3789 Deliverable B) - #5998

Draft
weimiao67 wants to merge 3 commits into
OPS-3789/context-aware-breadcrumbsfrom
OPS-3789/session-persistent-filters
Draft

feat: session-persistent list filters (issue #3789 Deliverable B)#5998
weimiao67 wants to merge 3 commits into
OPS-3789/context-aware-breadcrumbsfrom
OPS-3789/session-persistent-filters

Conversation

@weimiao67

@weimiao67 weimiao67 commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

What changed

Filter selections on the list pages now persist across client-side navigation for the duration of the session, so returning to a list (e.g. via a breadcrumb) keeps the user's filters — and selected fiscal year — applied. Filters are intentionally lost on browser refresh / new tab (in-memory Redux) and cleared on logout.

Approach: extend the session-scoped sessionUISlice (introduced in Deliverable A) with a listFilters sub-state keyed by page, and add a useListFilters(page) hook exposing a useState-compatible API (object or functional-updater form) so the existing *FilterButton / *FilterTags sub-components keep working unchanged. All six list pages source their filters (and fiscal year, where applicable) from the slice instead of local useState. Logout reset reuses A's single extraReducers hook.

Pages: Agreements, CANs, Budget Lines, Portfolios, Projects, Procurement Dashboard.

Key pieces:

  • sessionUISlice listFilters — per-page default shapes copied verbatim from each page's original useState; selectedFiscalYear persisted alongside filters where the resolved query blends both. setListFilters / setListFiscalYear / resetListFilters actions; buildPageState is the single deep-clone source for init + reset.
  • useListFilters(page)filters / setFilters / selectedFiscalYear / setSelectedFiscalYear, plus changeFiscalYear which atomically resets the page's filters and sets the new FY (one shared code path for all FY-aware pages).
  • CANFilterButton — fixed a reference-equality budget check to compare by value.

This is Deliverable B of the issue and stacks on Deliverable A (#5995) — the PR is based on the A branch so the diff shows only B's changes. Rebase to main once A merges.

Issue

#3789

How to test

  1. Go to any list page (e.g. Portfolios), apply a filter → open a detail page → navigate back. The filter is still applied.
  2. Change the fiscal year on a list, navigate away and back → the FY selection persists.
  3. Fiscal-year change resets filters consistently across Agreements, CANs, Projects, and Portfolios (a filter valid in one FY is not carried into another).
  4. On CANs, apply a budget-range filter, reopen the filter modal, drag the slider back to the full range, and Apply → the budget filter is cleared (not retained).
  5. Refresh the browser → filters clear (in-memory only, by design). Log out → filters clear.

Verified locally by driving the running stack (apply → navigate → back → still applied; FY persists; refresh clears).

A11y impact

  • No accessibility-impacting changes in this PR

No markup/ARIA changes; filter state moves from local component state to Redux with identical rendering.

Storybook

  • N/A — change is page-specific or non-visual

Changes are in a Redux slice, a hook, and page/hook wiring — no src/components/UI/ component added or changed.

Definition of Done Checklist

  • OESA: Code refactored for clarity
  • OESA: Dependency rules followed
  • Automated unit tests updated and passed
  • Automated integration tests updated and passed
  • Automated quality tests updated and passed
  • Automated load tests updated and passed
  • Automated a11y tests updated and passed
  • Automated security tests updated and passed
  • 90%+ Code coverage achieved
  • Form validations updated

Notes for reviewers

  • Behavior change: CANs and Projects now clear other filters when the fiscal year changes (previously CANs reset nothing and Projects cleared only the FY chip). This aligns with the product decision "changing FY still clears other filters" and makes all four FY-aware pages consistent.
  • A follow-up commit in this branch addresses code-review findings (budget-clear bug, FY-reset unification, defensive selector chaining, dedup).

Links

weimiao67 and others added 2 commits July 26, 2026 23:11
Filter selections (and the selected fiscal year) on the list pages now
persist across client-side navigation for the duration of the session, so
returning to a list via breadcrumb keeps the user's filters applied.
Filters are intentionally lost on browser refresh / new tab (in-memory
Redux) and cleared on logout.

- Extend sessionUISlice with a `listFilters` sub-state keyed by page
  (agreements, cans, budgetLines, portfolios, projects,
  procurementDashboard). Default shapes copied verbatim from each page's
  original useState; selectedFiscalYear persisted alongside filters where
  the resolved query blends both. Logout reset is the same single
  extraReducers hook shared with the breadcrumb trail.
- Add useListFilters(page) hook exposing a useState-compatible API
  (object OR functional-updater form) so the *FilterButton/*FilterTags
  sub-components keep working unchanged; updaters resolve against the
  latest committed store state.
- Refactor all 6 list pages to source filters (and FY) from the slice.
- Fix CANFilterButton reference-equality budget check (compare by value,
  not ===) now that budget can originate from the persisted slice.
- Tests: slice listFilters reducers + logout reset; useListFilters hook
  (persist across remount, logout clear, per-page isolation, updater
  form). Update page/hook tests to provide the sessionUI reducer and
  reset session state between cases.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to the Deliverable B commit, fixing defects surfaced in review:

- fix: CANFilterButton full-range selection now clears a persisted budget
  filter (set `budget: []`) instead of retaining the stale prevState value.
  The value-based comparison introduced in Deliverable B made this branch
  reachable, exposing the latent bug. Add a regression test.
- fix: unify fiscal-year-change reset across all FY-aware list pages via a
  new `changeFiscalYear` in useListFilters that dispatches resetListFilters
  + setListFiscalYear. Previously CANs reset nothing and Projects cleared
  only the FY key on FY change; now Agreements, CANs, Projects, and
  Portfolios all consistently reset filters (matching the product decision
  "changing FY still clears other filters"). This also fixes stale
  cross-FY filters persisting after an FY switch, and the Portfolio slider
  bounds recompute correctly on FY change.
- harden: useListFilters selectors/getState now defensively chain
  `state.sessionUI?.listFilters?.[page]?.`, matching Breadcrumb.
- refactor: extract buildPageState() as the single clone source for both
  initial-state construction and resetListFilters (removes duplication);
  activate the previously-unused resetListFilters action; drop the
  AgreementsList shallow-spread aliasing of exported defaults and the
  Portfolio inline-default duplication.
- test: add changeFiscalYear coverage and CANFilterButton budget-clear test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@weimiao67 weimiao67 self-assigned this Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant