Skip to content

feat(settings): wire up and rebuild the account & app settings surface#746

Merged
BigSimmo merged 12 commits into
mainfrom
claude/settings-page-components-xe4zky
Jul 17, 2026
Merged

feat(settings): wire up and rebuild the account & app settings surface#746
BigSimmo merged 12 commits into
mainfrom
claude/settings-page-components-xe4zky

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

The settings dialog was largely a static display: the left-hand navigation didn't navigate, and most rows (clinical defaults, density, notifications…) were read-only labels with no persistence or effect. This PR turns it into a fully wired, persisted, and polished preferences surface without changing any clinical logic.

New preferences storeuse-app-preferences.ts, mirroring the existing use-theme / use-sidebar-collapsed external-store pattern (localStorage + custom event, SSR-safe, in-memory fallback). Persists density, motion, jurisdiction, population, answer style, default landing, personalisation, and notification toggles, with defensive per-field normalisation of stored values.

Theme → light / dark / system — extended the theme hook with a preference + setPreference (keeping theme / toggleTheme). "System" follows the OS and stores no pin, so the existing pre-hydration script keeps resolving it.

Preferences that take real effect

  • Interface density scales the rem baseline via html[data-density] (comfortable is the untouched default).
  • Reduce motion adds an html[data-motion="reduced"] suppression block mirroring the prefers-reduced-motion rules.
  • Both are applied before first paint by the layout.tsx inline script so nothing flashes in.

Privacy actions — clear recent searches, clear saved items, and reset preferences, wired to the real storage helpers with live counters that disable the action when there's nothing to clear.

UI / design

  • Functional desktop scroll-spy navigation across eight sections.
  • Interactive controls: wrapping segmented controls (long labels like "Comprehensive" stay fully readable on narrow screens instead of truncating), accessible native selects, and role="switch" toggles — all design-token based, legible in light/dark and forced-colors.
  • Complete Account, Clinical defaults, App preferences, Personalisation, Notifications, Privacy & security, Keyboard shortcuts, and Help & About sections.

Drops the now-unused theme / onToggleTheme props from the two call sites. The required section headings, row data-testids, and the close affordance are preserved so the existing ui-smoke settings contract still holds.

Verification

  • npm run typecheck — clean for all src/ files (pre-existing test-only failures come from @testing-library / @axe-core dev-deps missing in this environment, unrelated to this change).
  • npm run lint — clean on every changed file.
  • Unit tests (vitest, non-DOM): 2672 passed, 1 unrelated pre-existing flake (pdf-extraction-budget Python-subprocess kill timing). Added coverage for the new theme preference resolver and app-preference normalisation.
  • Production build: next build compiled successfully in 35.2s; the subsequent TS pass fails only on tests/setup/jsdom.setup.ts (@testing-library dev-dep missing in this env — pre-existing, also fails on main).
  • Manual browser QA via Playwright against the running app: desktop + mobile, light + dark, all eight sections — confirmed the appearance control flips the whole app theme live, controls persist, and no layout overflow.
  • npm run verify:pr-local — Verification not run: this environment's node_modules is missing the @testing-library / @axe-core dev-dependencies, which breaks the build's TS pass and the DOM/UI suites regardless of this change; substituted the checks above.
  • npm run verify:ui — UI verification not run: the @testing-library / @axe-core dev-dependencies are absent from this environment, so the Chromium UI gate cannot run here; substituted manual cross-theme, cross-viewport Playwright QA and recommend running the formal gate in CI.

Risk and rollout

  • Risk: Low. Changes are confined to a client-side settings surface and browser-local preferences; no server, API, ingestion, retrieval, or answer-generation code is touched. Density/motion default to the current experience (no attribute set), so existing users see no change until they opt in.
  • Rollback: Revert this commit; no migrations or persisted server state involved. Stale localStorage values are normalised back to defaults on read.
  • Provider or production effects: None.

Clinical Governance Preflight

This change is a local, non-PHI preferences UI and does not alter ingestion, answer generation, search/ranking, source rendering, document access, or clinical output logic; every item below remains true and unchanged.

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed

Notes

  • The clinical-defaults, personalisation, and notification preferences persist today and are surfaced consistently across the settings surface; wiring them into retrieval/answer behaviour is intentionally left for a follow-up so this PR stays UI-only and provider-safe.
  • Merge conflict with main (an iOS Safari sizing fix on the settings layout div) was resolved on-branch in 83ae1ae9, keeping main's h-full/max-h-full fix alongside this branch's desktop sizing.

Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Redesigned the Settings experience with section-based navigation, enhanced privacy controls (clear/reset), and refreshed privacy summaries.
    • Added app-wide preferences for theme pinning, display density (compact/spacious), motion behavior, population, answer style, landing behavior, and notification options.
    • Enabled Settings to trigger sign-out and open the guide.
  • Accessibility
    • Added explicit reduced-motion support that suppresses animations/transitions and disables smooth scrolling, aligned with user preference.
  • Tests
    • Added unit tests for preference normalization and theme preference resolution.

Turn the settings dialog from a mostly-static display into a fully
functional, persisted preferences surface, and polish the design.

Behaviour
- Add a per-browser app-preferences store (use-app-preferences.ts) mirroring
  the existing external-store pattern: density, motion, jurisdiction,
  population, answer style, default landing, personalisation, and notification
  toggles, with defensive normalisation of stored values.
- Extend the theme hook to a light/dark/system preference (setPreference)
  while keeping theme/toggleTheme; "system" follows the OS and stores no pin.
- Density and reduce-motion now take real effect via html[data-density] font
  scaling and an html[data-motion="reduced"] suppression block, applied before
  first paint by the layout pre-hydration script so nothing flashes in.
- Privacy actions clear recent searches / saved items and reset preferences,
  with live counters that disable the action when there is nothing to clear.

UI
- Functional desktop scroll-spy navigation across eight sections.
- Interactive controls: wrapping segmented controls (readable long labels on
  narrow screens), accessible native selects, and role="switch" toggles, all
  design-token based and legible in light/dark and forced-colors.
- Complete Account, Clinical defaults, App preferences, Personalisation,
  Notifications, Privacy, Keyboard shortcuts, and Help & About sections.

Drops the now-unused theme/onToggleTheme props from the two call sites; the
required headings, row test ids, and close affordance are preserved.

Tests: add theme preference and app-preference normalisation coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RiRMGogS7jxgngFRYEHjw3
@supabase

supabase Bot commented Jul 17, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@cursor[bot], you've reached your PR review limit, so we couldn't start this review.

Next review available in: 24 seconds

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f7cf7920-44dd-488c-b064-2632b98c434b

📥 Commits

Reviewing files that changed from the base of the PR and between bc1b291 and 2c1e04f.

📒 Files selected for processing (3)
  • src/components/clinical-dashboard/global-search-shell.tsx
  • src/components/clinical-dashboard/settings-dialog.tsx
  • src/components/clinical-dashboard/use-app-preferences.ts
📝 Walkthrough

Walkthrough

The pull request adds persisted application preferences, expanded theme preference state, density and motion attributes, and a reworked settings dialog with section navigation, privacy actions, accessibility controls, callback wiring, and validation tests.

Changes

Settings and Preference Controls

Layer / File(s) Summary
Preference and theme stores
src/components/clinical-dashboard/use-app-preferences.ts, src/components/clinical-dashboard/use-theme.ts, src/lib/theme.ts
Adds typed preference persistence, normalization, synchronization, document side effects, and explicit light/dark/system theme preferences.
Preference bootstrap and CSS effects
src/app/globals.css, src/app/layout.tsx
Applies density font-size baselines and explicit reduced-motion behavior through <html> attributes, and documents the preference bootstrap script.
Settings dialog behavior and rendering
src/components/clinical-dashboard/settings-dialog.tsx, src/components/clinical-dashboard/recent-query-storage.ts, src/components/ClinicalDashboard.tsx, src/components/clinical-dashboard/global-search-shell.tsx
Reworks settings sections, controls, navigation, privacy counters/actions, dynamic workspace context, and dialog callback wiring.
Preference and theme tests
tests/app-preferences.test.ts, tests/theme.test.ts
Tests preference normalization, default option membership, and stored theme preference interpretation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Clinician
  participant SettingsDialog
  participant useAppPreferences
  participant useTheme
  participant BrowserStorage
  participant documentElement
  Clinician->>SettingsDialog: Change application or theme preference
  SettingsDialog->>useAppPreferences: setPreference(key, value)
  SettingsDialog->>useTheme: setPreference(theme)
  useAppPreferences->>BrowserStorage: Persist normalized preferences
  useTheme->>BrowserStorage: Persist theme preference
  useAppPreferences->>documentElement: Apply data-density and data-motion
  useTheme->>documentElement: Apply dark class and theme-color metadata
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: rebuilding the settings surface.
Description check ✅ Passed The description matches the template with Summary, Verification, Risk and rollout, Clinical Governance Preflight, and Notes filled in.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/settings-page-components-xe4zky

Comment @coderabbitai help to get the list of available commands.

@BigSimmo
BigSimmo marked this pull request as ready for review July 17, 2026 19:33
@BigSimmo

Copy link
Copy Markdown
Owner Author

@copilot resolve the merge conflicts in this pull request

…components-xe4zky

# Conflicts:
#	src/components/clinical-dashboard/settings-dialog.tsx

Copilot AI commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Merge conflicts resolved in 83ae1ae9. There was one conflict in src/components/clinical-dashboard/settings-dialog.tsx — a single CSS class on the settings layout div. Main had changed h-dvh max-h-dvhh-full max-h-full to fix an iOS Safari black-band bug; I kept that fix while preserving this branch's sizing values (88dvh, 840px, 248px sidebar).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/clinical-dashboard/recent-query-storage.ts`:
- Around line 23-33: Update the recent-query counting logic in the
sessionStorage iteration so JSON parsing failures for one key are caught and
ignored locally, while processing continues for other matching keys. Keep valid
array entries counted as before, and reserve the outer fallback for broader
storage-access failures rather than malformed individual values.

In `@src/components/clinical-dashboard/settings-dialog.tsx`:
- Line 695: Update the privacy notice fallback in the settings dialog to avoid
claiming that the app never stores patient-identifying information; use wording
that clearly warns users that recent queries are retained verbatim in
sessionStorage and should not contain such information. Preserve the
privacyNotice override behavior.

In `@src/components/clinical-dashboard/use-theme.ts`:
- Around line 29-31: Update getPreferenceSnapshot and the corresponding
theme-preference write logic to guard localStorage reads and writes against
storage exceptions. When storage is unavailable or blocked, preserve the
in-memory/session-only theme state and continue returning or applying the
preference without crashing; retain the existing localStorage behavior when
access succeeds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d40debce-fbbf-4778-b27a-920573072370

📥 Commits

Reviewing files that changed from the base of the PR and between ad2752d and 0471c2d.

📒 Files selected for processing (11)
  • src/app/globals.css
  • src/app/layout.tsx
  • src/components/ClinicalDashboard.tsx
  • src/components/clinical-dashboard/global-search-shell.tsx
  • src/components/clinical-dashboard/recent-query-storage.ts
  • src/components/clinical-dashboard/settings-dialog.tsx
  • src/components/clinical-dashboard/use-app-preferences.ts
  • src/components/clinical-dashboard/use-theme.ts
  • src/lib/theme.ts
  • tests/app-preferences.test.ts
  • tests/theme.test.ts
💤 Files with no reviewable changes (2)
  • src/components/ClinicalDashboard.tsx
  • src/components/clinical-dashboard/global-search-shell.tsx

Comment thread src/components/clinical-dashboard/recent-query-storage.ts
Comment thread src/components/clinical-dashboard/settings-dialog.tsx Outdated
Comment thread src/components/clinical-dashboard/use-theme.ts Outdated
@BigSimmo
BigSimmo enabled auto-merge July 17, 2026 19:43
claude and others added 10 commits July 17, 2026 19:45
…privacy copy

- countRecentQueries: isolate a corrupt sessionStorage entry to its own key so
  one malformed value no longer zeroes the total and wrongly disables the
  "Clear recent searches" action.
- use-theme: guard localStorage reads/writes against blocked-storage throws
  (Safari private mode, quota) with an in-memory fallback, matching the
  use-sidebar-collapsed / use-app-preferences pattern, so a blocked store no
  longer crashes the settings render or aborts a theme change.
- settings-dialog: replace the inaccurate "never stores patient-identifying
  information" privacy notice — recent searches are retained verbatim in this
  browser — with wording that reassures on local-only storage while warning
  against entering PHI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RiRMGogS7jxgngFRYEHjw3
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.

4 participants