Skip to content

Settings General discards unsaved timezone and location edits on navigation #5480

Description

@atomantic

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

  1. Open /settings/general and wait for settings to load.
  2. Edit the timezone, latitude, or longitude without pressing that section's Save button.
  3. Select any Settings header tab, use the sidebar / command palette / voice navigation, press Back, reload, or close the browser tab.
  4. 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

  • Editing timezone or either coordinate visibly marks the corresponding section unsaved.
  • Any route navigation away from a dirty General tab is parked behind the shared inline discard confirmation.
  • Browser reload/close is guarded while either section is dirty.
  • Saving one section clears only that section's dirty state after server success.
  • A failed save leaves the section dirty and the navigation guard armed.
  • Returning all fields to their saved values clears the indicator and guard without requiring a save.
  • Data-router component tests cover both forms and both confirmation choices.

Metadata

Metadata

Assignees

Labels

area:uiUI components and stylingeffort:highDispatch reasoning effort: highmodel:mediumModel size: mediumplanTracked by /do:replanuxUser experience

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions