# EDITOR (Project Editor) Replaces the old `docs/Project-Editor.md` (content merged in below and extended with the Sprint 19 field-description/dropdown work). The Project Editor (`src/app/features/project-editor/`) edits the tenant's `BootstrapConfig` (`docs/BOOTSTRAP.md`) directly — no parallel model. It is out of scope for products, categories, orders, or analytics management (those live under `features/admin/*`/`features/backoffice/*`, see `docs/ADMIN.md`). ``` src/app/features/project-editor/ pages/ route container sections/ one component per editor tab (see below) components/ shared editor UI (save bar, HTML editor) models/ ProjectEditorState, EDITOR_SECTION_BOOTSTRAP_KEYS schema/ field-schema registry, validators/, history.util (Sprint X+1, see below) services/ ProjectValidator, ProjectEditorDraftStorageService, LocaleSyncService facade/ ProjectEditorFacade ``` Route: `/edit/:section` or `/{lang}/edit/:section`. `/backoffice/static-pages` (the Admin dashboard) redirects here (`/edit/static-pages`) rather than hosting a second CRUD surface over the same `bootstrap.staticPages` data (Sprint X+2 — see `docs/StaticPages.md`). ## Facade `ProjectEditorFacade` exposes: `loadBootstrap()`, `updateBootstrap(updater)`, `exportBootstrap()`, `importBootstrap()`, `preview()`, `save()`, `publish()`, `undo()`, `redo()`, plus signals `bootstrap`, `status` (`draft|published`), `dirty`, `canUndo`, `canRedo`, `lastSavedAt`, `lastPublishedAt`, `validationIssues`, `blockingIssues`, `hasBlockingIssues`, `issuesByField`, `issuesBySection`, `modifiedFields`, `modifiedSections`, `changeSummary`, `homepageWidgets`, `homepagePage`, plus the `fieldError(key)` method. Components in `sections/*` inject this facade directly (an accepted exception to the presentational-component rule, per ADR-006 — these are container/section components, not shared UI). See "Configuration schema, form engine, and validation architecture" below for the schema/validator/undo internals. ## Sections | Section | Component | Covers | |---|---|---| | General | `general-section` | marketplace name, domain, description, default/supported languages | | Branding | `branding-section` | logo, small logo, favicon, social share (OG) image, gallery, marketplace title — all image fields use `app-image-field` (thumbnail preview + replace/remove) | | Theme | `theme-section` | palette colors (live, applied as CSS custom properties), theme mode (**not applied at runtime, see Known gaps**), site layout mode | | Header | `header-section` | logo/search/categories/languages/cart/profile/wishlist/compare/region toggles, layout (default/centered), sticky | | Footer | `footer-section` | company info, address, phone, email, copyright, payment icons, social links, static pages list | | Homepage | `homepage-section` | homepage section list: visibility, order (drag-and-drop), layout strategy, columns | | Widgets | `widgets-section` | homepage widget configuration — typed editors for hero/categories/product-collection, JSON fallback (with draft-preserving inline error, not silent-discard) for everything else | | Static Pages | `static-pages-editor` (`features/content-management/`) | Full CRUD, media, SEO, per-page draft/publish, device preview, nav integration — see `docs/StaticPages.md` (Sprint X+2) | | Marketplace Features | `features-section` | feature flags, catalog navigation mode, search suggestions/history, recently viewed, reviews/questions/recommendations | | Languages | `languages-section` | add/remove supported locale, set default locale; syncs translation keys across static pages and nav labels via `LocaleSyncService` | | Navigation | `navigation-section` | header nav: add/remove/reorder/edit label/URL/visibility, per-locale via `app-locale-tabs`. Flat footer nav: same. Grouped (column-based) footer nav is read-only here — edit via Footer tab. | | Preview | `preview-section` | export/import JSON, in-memory runtime preview without full reload | ## Save / publish / draft / reset model - **Save**: `save()` snapshots the current in-memory bootstrap as "last saved" (`lastSavedAt`). `ProjectEditorDraftStorageService` persists the full draft to `localStorage` (`projectEditor.draftBootstrap.v1`, scoped by `tenant.id`) on every `updateBootstrap()`, `save()`, and `publish()` call. - **Publish**: runs `ProjectValidator`; if clean, calls `PlatformRuntimeService.reloadFromBootstrap()`, sets `status = 'published'`, sets `lastPublishedAt`, and becomes the new `originalBootstrap` baseline used by reset. - **Draft restore**: on `loadBootstrap()`, if a stored draft exists for the same tenant it loads instead of the fresh fetch, and `draftRestored` is set (shown as a dismissible banner in the save bar). - **Reset section**: reverts one section's bootstrap keys (per `EDITOR_SECTION_BOOTSTRAP_KEYS` in `models/project-editor.model.ts`) to `originalBootstrap`. Confirmation required. - **Reset draft**: reverts the entire bootstrap to `originalBootstrap` and clears the persisted local draft. Confirmation required. - **Per-field reset is not implemented** — no per-field default registry exists; only section- and project-level reset. - **No backend persistence exists for any of this today** — see `docs/BACKEND.md` item 2 for the endpoints needed. ## Configuration schema, form engine, and validation architecture (Sprint X+1) **Approach: metadata-augmented, not fully schema-driven.** Section templates stay hand-authored (`sections/*.component.html`); a field-schema registry sits alongside them as the single source of truth for field identity, labels, and validator wiring. This was chosen over a schema-driven renderer to preserve every existing template/UX pixel-for-pixel while still centralizing metadata and validation — the highest-value, lowest-regression-risk option given 11 mature section templates already built on the `shared/ui` primitives (see the primitives table above). ### Field-schema registry (`schema/`) - `field-schema.model.ts` — `FieldSchema`: `{ key, section, type, labelKey, hintKey?, default?, required?, validators? }`. `key` is a dot path into `BootstrapConfig` (e.g. `theme.palette.primary`), unique per section. `validators` references reusable validator names (`hexColor`, `url`, `email`, `json`, `css`, `localeCompleteness`, `duplicateRoutes`, `widgetConfig`) rather than embedding logic. - `editor-schema.ts` — `SECTION_FIELD_SCHEMAS`: every editable field, one entry per section, sourced from what each template already renders. `ALL_FIELD_SCHEMAS` flattens it. - `editor-schema.service.ts` (`EditorSchemaService`, `providedIn: 'root'`) — `getFields(section)`, `getField(key)`, `all()`, `getByPath(source, key)` (safe dot-path resolver, never throws on a missing segment). The schema is currently consumed by the facade (validation issue → field mapping, modified-field diffing, change-summary labels), not by the templates directly — templates keep calling `facade.updateBootstrap()` the same way they always did. ### Centralized validators (`schema/validators/`) `primitives.ts` holds pure, framework-free functions — one per concern, reused everywhere that concern appears: `isValidHexColor`, `isValidHttpUrl`, `isValidEmail`, `validateJson`, `validateCss` (brace-balance check, comments stripped), `extractStyleBlocks` (pulls `