feat(static-pages): CRUD completion + search/filter/bulk actions

Milestone 2 of the Static Pages Module sprint.

- StaticPagesEditorComponent: duplicate page, confirm-before-delete/bulk-
  delete (matches the resetDraft confirm pattern), route/enabled/customTemplate/
  media(hero/thumbnail/gallery) fields wired into the card, per-page publish/
  unpublish action, status + duplicate-route/invalid-html/invalid-seo badges.
- Search (id/slug/route/title across all locales), filter by status
  (draft/published) and by locale (hides pages missing a translation for the
  selected locale) - all local computed() filters, no new service.
- Bulk selection (per-row + select-all-visible checkboxes) with bulk delete/
  enable/disable/publish/unpublish, one updateBootstrap() call each.
- Correctness note: introduced `allPages` (unfiltered) vs `pages` (filtered
  view) computeds. Every mutation (create/duplicate/delete/move/bulk) reads
  from allPages(), never the filtered pages() - reading from the filtered
  view would have silently deleted whatever an active search/filter hid on
  the next persist(). Documented inline on persist() as a guardrail for
  future edits.
- Fixed a template compile error found by the build gate: Angular templates
  don't support inline arrow functions in binding expressions
  ((ngModelChange)="...map(v => v.trim())..." failed to parse) - moved the
  gallery CSV-parsing into a component method (updateGallery).
- SEO robots field added to the page card (validated against a known-token
  set from M1).
- i18n: staticPages.* extended (search/filter/bulk/route/enabled/status/
  media/robots/disabled labels) across the interface + en/ru/hy.

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:
sdarbinyan
2026-07-17 09:55:21 +04:00
parent 4861990551
commit cb3dff819e
6 changed files with 356 additions and 12 deletions

View File

@@ -719,6 +719,33 @@ export const hy: Translations = {
backHome: 'Վերադառնալ գլխավոր',
duplicateSlug: 'Կրկնվող slug-երը թույլատրելի չեն։',
emptyTitle: 'Յուրաքանչյուր էջ պետք է ունենա առնվազն մեկ վերնագիր։',
duplicateRoute: 'Երկու կամ ավելի էջ ունեն նույն երթուղին։',
invalidHtml: 'Այս էջը պարունակում է սխալ HTML բովանդակություն։',
invalidSeo: 'Այս էջն ունի անվավեր SEO դաշտ (canonical/OG պատկեր կամ robots արժեք)։',
searchPlaceholder: 'Փնտրել էջեր…',
filterAll: 'Բոլոր կարգավիճակները',
filterAllLocales: 'Բոլոր լեզուները',
confirmDeletePage: 'Ջնջե՞լ այս էջը։ Հնարավոր չէ հետարկել։',
confirmBulkDelete: 'Ջնջե՞լ ընտրված էջերը։ Հնարավոր չէ հետարկել։',
duplicatePage: 'Կրկնօրինակել',
selectAll: 'Ընտրել բոլորը',
selectedCount: 'ընտրված է',
bulkDelete: 'Ջնջել ընտրվածները',
bulkEnable: 'Միացնել ընտրվածները',
bulkDisable: 'Անջատել ընտրվածները',
bulkPublishAction: 'Հրապարակել ընտրվածները',
bulkUnpublish: 'Հանել հրապարակումից ընտրվածները',
routeLabel: 'Երթուղի',
enabledLabel: 'Միացված է',
statusLabel: 'Կարգավիճակ',
publishPageAction: 'Հրապարակել էջը',
unpublishPageAction: 'Հանել էջը հրապարակումից',
heroImageLabel: 'Գլխավոր պատկեր',
thumbnailLabel: 'Մանրապատկեր',
galleryLabel: 'Պատկերասրահ',
customTemplateLabel: 'Հատուկ ձևանմուշ',
seoRobotsLabel: 'Robots',
disabledBadge: 'Անջատված',
},
widgets: {
unavailable: 'Վիջեթը հասանելի չէ',