Problem
Settings > General has two explicit-save forms, but neither tracks or protects unsaved work. The timezone and location fields are loaded directly into editable state at client/src/components/settings/GeneralTab.jsx:22 and client/src/components/settings/GeneralTab.jsx:30; subsequent edits happen at client/src/components/settings/GeneralTab.jsx:126 and client/src/components/settings/GeneralTab.jsx:178 with no saved baseline, dirty indicator, or navigation guard anywhere in the component.
Settings tabs are real routes. Selecting another tab calls navigate(target.to) in client/src/components/ui/RouteTabsHeader.jsx:38, and /settings/:tab remounts a different tab through client/src/App.jsx:333 and client/src/pages/Settings.jsx:36. The edited GeneralTab state is therefore discarded immediately.
Trigger
- Open
/settings/general and wait for settings to load.
- Edit the timezone, latitude, or longitude without pressing that section's Save button.
- Select any Settings header tab, use the sidebar / command palette / voice navigation, press Back, reload, or close the browser tab.
- Navigation proceeds with no on-screen unsaved state and the local edit is lost. Returning to General reloads the old persisted value.
Impact
Users can lose carefully entered location or timezone changes without warning. Because the controls use explicit Save buttons and provide no dirty feedback, it is also unclear whether a typed value is already persisted.
Fix
- In
client/src/components/settings/GeneralTab.jsx, keep saved baselines for timezone and both location fields. Derive per-section dirty state and a combined dirty state.
- Advance only the relevant baseline after that section's successful save. A successful timezone save must not clear a still-dirty location edit, and vice versa; failed saves must remain dirty.
- Show a visible
Unsaved changes status beside the affected section's Save action.
- Reuse
client/src/hooks/useUnsavedChangesGuard.js and client/src/components/ui/UnsavedChangesConfirm.jsx for routed navigation and beforeunload, with a discard action that allows the parked navigation to proceed.
- Add
client/src/components/settings/GeneralTab.test.jsx under a data router. Cover dirty indicators, switching a Settings route, keeping versus discarding edits, successful save baseline updates per section, failed saves, and beforeunload arming.
Rejected alternative: auto-save every edit. Coordinates deliberately allow partial text such as - while typing and are validated only on Save (client/src/components/settings/GeneralTab.jsx:9), so auto-save would fight the existing validation and explicit-submit contract.
Dispatch rationale: model:medium fits a routine component-plus-router-test change; effort:high is justified by the two independent baselines and the requirement that saving one section must not clear the other's dirty/blocked state.
Acceptance criteria
Problem
Settings > General has two explicit-save forms, but neither tracks or protects unsaved work. The timezone and location fields are loaded directly into editable state at
client/src/components/settings/GeneralTab.jsx:22andclient/src/components/settings/GeneralTab.jsx:30; subsequent edits happen atclient/src/components/settings/GeneralTab.jsx:126andclient/src/components/settings/GeneralTab.jsx:178with no saved baseline, dirty indicator, or navigation guard anywhere in the component.Settings tabs are real routes. Selecting another tab calls
navigate(target.to)inclient/src/components/ui/RouteTabsHeader.jsx:38, and/settings/:tabremounts a different tab throughclient/src/App.jsx:333andclient/src/pages/Settings.jsx:36. The editedGeneralTabstate is therefore discarded immediately.Trigger
/settings/generaland wait for settings to load.Impact
Users can lose carefully entered location or timezone changes without warning. Because the controls use explicit Save buttons and provide no dirty feedback, it is also unclear whether a typed value is already persisted.
Fix
client/src/components/settings/GeneralTab.jsx, keep saved baselines for timezone and both location fields. Derive per-section dirty state and a combined dirty state.Unsaved changesstatus beside the affected section's Save action.client/src/hooks/useUnsavedChangesGuard.jsandclient/src/components/ui/UnsavedChangesConfirm.jsxfor routed navigation andbeforeunload, with a discard action that allows the parked navigation to proceed.client/src/components/settings/GeneralTab.test.jsxunder a data router. Cover dirty indicators, switching a Settings route, keeping versus discarding edits, successful save baseline updates per section, failed saves, andbeforeunloadarming.Rejected alternative: auto-save every edit. Coordinates deliberately allow partial text such as
-while typing and are validated only on Save (client/src/components/settings/GeneralTab.jsx:9), so auto-save would fight the existing validation and explicit-submit contract.Dispatch rationale:
model:mediumfits a routine component-plus-router-test change;effort:highis justified by the two independent baselines and the requirement that saving one section must not clear the other's dirty/blocked state.Acceptance criteria