feat: shared backlog filter popover + sprint header polish (v1.3.3) (#184)

- Move sprint switcher into sprint header, centered between title and actions
- Extract BacklogFilterPopover as shared component used by sprint and product backlog
- Add sort options (code/priority/status) with single-pill asc/desc toggle
- Default sprint backlog status filter to OPEN, remove "alleen niet klaar" button
- Persist collapsed state and filter popover open in localStorage
- Fix hydration flicker: defer localStorage read to useEffect with prefsLoaded gate for writes
- Increase sprint switcher text size for readability

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-05-10 11:12:04 +02:00 committed by GitHub
parent a9b53dedf0
commit 1f8cbacb0a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 424 additions and 330 deletions

View file

@ -0,0 +1,22 @@
/**
* SSR-safe synchronous read of a localStorage value with a typed parser.
*
* Use inside `useState(() => readLocalStoragePref(...))` so the first render
* already has the persisted value no useEffect-driven re-render flicker.
*
* On the server `window` is undefined returns `fallback`. On the client the
* raw value is parsed; if the parser returns `null` the fallback is used.
* Hydration mismatches between server-rendered HTML (default) and the
* client-rendered tree (persisted) are accepted: React adapts the DOM in the
* same hydration pass without a visible flicker for matching values.
*/
export function readLocalStoragePref<T>(
key: string,
parse: (raw: string) => T | null,
fallback: T,
): T {
if (typeof window === 'undefined') return fallback
const raw = window.localStorage.getItem(key)
if (raw === null) return fallback
return parse(raw) ?? fallback
}