Milestone 6 of the Static Pages Module sprint.
- ProjectEditorFacade.addStaticPageNavLink(target, pageId): creates a
NavigationItemConfig { type: 'staticPage', key: pageId }. This shape was
already understood end-to-end by the resolvers (StaticPageResolverService/
FooterResolverService derive label+route from the linked page - see
footer-resolver.service.ts resolveGroupItem/resolveLegacyItem) - the only
gap was that the editor UI never exposed a way to create it.
- navigation-section: "Insert page link" control (page picker + button) next
to both header and footer "Add link". Rows for a static-page link show a
"Linked to page" indicator instead of the raw label/URL inputs (those
fields don't apply - the resolver derives them dynamically). labelOf()
falls back to the page id for the row heading since a static-page link has
no label of its own.
- i18n: builder.insertPageLink, builder.linkedToPage in interface + en/ru/hy.
Verified (no rebuild needed) that pages already participate in preview(),
exportBootstrap()/importBootstrap(), and draft/publish gating - all flow
through bootstrap.staticPages and the M1-M2 additive fields untouched here.
Gate: tsc --noEmit, npm test (57/57), arch:check, build all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Milestone 4 of the Static Pages Module sprint.
- heroImage/thumbnail now wire through the existing MediaPickerComponent
(same media-field-row + "choose image" pattern as branding-section), not
plain URL text alone. gallery stays a lightweight CSV field ("future
ready" per the brief - no dedicated multi-upload UI this sprint).
- ProjectEditorFacade: add originalStaticPages, a narrow computed exposing
the originally loaded/published staticPages snapshot (mirrors the facade's
existing pattern of small single-purpose computeds).
- StaticPagesEditorComponent: isModified(page) diffs a page against its
normalized original snapshot, reusing ContentManagementFacade.pages() for
normalization rather than reimplementing it. Renders as an amber
"unsaved changes" badge per page.
Draft/published status UI, publish/unpublish actions, and the plain-text
media fields landed already in M2; this milestone completes M4's remaining
scope (visual media picker + modified indicator) without duplicating that
work.
Gate: tsc --noEmit, npm test (57/57), arch:check, build all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Independent review pass over the Configuration Engine sprint (M1-M6).
- Facade: a fresh edit burst now clears the redo (future) stack immediately,
not just once its debounced commit lands ~300ms later. Previously, editing
right after an undo left canRedo() true for that window; clicking Redo
during it would have silently discarded the new edit and jumped back to
the stale future snapshot. Reordered two interspersed imports/interface
for readability while in the file.
- footer-section: removed a local HTTP_URL regex + duplicate isValidUrl
logic (its "shared/ui can't import features" justification didn't apply -
this file already lives in features/project-editor/sections/, the same
feature as schema/validators/). Now calls isValidHttpUrl from
schema/validators/primitives, closing a validator duplication the sprint's
"no duplicated validators" requirement was meant to catch.
Gate: tsc --noEmit, npm test (33/33), arch:check, build all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Milestone 5 of the Configuration Engine sprint.
- Facade: changeSummary computed - per modified field, before/after values
(schema label + stringified diff vs originalBootstrap), reusing
modifiedFields from M4.
- preview-section: new "changes since last publish" card ahead of the
existing export/import/live-preview card - validation issue list
(warning/error styled) plus a before/after change table. Reuses the
existing, non-destructive ProjectEditorPreviewService.preview() call.
- i18n: previewChangesTitle/NoIssues/NoChanges/ChangeField/Before/After in
interface + en/ru/hy.
Gate: tsc --noEmit, npm test (33/33), arch:check, build all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Milestone 4 of the Configuration Engine sprint.
- Add schema/history.util: pure undo/redo reducer (commit/undo/redo, depth cap)
with full spec coverage.
- Facade: debounced snapshot history (~300ms coalesce so a typing burst = one
undo step); undo()/redo() route through the draft-save path so autosave never
desyncs; canUndo/canRedo; history cleared on load/publish/resetDraft.
modifiedFields (schema-diff vs original) + modifiedSections computeds.
- save-bar: Undo/Redo buttons. Page: Ctrl/Cmd+Z / Shift+Z / Y shortcuts
(skipped while a text field is focused so native text undo is preserved);
beforeunload guard already present.
- nav: amber modified-field dot per section (when no blocking badge).
- i18n: builder.undo / builder.redo in interface + en/ru/hy.
Gate: tsc --noEmit, npm test (33/33), arch:check, build all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Milestone 3 of the Configuration Engine sprint.
- Facade fieldError(key) accessor over issuesByField for inline field errors.
- Bind [error] on schema-backed fields: theme palette colours, general
name/domain, branding logo (translated via each section).
- project-editor-nav: per-section blocking-issue count badge (issuesBySection).
- save-bar: Publish now disabled on hasBlockingIssues() (errors only, so new
warnings no longer block); issue list tags warning vs error severity.
Editor verified rendering at /ru/edit/theme with the new nav + save bar;
validation logic covered by the 25 unit tests.
Gate: tsc --noEmit, npm test (25/25), arch:check, build all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Milestone 2 of the Configuration Engine sprint.
- Add schema/validators/primitives: pure isValidHexColor/HttpUrl/Email,
validateJson, validateCss, extractStyleBlocks, normalizeRoute. One function
per concern, no duplicated validator logic.
- Refactor ProjectValidator to compose the primitives and tag every issue with
section + fieldKey + severity ('error' blocks publish, 'warning' advisory).
Preserves all existing codes/messages; adds duplicate-routes, invalid-css,
invalid-widget-config checks.
- Facade: issuesByField, issuesBySection, blockingIssues, hasBlockingIssues;
publish() now gates on severity==='error' instead of any issue.
- i18n: add validationInvalidJson/Css/DuplicateRoutes/InvalidWidgetConfig to
the Translations interface + en/ru/hy.
- Specs: primitives + ProjectValidator (25 passing total).
Gate: tsc --noEmit, npm test (25/25), arch:check, build all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- navigation-section: add LocaleTabs; label input now reads/writes the
active locale's translation via a new editableLabel() helper instead of
always the default locale. facade.updateNavLinkLabel() gained an optional
locale param (defaults to current default locale, so existing callers
are unaffected) and correctly promotes a plain-string label into a
per-locale map when writing a non-default locale.
- languages-section: wrap in SectionCard, add LocaleTabs for consistency.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Project editor: persist draft to localStorage, restore on reload,
last-saved/draft-restored status indicators, section/whole-draft reset
with confirmation.
- Extract shared QR/polling/expiry engine from TelegramLoginComponent
(shared/qr-login) and reuse it for a new admin login flow.
- Admin authentication kept fully separate from customer session:
own cookie/localStorage keys, signals, guard, and header interceptor
(core/admin-auth).
- ?login=true / ?adminLogin=true open the respective login dialog for
manual testing.
- Ed25519 challenge/verify interfaces (fail-closed no-op binding) ready
for backend delivery.
- Document autosave/reset/admin-auth/QR-reuse/Ed25519 model and the
remaining full-field-coverage gap in docs/Project-Editor.md.
updateLabel() previously overwrote NavigationItemConfig.label with a bare
string via updateNavLink, destroying every other locale's translation
whenever a localized label object was edited. Add a facade method
updateNavLinkLabel() that inspects the existing label shape: plain
strings are replaced as before, but localized objects only have the
current default locale's key overwritten, leaving other locales intact.
Wires ProjectValidator into ProjectEditorFacade and extends ProjectEditorState
with status ('draft'|'published') and lastSavedBootstrap. Adds dirty computed
(diffed against lastSavedBootstrap), validationIssues computed, and save()/publish()
methods. publish() calls PlatformRuntimeService.reloadFromBootstrap() and refuses
when validation issues exist. loadBootstrap() seeds lastSavedBootstrap so a
freshly-loaded bootstrap is not dirty.