feat(static-pages): media picker + per-page modified indicator
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>
This commit is contained in:
@@ -55,6 +55,9 @@
|
||||
@if (!page.enabled) {
|
||||
<app-badge variant="neutral">{{ 'staticPages.disabledBadge' | translate }}</app-badge>
|
||||
}
|
||||
@if (isModified(page)) {
|
||||
<app-badge variant="info">{{ 'builder.unsavedChanges' | translate }}</app-badge>
|
||||
}
|
||||
@if (hasDuplicateSlug(page)) {
|
||||
<app-badge variant="danger">{{ 'staticPages.duplicateSlug' | translate }}</app-badge>
|
||||
}
|
||||
@@ -114,10 +117,16 @@
|
||||
|
||||
<div class="editor-grid three">
|
||||
<app-form-field [label]="'staticPages.heroImageLabel' | translate">
|
||||
<app-input [ngModel]="page.heroImage || ''" (ngModelChange)="updatePage(page.id, { heroImage: $event })" />
|
||||
<div class="media-field-row">
|
||||
<app-input [ngModel]="page.heroImage || ''" (ngModelChange)="updatePage(page.id, { heroImage: $event })" />
|
||||
<app-button variant="secondary" size="sm" (click)="openMediaPicker(page.id, 'heroImage')">{{ 'adminCategories.chooseImage' | translate }}</app-button>
|
||||
</div>
|
||||
</app-form-field>
|
||||
<app-form-field [label]="'staticPages.thumbnailLabel' | translate">
|
||||
<app-input [ngModel]="page.thumbnail || ''" (ngModelChange)="updatePage(page.id, { thumbnail: $event })" />
|
||||
<div class="media-field-row">
|
||||
<app-input [ngModel]="page.thumbnail || ''" (ngModelChange)="updatePage(page.id, { thumbnail: $event })" />
|
||||
<app-button variant="secondary" size="sm" (click)="openMediaPicker(page.id, 'thumbnail')">{{ 'adminCategories.chooseImage' | translate }}</app-button>
|
||||
</div>
|
||||
</app-form-field>
|
||||
<app-form-field [label]="'staticPages.galleryLabel' | translate">
|
||||
<app-input [ngModel]="(page.gallery || []).join(', ')" (ngModelChange)="updateGallery(page.id, $event)" />
|
||||
@@ -177,4 +186,5 @@
|
||||
</app-card>
|
||||
}
|
||||
</div>
|
||||
<app-media-picker [open]="mediaPickerOpen" (selected)="onImagePicked($event)" (closed)="mediaPickerOpen = false" />
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user