-
- @if (selectedCount() > 0) {
-
{{ selectedCount() }} {{ 'staticPages.selectedCount' | translate }}
-
{{ 'staticPages.bulkEnable' | translate }}
-
{{ 'staticPages.bulkDisable' | translate }}
-
{{ 'staticPages.bulkPublishAction' | translate }}
-
{{ 'staticPages.bulkUnpublish' | translate }}
-
{{ 'staticPages.bulkDelete' | translate }}
+
+
+
+
+ @if (pages().length > 0) {
+
+
+ @if (selectedCount() > 0) {
+
{{ selectedCount() }} {{ 'staticPages.selectedCount' | translate }}
+
{{ 'staticPages.bulkEnable' | translate }}
+
{{ 'staticPages.bulkDisable' | translate }}
+
{{ 'staticPages.bulkPublishAction' | translate }}
+
{{ 'staticPages.bulkUnpublish' | translate }}
+
{{ 'staticPages.bulkDelete' | translate }}
+ }
+
+ }
+
+ @if (pages().length === 0) {
+
+
+ {{ 'builder.createPage' | translate }}
+
+
+ }
+
+
+ @for (page of pages(); track page.id) {
+
+
+
+ @if (previewOpenPageId() === page.id) {
+
+ }
+
}
}
- @if (pages().length === 0) {
-
-
- {{ 'builder.createPage' | translate }}
-
-
- }
-
-
- @for (page of pages(); track page.id) {
-
-
-
-
- @if (previewOpenPageId() === page.id) {
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- @for (locale of locales(); track locale) {
-
-
-
-
-
-
- }
-
-
{{ 'builder.seoSection' | translate }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- }
-
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 35b4b19..cfadc6e 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
@@ -1,44 +1,18 @@
-.page-card {
+.page-card-grid {
display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 14px;
}
-.page-card__header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- flex-wrap: wrap;
- gap: 10px;
-}
-
-.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);
-}
-
-.page-card__subheading {
- margin: 4px 0 0;
- font-size: 0.875rem;
- font-weight: 700;
- color: var(--text-secondary, #5f6e6a);
- text-transform: uppercase;
- letter-spacing: 0.04em;
-}
-
-.toggle-grid {
+.page-card-grid__item {
+ position: relative;
display: grid;
- grid-template-columns: repeat(4, minmax(0, 1fr));
- gap: 12px;
+ gap: 8px;
}
-@media (max-width: 900px) {
- .toggle-grid {
- grid-template-columns: repeat(2, minmax(0, 1fr));
- }
+.page-card-grid__select {
+ position: absolute;
+ top: 12px;
+ right: 12px;
+ z-index: 1;
}
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 b4a723c..324e7f7 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
@@ -3,25 +3,25 @@ import { FormsModule } from '@angular/forms';
import { ProjectEditorFacade } from '../../project-editor/facade/project-editor.facade';
import { ContentManagementFacade } from '../facade/content-management.facade';
import { ContentPage, ContentPageSeoConfig, ContentPageStatus } from '../models/content-page.model';
+import { findLegalDefinition } from '../models/legal-pages.model';
import { BootstrapConfig } from '../../../shared/models/config';
import { TranslatePipe } from '../../../i18n/translate.pipe';
import { TranslateService } from '../../../i18n/translate.service';
-import { MarketplaceHtmlEditorComponent } from '../../project-editor/components/html-editor/marketplace-html-editor.component';
import { ButtonComponent } from '../../../shared/ui/button/button.component';
import { InputComponent } from '../../../shared/ui/input/input.component';
-import { CardComponent } from '../../../shared/ui/card/card.component';
import { FormFieldComponent } from '../../../shared/ui/form-field/form-field.component';
-import { BadgeComponent } from '../../../shared/ui/badge/badge.component';
import { EmptyStateComponent } from '../../../shared/ui/empty-state/empty-state.component';
import { SelectComponent, SelectOption } from '../../../shared/ui/select/select.component';
import { ToggleComponent } from '../../../shared/ui/toggle/toggle.component';
import { MediaPickerComponent } from '../../../shared/media/media-picker/media-picker.component';
import { MediaAsset } from '../../../core/media/models/media-asset.model';
import { StaticPagePreviewComponent } from './static-page-preview/static-page-preview.component';
+import { ContentDashboardComponent } from './content-dashboard/content-dashboard.component';
+import { PageCardComponent, PageSeoStatus } from './page-card/page-card.component';
+import { PageEditorComponent, PageMediaField } from './page-editor/page-editor.component';
type StatusFilter = 'all' | ContentPageStatus;
const ALL_LOCALES_FILTER = 'all';
-type MediaField = 'heroImage' | 'thumbnail';
@Component({
selector: 'app-static-pages-editor',
@@ -29,17 +29,17 @@ type MediaField = 'heroImage' | 'thumbnail';
imports: [
FormsModule,
TranslatePipe,
- MarketplaceHtmlEditorComponent,
ButtonComponent,
InputComponent,
- CardComponent,
FormFieldComponent,
- BadgeComponent,
EmptyStateComponent,
SelectComponent,
ToggleComponent,
MediaPickerComponent,
StaticPagePreviewComponent,
+ ContentDashboardComponent,
+ PageCardComponent,
+ PageEditorComponent,
],
templateUrl: './static-pages-editor.component.html',
styleUrls: ['../../project-editor/sections/section.shared.scss', './static-pages-editor.component.scss'],
@@ -57,12 +57,18 @@ export class StaticPagesEditorComponent {
};
readonly allPages = computed(() => this.contentFacade.pages(this.bootstrap()));
readonly validation = computed(() => this.contentFacade.validatePages(this.bootstrap()));
+ readonly contentHealth = computed(() => this.contentFacade.contentHealth(this.bootstrap()));
readonly locales = computed(() => this.bootstrap()?.localization.supportedLocales ?? ['en']);
+ readonly defaultLocale = computed(() => this.bootstrap()?.localization.defaultLocale ?? 'en');
readonly searchQuery = signal('');
readonly statusFilter = signal
('all');
readonly localeFilter = signal(ALL_LOCALES_FILTER);
readonly selectedIds = signal>(new Set());
+ readonly editingPageId = signal(null);
+ readonly previewOpenPageId = signal(null);
+
+ readonly editingPage = computed(() => this.allPages().find(page => page.id === this.editingPageId()) ?? null);
readonly statusFilterOptions = computed(() => [
{ value: 'all', label: this.translate.t('staticPages.filterAll') },
@@ -80,7 +86,7 @@ export class StaticPagesEditorComponent {
const status = this.statusFilter();
const locale = this.localeFilter();
- return this.allPages().filter(page => {
+ const filtered = this.allPages().filter(page => {
if (status !== 'all' && page.status !== status) {
return false;
}
@@ -95,6 +101,13 @@ export class StaticPagesEditorComponent {
.toLowerCase();
return haystack.includes(query);
});
+
+ // Legal pages surface first (Task 3) - stable sort keeps the rest in their existing order.
+ return [...filtered].sort((a, b) => {
+ const aLegal = findLegalDefinition(a) ? 0 : 1;
+ const bLegal = findLegalDefinition(b) ? 0 : 1;
+ return aLegal - bLegal;
+ });
});
readonly selectedCount = computed(() => this.selectedIds().size);
@@ -103,11 +116,6 @@ export class StaticPagesEditorComponent {
return visible.length > 0 && visible.every(page => this.selectedIds().has(page.id));
});
- /**
- * The originally loaded/published pages, normalized through the same
- * ContentManagementFacade.pages() path as the live editor list, keyed by
- * id - reused (not reimplemented) for the per-page modified indicator.
- */
private readonly originalPagesById = computed