Skip to content

feat(customization): PageBuilder named page variants + activation (ADR-0001)#167

Merged
windischb merged 1 commit into
developfrom
feat/page-builder-variants
Jul 22, 2026
Merged

feat(customization): PageBuilder named page variants + activation (ADR-0001)#167
windischb merged 1 commit into
developfrom
feat/page-builder-variants

Conversation

@windischb

Copy link
Copy Markdown
Contributor

What

Reworks the PageBuilder into a named-variant + activation model (ADR-0001), decoupling three concepts the initial single-schema design (#166) conflated: a schema existsit is activethe built-in fixed view.

Motivated by field testing on auth-test.cocoar.dev, which surfaced three UX traps:

  • "Reset to default" was destructive — it deleted server-side, then repopulated the editor with a template and left Save enabled, so users kept re-creating a custom page and could never get back to the built-in login.
  • No way to deactivate without deleting an elaborate custom page.
  • Only one page per slot — operators want several variants and to pick which is live per realm and per app.

Model

Per slot (login / logout / password-forgot) a realm/app owns a library of named variants plus an active selection:

  • Realm: { Variants[], ActiveVariantId? }null ⇒ built-in.
  • App: { Variants[], InheritActive, ActiveVariantId? } — inherit realm, or override to built-in / an app variant.
  • Effective resolution: app → realm → built-in.

Activation is a settings decision: the Pages overview has an Active for realm selector per slot (and badges which variant is live so the edit blast-radius is visible); apps set Active for this app in Settings → Pages. "Reset to default" in the editor is now a UI-only buffer load — nothing persists until Save, and Save creates/updates a variant, never deletes.

Changes

Backend

  • New PageVariant / RealmPageSlot / AppPageSlot domain types; RealmSettings.PageSlots + ApplicationSettings.PageSlots.
  • Lazy one-time migration from the legacy single-schema Pages dict → one active "Custom" variant.
  • CustomizationPagesEndpoints: variant CRUD + set-active for realm and app.
  • EffectiveSettings resolution is activation-aware (with legacy fallback); /api/app-info keeps its wire shape (effective active schema per slot).
  • RealmSettingsDto no longer carries page schemas (moved to the dedicated endpoints).

Frontend

  • Overview: variants per slot with usage badges + active selector; create/edit/delete.
  • Editor: variant-scoped, name field, non-destructive "Load built-in template".
  • App Settings → Pages: inherit/active selector + per-app variant management.
  • Shared usePagesApi composable; editor route is now pages/:slug/:variantId (new to author).

Docs / ADR

  • docs/platform/pages.md updated; ADR-0001 recorded (Atlas, area modgud).

Feature flag — safe to merge

Everything stays behind AppSettings.Features.PageBuilder (operator-level, defaults false). Supersedes the single-schema storage from #166 (same flag, still dark). Built-in hardcoded views + ?safemode=1 remain the always-safe fallback.

Verification

  • 1,426 unit tests green (incl. new variant/activation/migration EffectiveSettings tests).
  • 10 PageBuilder integration tests green (variant CRUD, activate/deactivate, legacy migration, app inherit/override, local-authorize resolution).
  • Frontend type-check + production build green.
  • DevTools smoke: create variant → not-active → set active → runtime renders → deactivate → built-in fallback (variant retained) → delete (with confirm).

🤖 Generated with Claude Code

Decouple three previously-conflated concepts (ADR-0001): a page schema
exists, a schema is active, and the built-in fixed view. Each slot now
owns a library of named variants plus an explicit active selection at
realm and application level (app overrides realm; inherit/built-in/variant).

Fixes the field-tested UX traps: "reset to default" is now a UI-only
editor buffer load (never a destructive server delete), a variant can be
deactivated without deleting it, and multiple variants per slot are
supported with activation chosen in realm/app settings.

Backend: new PageVariant/RealmPageSlot/AppPageSlot domain types with a
lazy migration from the legacy single-schema Pages dict; variant CRUD +
set-active endpoints for realm and app; activation-aware effective
resolution (app -> realm -> built-in); app-info keeps its wire shape
(effective active schema per slot).

Frontend: overview lists variants with usage badges + an active selector;
variant-scoped editor with non-destructive reset and a name field; app
settings Pages tab gains inherit/active selection + variant management.

Verified: 1426 unit tests + 10 PageBuilder integration tests green;
frontend type-check + build green; DevTools smoke of create -> activate
-> runtime render -> deactivate -> built-in fallback -> delete.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@windischb
windischb merged commit 3397738 into develop Jul 22, 2026
8 checks passed
@windischb
windischb deleted the feat/page-builder-variants branch July 22, 2026 08:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant