Problem
Each entity list page (Channels, Zones, Talk groups, Contacts, RX group lists) has a DataTable with filters, sort, and optional columns. Switching between pages — or returning from bulk operations, detail views, or edit flows — resets some of that UI state, which makes repeated list work tedious.
What persists today:
URL params only survive while the route keeps them. Navigating to /channels/abc and back to /channels drops ?q=, filters, and sort mode. Column header sort is never persisted at all.
Intended outcome
Persist the most recent list UI state per entity in browser localStorage, restored when the operator returns to that list (even without URL params).
State to persist (per list)
| Key |
Channels |
Zones / TG / Contacts / RGL |
Name search (q) |
✓ |
✓ |
| Column header sort (key + direction) |
✓ |
✓ |
| Band / mode / duplex filters |
✓ |
— |
Sort mode (name | distance) |
✓ |
— |
| Distance filter enabled + max km |
✓ |
— |
| Visible optional columns |
✓ (already) |
— (future if column picker added) |
Behaviour
- Restore on mount: when the list route loads with no relevant URL params, hydrate from
localStorage defaults for that list.
- URL wins on explicit navigation: if the operator lands with query params (shared link, bookmark), URL values take precedence over stored prefs for that visit.
- Write-through: every change to filter/sort/column state updates
localStorage (debounce text search if needed).
- Per-project scope: keys should include active project id (or a stable project slug) so switching codeplugs does not leak list prefs across projects.
- Register new keys in
storageKeyRegistry.ts for the debug localStorage viewer.
Implementation sketch
- Introduce a small hook, e.g.
usePersistedListQuery(storageKey, defaults) or extend existing query hooks with a localStorage backing layer.
- Channels: extend
useChannelListQuery + column sort state in channels/list.tsx.
- Other lists: extend
useListNameQuery and wire column sort from each list route's DataTable.
- Keep URL params as an optional shareable overlay — do not remove them.
Affected
src/hooks/useChannelListQuery.ts, useListNameQuery.ts (or new shared hook)
src/routes/channels/list.tsx, zones/list.tsx, ContactsList.tsx, TalkGroupsList.tsx, RxGroupListsList.tsx
src/lib/debug/storageKeyRegistry.ts
docs/features/ui/README.md — document persistence contract
Related
Out of scope
- Persisting operator geolocation (session-only per #70).
- Cross-browser sync (cloud storage is a separate initiative).
- Persisting row selection for bulk actions (future groundwork only).
Manual verify
- Channels list: set name search, band filter, distance sort, column header sort, optional columns → navigate to a channel detail → back → all settings restored.
- Reload page on
/channels with no query string → stored prefs applied.
- Open
/channels?q=GB7 directly → URL q wins; changing search updates storage.
- Switch active project → each project retains its own list prefs.
- Zones (and one other entity list): name filter + column sort survive navigation.
- Debug localStorage viewer lists the new keys.
Workflow note
Branch from origin/main, atomic conventional commits (shared hook → channels → other lists → registry/docs), PR linking Closes #.
Problem
Each entity list page (Channels, Zones, Talk groups, Contacts, RX group lists) has a
DataTablewith filters, sort, and optional columns. Switching between pages — or returning from bulk operations, detail views, or edit flows — resets some of that UI state, which makes repeated list work tedious.What persists today:
localStorageviauseDataTableColumnVisibility/loadChannelVisibleColumns?q=viauseChannelListQuery?q=viauseListNameQueryuseChannelListQuerychannels/list.tsx(columnSortOverride) — lost on navigationURL params only survive while the route keeps them. Navigating to
/channels/abcand back to/channelsdrops?q=, filters, and sort mode. Column header sort is never persisted at all.Intended outcome
Persist the most recent list UI state per entity in browser
localStorage, restored when the operator returns to that list (even without URL params).State to persist (per list)
q)name|distance)Behaviour
localStoragedefaults for that list.localStorage(debounce text search if needed).storageKeyRegistry.tsfor the debug localStorage viewer.Implementation sketch
usePersistedListQuery(storageKey, defaults)or extend existing query hooks with alocalStoragebacking layer.useChannelListQuery+ column sort state inchannels/list.tsx.useListNameQueryand wire column sort from each list route'sDataTable.Affected
src/hooks/useChannelListQuery.ts,useListNameQuery.ts(or new shared hook)src/routes/channels/list.tsx,zones/list.tsx,ContactsList.tsx,TalkGroupsList.tsx,RxGroupListsList.tsxsrc/lib/debug/storageKeyRegistry.tsdocs/features/ui/README.md— document persistence contractRelated
Out of scope
Manual verify
/channelswith no query string → stored prefs applied./channels?q=GB7directly → URLqwins; changing search updates storage.Workflow note
Branch from
origin/main, atomic conventional commits (shared hook → channels → other lists → registry/docs), PR linkingCloses #.