Page properties: add Save & close button to the docheader #82

Closed
opened 2026-07-17 23:28:29 +02:00 by dominik.polakovics · 1 comment

What to build

The page-properties mode of the page docheader (props=1) gains a second primary action: Save & close next to the existing Save. Save keeps the form open (save-and-keep-editing, useful with preview); Save & close saves and, on success, returns to page content (clears props). On a 422 validation error both buttons leave the form open with the existing inline field errors. The unsaved-changes guard on plain Close is unchanged.

Key code: web/src/components/PagePane.tsx (props-mode docheader, props-save button, SettingsSaveApi seam), web/src/components/PageSettings.tsx.

Acceptance criteria

  • Props-mode docheader shows Save and Save & close
  • Save & close: successful save returns to page content; failed save (422) stays in the form with inline errors shown
  • Plain Save behavior unchanged (stays in form); Close + dirty still triggers the unsaved-changes dialog
  • Test covers the save-then-close success path and the 422 stay-open path

Blocked by

None - can start immediately

## What to build The page-properties mode of the page docheader (`props=1`) gains a second primary action: **Save & close** next to the existing **Save**. Save keeps the form open (save-and-keep-editing, useful with preview); Save & close saves and, on success, returns to page content (clears `props`). On a 422 validation error both buttons leave the form open with the existing inline field errors. The unsaved-changes guard on plain Close is unchanged. Key code: `web/src/components/PagePane.tsx` (props-mode docheader, `props-save` button, `SettingsSaveApi` seam), `web/src/components/PageSettings.tsx`. ## Acceptance criteria - [ ] Props-mode docheader shows Save and Save & close - [ ] Save & close: successful save returns to page content; failed save (422) stays in the form with inline errors shown - [ ] Plain Save behavior unchanged (stays in form); Close + dirty still triggers the unsaved-changes dialog - [ ] Test covers the save-then-close success path and the 422 stay-open path ## Blocked by None - can start immediately
Author
Owner

This was generated by AI during triage.

Triage addendum — verified against the codebase

Brief confirmed accurate; the change is smaller than it may look. Facts for the implementing agent:

  • The seam is already shaped for this: SettingsSaveApi.save() (web/src/components/PageSettings.tsx ~37–41) returns Promise<boolean>true on successful save, false on a 422 (result.kind === "fields" maps inline field errors) or client-side validation failure. Save & close is essentially: if (await settingsApi()?.save()) setSearchParams({ props: undefined }).
  • The existing Save button is props-save in web/src/components/PagePane.tsx (~1944–1952); Close is props-close (~1937), which just drops the props param. Disable the new button under the same barSaveDisabled() condition.
  • The unsaved-changes guard is not wired in the Close handler — it fires via the shell router (useBeforeLeave in App.tsx ~245, form registered through useGuard in PageSettings.tsx ~223). Save & close clears dirtiness before navigating (a successful save resets fieldsBaseline), so the guard won't fire on the success path — no extra wiring needed. Close + dirty behavior stays untouched.
  • Tests to extend: web/src/test/Docheader.test.tsx describe("props mode") (~229–304, already covers save-stays-open and dirty-Close-guard) and web/src/test/PageSettings.test.tsx (has the 422 inline-error fixture at ~152–168 to reuse for the stay-open path).
> *This was generated by AI during triage.* ## Triage addendum — verified against the codebase Brief confirmed accurate; the change is smaller than it may look. Facts for the implementing agent: - The seam is already shaped for this: `SettingsSaveApi.save()` (`web/src/components/PageSettings.tsx` ~37–41) returns `Promise<boolean>` — `true` on successful save, `false` on a 422 (`result.kind === "fields"` maps inline field errors) or client-side validation failure. Save & close is essentially: `if (await settingsApi()?.save()) setSearchParams({ props: undefined })`. - The existing Save button is `props-save` in `web/src/components/PagePane.tsx` (~1944–1952); Close is `props-close` (~1937), which just drops the `props` param. Disable the new button under the same `barSaveDisabled()` condition. - The unsaved-changes guard is not wired in the Close handler — it fires via the shell router (`useBeforeLeave` in `App.tsx` ~245, form registered through `useGuard` in `PageSettings.tsx` ~223). Save & close clears dirtiness before navigating (a successful save resets `fieldsBaseline`), so the guard won't fire on the success path — no extra wiring needed. Close + dirty behavior stays untouched. - Tests to extend: `web/src/test/Docheader.test.tsx` `describe("props mode")` (~229–304, already covers save-stays-open and dirty-Close-guard) and `web/src/test/PageSettings.test.tsx` (has the 422 inline-error fixture at ~152–168 to reuse for the stay-open path).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/deckle#82
No description provided.