# Form Page ## Overall Styling Guideline Form pages create and edit entities. A single shared `` component handles both flows; whether it acts as create or edit is derived from whether a starting value is provided. The form is rendered inside a centered, max-width container. Form fields are grouped into glass-pane cards, one per logical panel. | Component | Path | | ---------- | -------------------------------------------------- | | Glass Pane | `src/lib/components/common/glass-pane/pane.svelte` | | Card | `src/lib/components/common/card/card.svelte` | | Field | `src/lib/components/controls/field/` | | IconPicker | `src/lib/components/forms/icon-picker/` | ## Component Props ```ts type Props = { value?: XFormValue; cancelHref: string; onsubmit: (value?: XFormValue) => void; }; ``` `isEdit` is derived from whether `value` is provided — undefined → create, defined → edit. The edit wrapper converts the loaded entity to a form value via `to{X}FormValue` before passing it in. ## Page Header ``` New {Entity} | Edit {Entity} [Cancel] [Create | Save Changes] {mode} (edit mode only) ``` - Title: `New {Entity}` (create) or `Edit {Entity}` (edit) - Mode subtitle: shown only in edit mode, small capitalized text, reflecting the locked mode - Cancel button: outline, navigates to `cancelHref` - Submit button: primary; label is `Create` (create) or `Save Changes` (edit); shows a spinner and is disabled while submitting The same Cancel + Submit pair is rendered again at the bottom of the form, right-aligned. ## Basic Information Panel The first panel in every form. The icon selector sits on the left as a square sidebar; basic identity fields stack on the right. ``` ┌─────────────────────────────────────────────────────────────┐ │ ┌────────┐ Name │ │ │ icon │ [ ] │ │ │selector│ Mode │ │ └────────┘ [External | Managed] │ │ …entity-specific fields… │ └─────────────────────────────────────────────────────────────┘ ``` - **Icon**: `` over the entity's icon registry - **Name**: text, maxlength 64 - **Mode** (entities with both `external` and `managed` variants): segmented toggle — `External` | `Managed`. Disabled in edit mode (mode is fixed once a resource is created). A description below the toggle explains what each mode means and updates as the user toggles. Entity-specific basic fields (description, type selectors, etc.) follow underneath. Optional sub-sections may be nested via ``. ## Submit `onsubmit` is invoked when the submit button is clicked. The form itself does not navigate or surface errors — the wrapper page is expected to call the API, navigate to the entity's detail page on success, and surface failures via the notification store. Each entity provides a `validate{X}FormValue` helper in its `types.ts` that converts a form value to the entity type and returns a list of validation error messages. ## Wrapper Files Each form is mounted via thin route wrappers: - `routes/{entity}/new/+page.svelte` — create wrapper, sets the breadcrumb to `[New {Entity}]` - `routes/{entity}/[id]/edit/+page.server.ts` — loads the entity (404 if not found) - `routes/{entity}/[id]/edit/+page.svelte` — edit wrapper, converts the loaded entity via `to{X}FormValue`, passes it to the form, and sets the breadcrumb to the entity name ## Page Name - Create: `New {Entity} - LocoStack` - Edit: `Edit {entity.name} - LocoStack`