Editor: form screen with General · Appearance · Access tabs, validation at rest, one unsaved-changes guard #172

Closed
opened 2026-10-08 21:46:14 +02:00 by dominik.polakovics · 0 comments

Parent

#168 — the editing screens overhaul, Direction A (page module). Mockup and decision log: docs/mockups/editing-screens/ (README item 8; in dist/deckle-page-module.html click any card), merged in #169.

Summary

Rebuild the element edit screen and the page properties screen as one FormScreen = Docheader + ElementForm | PageForm: TYPO3's tab set General · Appearance · Access, validation visible at rest, one unsaved-changes guard behind every way out, Save / Save & close / Close / Preview in the docheader, ⌘S. The same FieldGroup and Field widgets serve both forms and, later, the feature modules' entry editor.

Current behavior

  • /edit/<lang>/<page>/<uid> renders EditPane.tsx: Close / Save / "Save & Close", the element icon and label, "Saved", and the server-described form (POST /api/form, web/src/api/forms.ts, widgets in web/src/widgets/) capped at 820px, with no tabs; fields are checked when they lose focus (#164) and row messages move with their row; number fields keep what the user types.
  • Page properties are PageSettings.tsx behind ?props=1 (the page-module issue turns that into a docheader tab pointing at the same component).
  • The unsaved-changes guard (guard.ts, UnsavedChangesDialog) is wired per navigation site; wording differs ("Save & Close" vs "Save & close"); ✎ ⋮ × are text glyphs next to lucide icons.
  • The Enabled switch exists as a form field (first issue) but has no dedicated place.

Desired behavior

Everything below is shown in the mockup; where this text and the mockup differ, the mockup wins.

Docheader of an element form: row 1 left: × Close, Save (primary), Save & close, | Preview; right: Enable/Disable, Duplicate, ⋯ (Cut, Copy, Move up/down, Delete…, from the shared Menu). Row 2: crumbs Home / Services / Main (the last crumb is the slot the element lives in), the element's icon, label, #uid, and a status: "Saved just now" or an Unsaved changes badge as soon as any field changes. Preview opens the View module with the element highlighted and scrolled into view.

Form: tabs General · Appearance · Access, sticky under the docheader, max width ~880px, left-aligned.

  • General: the element's content fields in a FieldGroup "Content", then one FieldGroup per items collection (title = the collection label, hint "N of up to M" if a limit exists).
  • Appearance: the element's appearance fields in up to three columns, or a one-line note that the element has none.
  • Access: Enabled switch and the read-only Anchor (#uid, hint "Link to this element with #uid.").
  • Which tab a sidecar param belongs to comes from a new optional param key tab: general | appearance (default general). scaffold/site/AGENTS.md documents it; the scaffold assigns side, cols, lightbox, variant-style params to appearance.
  • Items collection rows: collapsible, index, thumbnail, summary, actions (move up/down, enable/disable, remove), the first row open; expanding shows the item's fields in two columns.
  • Field widgets keep today's behaviour (web/src/widgets/); layout is FieldGroup → Field (label, required marker, control, hint or error).

Validation at rest: the required/format checks the server already runs on focus-lost are also shown for the loaded state: a field with a problem shows its message; a collapsed item row shows a chip naming the missing fields ("Alternative text missing"); a tab containing a problem carries a red dot. Save stays available; the server's save response decides (today's contract).

Unsaved-changes guard: one leaveForm() path used by Close, Esc, the Content/Properties tabs, the tree, the module menu, the host navigation and browser navigation, showing UnsavedChangesDialog with Stay / Discard changes / Save & continue. ⌘/Ctrl+S saves. Esc closes the form only when no menu, dialog or notes layer is open (the #140 rule).

Page properties use the same screen: docheader Close / Save / Save & close with the Content/Properties tabs; form tabs General (title, description, menu title, …: the page type's fields), Appearance (backend layout, layout for subpages, and page-type fields marked tab: appearance), Access (Enabled, the page type's navigation flags such as hidden, read-only path).

Wording: "Save & close" everywhere; icons from lucide only.

Decisions

  • Tabs General · Appearance · Access, fixed set, in that order — TYPO3's form engine, which the maintainer wants the editor to feel like (README item 8); a sidecar can only choose between general and appearance.
  • Validation is visible at rest, not only after focus-lost — a problem on a hidden tab must be visible; the red dot on the tab is the minimum.
  • One guard function for every exit — today's per-site wiring is what let wording and coverage drift.
  • Forms replace the main area (no side panel, no sheet) — Direction A was chosen over B and C for simplicity and form width.
  • Full-width forms and sticky tabs — item collections and tables need the room.

Key interfaces

  • FormDescription/FormField (web/src/api/forms.ts, server side in internal/element/form.go and the properties/create description) — the tab marker per field; the Enabled field from the first issue lands on Access.
  • internal/element/schema.go — the param tab key; scaffold/site/AGENTS.md; internal/element/agentsdoc_test.go.
  • web/src/components/EditPane.tsx, PageSettings.tsx, FormPane.tsx (legacy) → FormScreen, ElementForm, PageForm, FieldGroup, Field, using Docheader, Tabs, Badge, Menu from the page-module issue.
  • guard.ts (GuardStore, DirtySource, unguarded), UnsavedChangesDialog, leave.ts, returnnav.ts — the single leaveForm().
  • bridge.ts / PreviewPane — Preview opens the View module at the element (the highlight message exists; the scroll-to may not).
  • EntryEditor.tsx (feature modules) — its Properties tab should be able to adopt PageForm later; do not rebuild it here, but do not make that impossible.

Acceptance criteria

  • Opening an element shows the docheader (Close, Save, Save & close, Preview; Enable/Disable, Duplicate, ⋯) with crumbs ending in the slot name, and tabs General · Appearance · Access.
  • A param with tab: appearance renders on Appearance; others on General; Enabled and Anchor on Access; AGENTS.md documents tab and the drift test passes.
  • Loading an element whose item row lacks a required field shows the field message when the row is expanded, a chip on the collapsed row, and a red dot on General.
  • Changing any field flips the status to "Unsaved changes"; Close, Esc, the Content tab, a tree click, a module click and a browser back each show Stay / Discard / Save & continue; Save & continue saves and proceeds.
  • ⌘/Ctrl+S saves; Esc with a menu or dialog open closes only that layer.
  • Preview opens the View module with the element highlighted and scrolled into view.
  • Page properties render through the same screen with General / Appearance / Access and the Enabled switch on Access.
  • "Save & Close" no longer appears; FormPane.tsx and its only test are gone.

How to verify

  • make test; tests for tab assignment, validation-at-rest rendering, the guard covering each exit, and the Esc layer rule.
  • make run on testdata/site: open Text & media on a page, compare with the mockup form (General, Appearance, Access), break a required item field, switch tabs, try every exit with unsaved changes.

Assumptions to check

  • Whether POST /api/form reports problems for the loaded state or only POST /api/form/check per field; if only the latter, the screen runs the checks on load.
  • Whether the preview bridge can scroll to a uid today (bridge.ts, previewmap.ts) or only highlight.
  • Whether FormDescription groups fields (first issue) in a way that maps to tabs, or the tab marker must be added here.
  • Browser-navigation guarding (useBeforeLeave, leave.ts) already exists for some routes; confirm which exits are uncovered today rather than assuming.

Out of scope

  • The page-module content screen, insert dialog, drag and drop, clipboard (previous issue).
  • Rebuilding the feature modules' EntryEditor and the create dialog (they may adopt the components later).
  • New field types or RTE features; the item-collection media picker.
  • An inspector or sheet variant of the form (Directions B and C were rejected).

Blocked by

## Parent #168 — the editing screens overhaul, Direction A (page module). Mockup and decision log: `docs/mockups/editing-screens/` (README item 8; in `dist/deckle-page-module.html` click any card), merged in #169. ## Summary Rebuild the element edit screen and the page properties screen as one `FormScreen = Docheader + ElementForm | PageForm`: TYPO3's tab set General · Appearance · Access, validation visible at rest, one unsaved-changes guard behind every way out, Save / Save & close / Close / Preview in the docheader, ⌘S. The same `FieldGroup` and `Field` widgets serve both forms and, later, the feature modules' entry editor. ## Current behavior - `/edit/<lang>/<page>/<uid>` renders `EditPane.tsx`: Close / Save / "Save & Close", the element icon and label, "Saved", and the server-described form (`POST /api/form`, `web/src/api/forms.ts`, widgets in `web/src/widgets/`) capped at 820px, with no tabs; fields are checked when they lose focus (#164) and row messages move with their row; number fields keep what the user types. - Page properties are `PageSettings.tsx` behind `?props=1` (the page-module issue turns that into a docheader tab pointing at the same component). - The unsaved-changes guard (`guard.ts`, `UnsavedChangesDialog`) is wired per navigation site; wording differs ("Save & Close" vs "Save & close"); ✎ ⋮ × are text glyphs next to lucide icons. - The Enabled switch exists as a form field (first issue) but has no dedicated place. ## Desired behavior Everything below is shown in the mockup; where this text and the mockup differ, the mockup wins. **Docheader of an element form**: row 1 left: × Close, **Save** (primary), Save & close, | **Preview**; right: Enable/Disable, Duplicate, ⋯ (Cut, Copy, Move up/down, Delete…, from the shared `Menu`). Row 2: crumbs *Home / Services / Main* (the last crumb is the slot the element lives in), the element's icon, label, `#uid`, and a status: "Saved just now" or an **Unsaved changes** badge as soon as any field changes. **Preview** opens the View module with the element highlighted and scrolled into view. **Form**: tabs **General · Appearance · Access**, sticky under the docheader, max width ~880px, left-aligned. - General: the element's content fields in a `FieldGroup` "Content", then one `FieldGroup` per items collection (title = the collection label, hint "N of up to M" if a limit exists). - Appearance: the element's appearance fields in up to three columns, or a one-line note that the element has none. - Access: **Enabled** switch and the read-only **Anchor** (`#uid`, hint "Link to this element with #uid."). - Which tab a sidecar param belongs to comes from a new optional param key `tab: general | appearance` (default general). `scaffold/site/AGENTS.md` documents it; the scaffold assigns `side`, `cols`, `lightbox`, `variant`-style params to appearance. - Items collection rows: collapsible, index, thumbnail, summary, actions (move up/down, enable/disable, remove), the first row open; expanding shows the item's fields in two columns. - Field widgets keep today's behaviour (`web/src/widgets/`); layout is `FieldGroup` → `Field` (label, required marker, control, hint or error). **Validation at rest**: the required/format checks the server already runs on focus-lost are also shown for the loaded state: a field with a problem shows its message; a collapsed item row shows a chip naming the missing fields ("Alternative text missing"); a tab containing a problem carries a red dot. Save stays available; the server's save response decides (today's contract). **Unsaved-changes guard**: one `leaveForm()` path used by Close, Esc, the Content/Properties tabs, the tree, the module menu, the host navigation and browser navigation, showing `UnsavedChangesDialog` with **Stay / Discard changes / Save & continue**. ⌘/Ctrl+S saves. Esc closes the form only when no menu, dialog or notes layer is open (the #140 rule). **Page properties** use the same screen: docheader Close / Save / Save & close with the Content/Properties tabs; form tabs General (title, description, menu title, …: the page type's fields), Appearance (backend layout, layout for subpages, and page-type fields marked `tab: appearance`), Access (Enabled, the page type's navigation flags such as `hidden`, read-only path). **Wording**: "Save & close" everywhere; icons from lucide only. ## Decisions - Tabs General · Appearance · Access, fixed set, in that order — TYPO3's form engine, which the maintainer wants the editor to feel like (README item 8); a sidecar can only choose between general and appearance. - Validation is visible at rest, not only after focus-lost — a problem on a hidden tab must be visible; the red dot on the tab is the minimum. - One guard function for every exit — today's per-site wiring is what let wording and coverage drift. - Forms replace the main area (no side panel, no sheet) — Direction A was chosen over B and C for simplicity and form width. - Full-width forms and sticky tabs — item collections and tables need the room. ## Key interfaces - `FormDescription`/`FormField` (`web/src/api/forms.ts`, server side in `internal/element/form.go` and the properties/create description) — the tab marker per field; the Enabled field from the first issue lands on Access. - `internal/element/schema.go` — the param `tab` key; `scaffold/site/AGENTS.md`; `internal/element/agentsdoc_test.go`. - `web/src/components/EditPane.tsx`, `PageSettings.tsx`, `FormPane.tsx` (legacy) → `FormScreen`, `ElementForm`, `PageForm`, `FieldGroup`, `Field`, using `Docheader`, `Tabs`, `Badge`, `Menu` from the page-module issue. - `guard.ts` (`GuardStore`, `DirtySource`, `unguarded`), `UnsavedChangesDialog`, `leave.ts`, `returnnav.ts` — the single `leaveForm()`. - `bridge.ts` / `PreviewPane` — Preview opens the View module at the element (the highlight message exists; the scroll-to may not). - `EntryEditor.tsx` (feature modules) — its Properties tab should be able to adopt `PageForm` later; do not rebuild it here, but do not make that impossible. ## Acceptance criteria - [ ] Opening an element shows the docheader (Close, Save, Save & close, Preview; Enable/Disable, Duplicate, ⋯) with crumbs ending in the slot name, and tabs General · Appearance · Access. - [ ] A param with `tab: appearance` renders on Appearance; others on General; Enabled and Anchor on Access; AGENTS.md documents `tab` and the drift test passes. - [ ] Loading an element whose item row lacks a required field shows the field message when the row is expanded, a chip on the collapsed row, and a red dot on General. - [ ] Changing any field flips the status to "Unsaved changes"; Close, Esc, the Content tab, a tree click, a module click and a browser back each show Stay / Discard / Save & continue; Save & continue saves and proceeds. - [ ] ⌘/Ctrl+S saves; Esc with a menu or dialog open closes only that layer. - [ ] Preview opens the View module with the element highlighted and scrolled into view. - [ ] Page properties render through the same screen with General / Appearance / Access and the Enabled switch on Access. - [ ] "Save & Close" no longer appears; `FormPane.tsx` and its only test are gone. ## How to verify - `make test`; tests for tab assignment, validation-at-rest rendering, the guard covering each exit, and the Esc layer rule. - `make run` on `testdata/site`: open Text & media on a page, compare with the mockup form (General, Appearance, Access), break a required item field, switch tabs, try every exit with unsaved changes. ## Assumptions to check - Whether `POST /api/form` reports problems for the loaded state or only `POST /api/form/check` per field; if only the latter, the screen runs the checks on load. - Whether the preview bridge can scroll to a uid today (`bridge.ts`, `previewmap.ts`) or only highlight. - Whether `FormDescription` groups fields (first issue) in a way that maps to tabs, or the tab marker must be added here. - Browser-navigation guarding (`useBeforeLeave`, `leave.ts`) already exists for some routes; confirm which exits are uncovered today rather than assuming. ## Out of scope - The page-module content screen, insert dialog, drag and drop, clipboard (previous issue). - Rebuilding the feature modules' `EntryEditor` and the create dialog (they may adopt the components later). - New field types or RTE features; the item-collection media picker. - An inspector or sheet variant of the form (Directions B and C were rejected). ## Blocked by - #171
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#172
No description provided.