From feda0f685f53eb532a6cf8f62756c7e3ec2f44f1 Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Fri, 17 Jul 2026 11:27:29 +0400 Subject: [PATCH] fix(static-pages): backfill enabled/status in export, fix missing i18n key Independent review pass over the Static Pages Module sprint (M1-M7), including live browser E2E per the task's own verification checklist. - ProjectEditorFacade.normalize() now backfills enabled/status on record-format static pages (defaulting missing values to enabled+published, same rule ContentPageService.normalizePage applies for display - mirrored rather than imported, to avoid a project-editor <-> content-management circular dependency since ContentManagementFacade already depends on this facade). Found live: exporting a page that predates this sprint and was never touched/re-saved in the current session produced JSON missing enabled/status entirely - the editor UI and storefront resolver both normalize-on-read so nothing was actually broken live, but Export/Import fidelity should match what the editor shows. Verified fixed live (export now includes "enabled":true,"status":"published" for an untouched legacy page) and via the full gate. - Added the missing adminCategories.chooseImage i18n key (interface + en/ru/hy). Found live: the media-picker "choose image" button rendered as the literal string "adminCategories.chooseImage" - a pre-existing, repo-wide bug (5 templates reference this key; none of the locale files ever defined it) that I propagated into a 3rd/4th/5th... well, 2 new occurrences by copying the existing branding-section/footer-section pattern into static-pages-editor. Fixed the actual defect (missing translation) rather than renaming the key, which would have required touching 2 unrelated admin components outside this sprint's scope. Live-verified this pass: Static Pages editor renders with all new fields; create page works (page count 4->confirmed); device preview toggles desktop/tablet/mobile widths correctly; navigation "Insert page link" creates a real type:'staticPage' nav item end-to-end (confirmed in the exported JSON); export includes all Sprint X+2 fields after the fix; no console errors throughout. Gate: tsc --noEmit, npm test (57/57), arch:check, build all green. Co-Authored-By: Claude Opus 4.8 --- .../facade/project-editor.facade.ts | 32 ++++++++++++++++++- src/app/i18n/en.ts | 3 ++ src/app/i18n/hy.ts | 3 ++ src/app/i18n/ru.ts | 3 ++ src/app/i18n/translations.ts | 8 +++++ 5 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/app/features/project-editor/facade/project-editor.facade.ts b/src/app/features/project-editor/facade/project-editor.facade.ts index 32ec9ed..72d27dc 100644 --- a/src/app/features/project-editor/facade/project-editor.facade.ts +++ b/src/app/features/project-editor/facade/project-editor.facade.ts @@ -552,7 +552,37 @@ export class ProjectEditorFacade { const leftOrder = Math.min(...left.sections.map(section => section.order)); const rightOrder = Math.min(...right.sections.map(section => section.order)); return leftOrder - rightOrder; - }) + }), + staticPages: this.normalizeStaticPagesShape(config.staticPages), }; } + + /** + * Backfills `enabled`/`status` on record-format static pages at the + * bootstrap-normalization layer, mirroring (not importing - would create a + * project-editor <-> content-management circular dependency, since + * ContentManagementFacade already depends on this facade) the same default + * ContentPageService.normalizePage applies for display: a page with no + * enabled/status predates this field and was always live, so it defaults + * to enabled+published rather than silently disappearing. Without this, + * exportBootstrap() on a page nobody has touched this session would omit + * both fields (present in the editor's display via normalize-on-read, but + * literally absent from the stored/exported JSON) - the storefront + * resolver was never at risk (it normalizes on every read), but export + * fidelity should match what the editor shows. + */ + private normalizeStaticPagesShape(staticPages: BootstrapConfig['staticPages']): BootstrapConfig['staticPages'] { + if (!staticPages || Array.isArray(staticPages)) { + return staticPages; + } + const result: typeof staticPages = {}; + for (const [key, page] of Object.entries(staticPages)) { + result[key] = { + ...page, + enabled: page.enabled ?? true, + status: page.status ?? 'published', + }; + } + return result; + } } diff --git a/src/app/i18n/en.ts b/src/app/i18n/en.ts index 0035b93..0983504 100644 --- a/src/app/i18n/en.ts +++ b/src/app/i18n/en.ts @@ -921,6 +921,9 @@ export const en: Translations = { emptyTitle: 'No files yet', emptyDescription: 'Upload an image or document to get started.', }, + adminCategories: { + chooseImage: 'Choose image', + }, adminProducts: { emptyTitle: 'No products found', emptyDescription: 'Try adjusting your filters, or create a new product.', diff --git a/src/app/i18n/hy.ts b/src/app/i18n/hy.ts index 465b8af..2af2787 100644 --- a/src/app/i18n/hy.ts +++ b/src/app/i18n/hy.ts @@ -916,6 +916,9 @@ export const hy: Translations = { emptyTitle: 'Ֆայլեր դեռ չկան', emptyDescription: 'Վերբեռնեք պատկեր կամ փաստաթուղթ սկսելու համար։', }, + adminCategories: { + chooseImage: 'Ընտրել պատկեր', + }, adminProducts: { emptyTitle: 'Ապրանքներ չեն գտնվել', emptyDescription: 'Փոխեք ֆիլտրերը կամ ստեղծեք նոր ապրանք։', diff --git a/src/app/i18n/ru.ts b/src/app/i18n/ru.ts index ffb8c7c..44d36af 100644 --- a/src/app/i18n/ru.ts +++ b/src/app/i18n/ru.ts @@ -916,6 +916,9 @@ export const ru: Translations = { emptyTitle: 'Пока нет файлов', emptyDescription: 'Загрузите изображение или документ, чтобы начать.', }, + adminCategories: { + chooseImage: 'Выбрать изображение', + }, adminProducts: { emptyTitle: 'Товары не найдены', emptyDescription: 'Измените фильтры или создайте новый товар.', diff --git a/src/app/i18n/translations.ts b/src/app/i18n/translations.ts index 1f017b8..675276b 100644 --- a/src/app/i18n/translations.ts +++ b/src/app/i18n/translations.ts @@ -923,6 +923,14 @@ export interface Translations { // state consistency fix introduces - NOT the full adminProducts/ // adminUsers/adminMonitoring/adminAnalytics translation surface, which is // still missing entirely (see docs/KNOWN-ISSUES.md, deferred to Sprint 29). + // Shared across branding/footer/static-pages/admin-product/admin-category media + // pickers - despite the namespace name, this is a generic "choose image" label, + // not category-specific. Renaming is a separate cleanup outside this sprint's + // scope; the key was previously referenced by 5 templates but never defined + // anywhere, so it rendered as the literal string "adminCategories.chooseImage". + adminCategories: { + chooseImage: string; + }; adminProducts: { emptyTitle: string; emptyDescription: string;