From 9133113ab2d930ac07afe4a0f4c3f2547c4077c5 Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Wed, 15 Jul 2026 04:46:27 +0400 Subject: [PATCH] feat(content-management): surface validation errors per-page Replace global duplicate-slug/empty-title banners with per-page indicators: a danger badge next to the offending page's heading, plus inline error text on the specific pageId/slug app-form-field. Made ContentPageService.normalizeSlug public (was private) so the component can match validation results to a given page's normalized slug without duplicating the normalization logic. Verified in browser: setting a duplicate slug live shows both the header badge and the inline field error immediately. Completes Sprint 3 (Static Page Generator): DRY cleanup, Design System adoption, SEO field coverage, per-page validation UX. --- .../static-pages-editor.component.html | 21 ++++++++++--------- .../static-pages-editor.component.scss | 4 ++++ .../static-pages-editor.component.ts | 8 +++++++ .../facade/content-management.facade.ts | 4 ++++ .../services/content-page.service.ts | 2 +- 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/app/features/content-management/components/static-pages-editor.component.html b/src/app/features/content-management/components/static-pages-editor.component.html index e516477..a5da9b0 100644 --- a/src/app/features/content-management/components/static-pages-editor.component.html +++ b/src/app/features/content-management/components/static-pages-editor.component.html @@ -4,13 +4,6 @@ {{ 'builder.createPage' | translate }} - @if (validation().duplicateSlugs.length > 0) { - {{ 'staticPages.duplicateSlug' | translate }} - } - @if (validation().emptyTitles.length > 0) { - {{ 'staticPages.emptyTitle' | translate }} - } - @if (pages().length === 0) { @@ -24,7 +17,15 @@
-

{{ page.id }}

+

+ {{ page.id }} + @if (hasDuplicateSlug(page)) { + {{ 'staticPages.duplicateSlug' | translate }} + } + @if (hasEmptyTitle(page)) { + {{ 'staticPages.emptyTitle' | translate }} + } +

@@ -33,10 +34,10 @@
- + - + diff --git a/src/app/features/content-management/components/static-pages-editor.component.scss b/src/app/features/content-management/components/static-pages-editor.component.scss index fce1121..35b4b19 100644 --- a/src/app/features/content-management/components/static-pages-editor.component.scss +++ b/src/app/features/content-management/components/static-pages-editor.component.scss @@ -13,6 +13,10 @@ .page-card__title { margin: 0; + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 8px; font-size: 1rem; font-weight: 700; color: var(--text-primary, #1e3c38); diff --git a/src/app/features/content-management/components/static-pages-editor.component.ts b/src/app/features/content-management/components/static-pages-editor.component.ts index 1721fbb..11e5c70 100644 --- a/src/app/features/content-management/components/static-pages-editor.component.ts +++ b/src/app/features/content-management/components/static-pages-editor.component.ts @@ -81,6 +81,14 @@ export class StaticPagesEditorComponent { }))); } + hasDuplicateSlug(page: ContentPage): boolean { + return this.validation().duplicateSlugs.includes(this.contentFacade.normalizeSlug(page.slug)); + } + + hasEmptyTitle(page: ContentPage): boolean { + return this.validation().emptyTitles.includes(page.id); + } + updateSeo(id: string, patch: Partial): void { this.persist(this.pages().map(page => page.id !== id ? page : ({ ...page, diff --git a/src/app/features/content-management/facade/content-management.facade.ts b/src/app/features/content-management/facade/content-management.facade.ts index 454fd3d..e932be7 100644 --- a/src/app/features/content-management/facade/content-management.facade.ts +++ b/src/app/features/content-management/facade/content-management.facade.ts @@ -26,4 +26,8 @@ export class ContentManagementFacade { serializePages(pages: ContentPage[]) { return this.service.toBootstrapRecord(pages); } + + normalizeSlug(value: string): string { + return this.service.normalizeSlug(value); + } } diff --git a/src/app/features/content-management/services/content-page.service.ts b/src/app/features/content-management/services/content-page.service.ts index 35e42ed..1b8d1ed 100644 --- a/src/app/features/content-management/services/content-page.service.ts +++ b/src/app/features/content-management/services/content-page.service.ts @@ -170,7 +170,7 @@ export class ContentPageService { return translations; } - private normalizeSlug(value: string): string { + normalizeSlug(value: string): string { return value.replace(/^\/+/, '').trim(); }