Files
marketplaces/docs/superpowers/specs/2026-07-13-marketplace-project-editor-sprint16-design.md

176 lines
7.8 KiB
Markdown
Raw Normal View History

# Sprint 16 — Marketplace Project Editor MVP
Status: approved for planning
Date: 2026-07-13
Related: [ADR-0001](../../context/adrs/ADR-0001-marketplace-platform-vision.md), [Project-Editor.md](../../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").