Files
marketplaces/docs/superpowers/specs/2026-07-13-marketplace-project-editor-sprint16-design.md
sdarbinyan 637ae28d47 docs: add platform vision ADR and Sprint 16 project editor design spec
Records the multi-tenant marketplace platform architecture as ADR-0001
(bootstrap-driven, config-only frontend) with a source-backed fact pack,
and writes the approved Sprint 16 design for extending the existing
project editor with Languages/Navigation tabs, an HTML editor, and a
client-side draft/publish flow.
2026-07-13 04:01:48 +04:00

7.8 KiB

Sprint 16 — Marketplace Project Editor MVP

Status: approved for planning Date: 2026-07-13 Related: ADR-0001, Project-Editor.md

Goal

A client can open the editor for their marketplace, edit settings, save a draft, preview, and publish — without touching JSON by hand. The editor edits the same BootstrapConfig the storefront consumes. No parallel/duplicate configuration model is introduced anywhere in this work.

Current state (as of this sprint)

A working editor already exists at /builder (src/app/features/project-editor/): ProjectEditorPageComponent + signal-based ProjectEditorFacade (no NgRx), sections for general, branding, theme, header, footer, homepage (already has drag-and-drop reordering), widgets, static-pages (via StaticPagesEditorComponent), features, preview. Static page HTML is edited via a plain <textarea>. There is no Languages tab, no Navigation tab, no Draft/Publish distinction, and no ProjectValidator. There is no separate admin app — admin-ish features are ordinary lazy routes in the same Angular build as the storefront. There is no projectId / multi-project concept: each domain (tenant) resolves to its own bootstrap via the existing tenant-resolution mechanism, so "a project" is implicitly the current domain's tenant.

This sprint extends the existing editor rather than rebuilding it.

Routing

Single Angular build (no separate admin app this sprint — deferred, see Out of scope). Flat, non-parameterized routes, since there is no project id:

/edit                  → redirect to /edit/general
/edit/general
/edit/branding
/edit/theme            (existing, kept)
/edit/languages        (new)
/edit/navigation       (new)
/edit/header           (existing, kept)
/edit/homepage
/edit/footer
/edit/static-pages
/edit/widgets          (existing, kept)
/edit/features         (existing, kept)
/edit/preview

/builder and /builder/* redirect to the equivalent /edit/* route for backward compatibility. Actual subdomain hosting (admin.marketplace.com) and a physically separate Angular deployment are a future ADR/sprint — this sprint only makes the route shape subdomain-ready (flat, no id segment).

Tabs become real child routes (deep-linkable, back-button works) instead of today's in-component signal switch. ProjectEditorFacade is unchanged by this — it already loads/holds bootstrap independent of which tab is active.

Languages tab (new)

New LocaleSyncService (in features/project-editor/services/). It knows both translation shapes already used in the codebase:

  • index-signature maps: { [locale: string]: string } (LocalizedTextContent/LocalizedHtmlContent)
  • keyed translation records: Record<string, { title?, html?, seo? }> (e.g. StaticPageTranslationConfig)

addLocale(code) / removeLocale(code) walk the known translatable slices of the in-memory bootstrap draft (static pages, footer nav labels, homepage widget text props) and add/remove that locale's key generically — not one if per field. setDefaultLocale(code) updates localization.defaultLocale (already exists). The tab lists supported locales, lets you add/remove, and pick the default; removing the current default is blocked by ProjectValidator (see below).

Navigation tab (new)

Visual nested list over header + footer navigation (src/app/shared/models/config/navigation.model.ts). Add link, remove link, reorder (buttons, matching the homepage tab's existing pattern — no new drag-and-drop library), edit label (multilingual, via the same translation shape as everywhere else), edit URL, "open in new tab" toggle, visibility toggle, and nested children. Backed by new facade actions on ProjectEditorFacade (addNavLink, removeNavLink, reorderNavLink, updateNavLink) — no new store, same facade.

Rich HTML editor (new)

New MarketplaceHtmlEditorComponent (features/project-editor/components/html-editor/): native contentEditable + a toolbar (bold, italic, underline, bullet/numbered lists, link, image, table, headings, code view, preview). No new npm dependency (no Quill/TipTap). Emits raw HTML on change; does not sanitize — sanitization remains a storefront-render concern, unchanged. Replaces the <textarea> currently in static-pages-editor.component.html, and is reused anywhere else HTML is edited going forward (e.g. footer custom content).

Draft / Publish

No backend draft/publish API exists yet — confirmed against docs/Project-Editor.md, which only lists recommended future endpoints (PUT /builder/bootstrap, POST /builder/bootstrap/{preview,validate,import}, GET /builder/bootstrap/export), none of which distinguish draft from published. This sprint models it client-side only:

  • ProjectDraft (new, in models/): { bootstrap: BootstrapConfig, status: 'draft' | 'published', dirty: boolean, lastSavedAt?: string }.
  • Save persists the draft using the same local mechanism the editor already uses for export/import (no new backend call this sprint) and clears dirty.
  • Publish runs ProjectValidator (must pass), then calls ConfigService.applyBootstrapOverride() (the mechanism already used for live preview) and sets status = 'published'.
  • Documented gap for backend: real draft/publish persistence needs PUT /builder/bootstrap/draft and POST /builder/bootstrap/publish endpoints. Not implemented this sprint; called out explicitly so backend work can be scheduled.

Validation (new)

New ProjectValidator (in services/), pure functions run against the current draft. Checks: missing logo, no languages configured, invalid marketplace URL, duplicate static-page slugs, empty homepage (no sections), homepage section referencing a missing/unregistered widget, duplicate navigation links (same URL+label pair), invalid color values (theme tab). Runs on every Save (surfaces warnings, does not block) and on Publish (blocks with a clear list of failures). Errors surface inline in the relevant tab plus a summary list.

Dirty-state

ProjectEditorFacade tracks dirty via a diff against the last-saved snapshot. A CanDeactivate route guard plus a beforeunload listener warn before navigating away or closing the tab with unsaved changes.

UI

Card-based layout per tab, sticky Save/Publish bar (shows dirty state and validation summary), responsive down to tablet width — matching the existing editor's current visual style, no new design system.

Architecture summary

Reused as-is: ProjectEditorFacade, ProjectEditorIoService, ProjectEditorPreviewService, existing section components.

New: LocaleSyncService, ProjectValidator, MarketplaceHtmlEditorComponent, ProjectDraft model, Navigation tab section + facade actions, Languages tab section + facade actions, CanDeactivate dirty-guard.

No ProjectEditorStore/ProjectSerializer as separate classes — the existing facade + ProjectEditorIoService already cover that responsibility (store = facade signals; serializer = IO service); introducing parallel classes would duplicate what's there, which the platform's own coding rules forbid.

Out of scope (this sprint)

  • Separate Angular app / actual subdomain deployment for admin.
  • Real backend draft/publish persistence (documented as a gap above).
  • Drag-and-drop for the new Navigation tab (buttons only, like homepage's existing pattern predates this — homepage itself already has drag-and-drop from a prior sprint and is left as-is).
  • Products, Orders, Users, Dashboards, Analytics (future sprints per the platform roadmap).

Documentation

This spec plus updates to docs/Project-Editor.md (new tabs, draft/publish model, new services) and a fact-pack update under docs/context/features/project-editor/FACTS.jsonl are part of the deliverable, per the platform's documentation rule ("every new feature must include documentation").