Commit Graph

294 Commits

Author SHA1 Message Date
sdarbinyan
d8456ecb9f feat(project-editor): header layout + sticky option
- HeaderConfig gains sticky (default true) and layout ('default'|'centered')
- header editor exposes layout select + sticky toggle
- runtime header component applies static/centered classes from config

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 15:29:55 +04:00
sdarbinyan
b183410888 feat(project-editor): reusable image-field (thumbnail+replace+remove), branding OG image+gallery
- shared app-image-field component: thumbnail preview, replace, remove, opens media-picker
- branding: add socialImageUrl + galleryUrls fields, wire to new image-field
- footer: logo + payment icon fields use image-field (drop manual media-picker plumbing)
- i18n: common.remove, adminCategories.replaceImage, builder.socialImage/gallery keys (en/ru/hy)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 15:22:45 +04:00
sdarbinyan
feda0f685f fix(static-pages): backfill enabled/status in export, fix missing i18n key
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
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 <noreply@anthropic.com>
2026-07-17 11:27:29 +04:00
sdarbinyan
82b4a7849a docs(static-pages): backoffice redirect + StaticPages.md
Milestone 7 of the Static Pages Module sprint.

- app.routes.ts: /backoffice/static-pages now redirects to /edit/static-pages
  (absolute redirectTo) instead of rendering BackofficeComingSoonPageComponent
  - Static Pages is a first-class Project Editor module, not a second CRUD
    surface over the same bootstrap.staticPages data.
- New docs/StaticPages.md: full field reference (General/Localization/SEO/
  Media/Publishing), the enabled+status storefront-gating story and its
  backward-compat default (existing/legacy data normalizes to
  enabled+published so nothing gets silently un-published; only new pages
  default to draft), CRUD/search/filter/bulk, the "mutate from the
  unfiltered list" implementation note, rich-text/HTML-mode contract
  (pointer to EDITOR.md), nav integration, and the export/import/draft/
  publish compatibility statement.
- docs/EDITOR.md: Static Pages row in the Sections table (was previously
  absent - the row lived implicitly in Footer's description), HTML editor
  section updated with the Sprint X+2 toolbar additions + validation
  contract, redirect noted near the route line. `docs/Project-Editor.md`
  (named in the original brief) no longer exists - superseded by EDITOR.md
  per that file's own header; documentation went there + the new file
  instead.
- docs/PROJECT.md: doc index entry for StaticPages.md.

Gate: tsc --noEmit, npm test (57/57), arch:check, build all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 10:24:18 +04:00
sdarbinyan
fffbb64e4b feat(static-pages): navigation integration (insert page link)
Milestone 6 of the Static Pages Module sprint.

- ProjectEditorFacade.addStaticPageNavLink(target, pageId): creates a
  NavigationItemConfig { type: 'staticPage', key: pageId }. This shape was
  already understood end-to-end by the resolvers (StaticPageResolverService/
  FooterResolverService derive label+route from the linked page - see
  footer-resolver.service.ts resolveGroupItem/resolveLegacyItem) - the only
  gap was that the editor UI never exposed a way to create it.
- navigation-section: "Insert page link" control (page picker + button) next
  to both header and footer "Add link". Rows for a static-page link show a
  "Linked to page" indicator instead of the raw label/URL inputs (those
  fields don't apply - the resolver derives them dynamically). labelOf()
  falls back to the page id for the row heading since a static-page link has
  no label of its own.
- i18n: builder.insertPageLink, builder.linkedToPage in interface + en/ru/hy.

Verified (no rebuild needed) that pages already participate in preview(),
exportBootstrap()/importBootstrap(), and draft/publish gating - all flow
through bootstrap.staticPages and the M1-M2 additive fields untouched here.

Gate: tsc --noEmit, npm test (57/57), arch:check, build all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 10:18:08 +04:00
sdarbinyan
35ce9ee78a feat(static-pages): live device preview (desktop/tablet/mobile)
Milestone 5 of the Static Pages Module sprint.

- New StaticPagePreviewComponent: client-side, sanitized HTML preview at
  desktop/tablet(768px)/mobile(375px) widths, entirely without navigation or
  publish. Uses the same DomSanitizer.sanitize(SecurityContext.HTML, ...)
  pattern as the real storefront renderer (StaticPageComponent), so what
  authors preview here matches what will actually render live.
- Wired into static-pages-editor as a per-page collapsible "Preview" toggle,
  showing the default-locale (or first available) translation's html/title.
- i18n: staticPages.previewDesktop/Tablet/Mobile/Toggle in 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>
2026-07-17 10:11:31 +04:00
sdarbinyan
910690c4d1 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>
2026-07-17 10:05:50 +04:00
sdarbinyan
bfee935798 feat(static-pages): rich text extensions + HTML-mode validation
Milestone 3 of the Static Pages Module sprint.

- MarketplaceHtmlEditorComponent toolbar: horizontal rule
  (insertHorizontalRule), code block (formatBlock -> PRE), embed (prompt for
  a URL, insert a sandboxed <iframe sandbox="allow-scripts allow-same-origin"
  loading="lazy">, same prompt-based UX as the existing link/image commands -
  no new dependency, consistent with the documented no-external-rich-text-
  library decision).
- toggleCode() now validates raw HTML via schema/validators/primitives'
  validateHtml (added in M1) before committing it back to the visual surface;
  on failure it stays in code mode with an inline error instead of silently
  writing malformed markup into the contenteditable surface. Error clears on
  the next edit.
- i18n: builder.promptEmbedUrl, builder.htmlEditorInvalidHtml in 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>
2026-07-17 10:00:33 +04:00
sdarbinyan
cb3dff819e 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>
2026-07-17 09:55:21 +04:00
sdarbinyan
4861990551 feat(static-pages): extend data model with route/enabled/status/media/SEO
Milestone 1 of the Static Pages Module sprint.

- StaticPageConfig / ContentPage / ContentPageBootstrapInput gain: explicit
  editable route (defaults from slug, independently overridable), enabled
  (master on/off), status: 'draft'|'published' (per-page publish lifecycle,
  independent of the whole-bootstrap draft/publish cycle), customTemplate,
  heroImage/thumbnail/gallery, and seo.robots.
- ContentPageService: normalizePage/normalizePages default missing
  enabled/status to enabled+published so existing bootstrap data never gets
  silently un-published; only the editor's createPage() opts a brand-new page
  into 'draft'. Legacy array-format pages get the same treatment.
- resolvePage now returns null (storefront 404) for a disabled or draft page,
  regardless of whether the surrounding bootstrap itself is published -
  affects the storefront static-page route AND the auto-generated footer nav
  group (both go through this same resolver), which is the correct behavior.
- validatePages extended: duplicateRoutes (route can now diverge from slug),
  invalidHtml, invalidSeo (canonical/ogImage URL shape, known robots tokens).
- New schema/validators/primitives.validateHtml: stack-based tag-balance
  check (void/self-closing elements skipped, comments stripped). Caught and
  fixed a real bug during its own spec run: the initial implementation popped
  the stack back to the nearest matching ancestor on a mismatched closing
  tag, which silently swallowed a genuinely unclosed inner tag instead of
  flagging it - now a closing tag must match the top of the stack exactly.
- toBootstrapRecord serializes the new fields; visible mirrors enabled so any
  reader of the older field name stays truthful.
- Specs: content-page.service.spec.ts (new), primitives.spec.ts (validateHtml).

Gate: tsc --noEmit, npm test (57/57), arch:check, build all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:43:00 +04:00
sdarbinyan
274f2a4101 fix(project-editor): close redo-staleness window, dedupe footer URL check
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Independent review pass over the Configuration Engine sprint (M1-M6).

- Facade: a fresh edit burst now clears the redo (future) stack immediately,
  not just once its debounced commit lands ~300ms later. Previously, editing
  right after an undo left canRedo() true for that window; clicking Redo
  during it would have silently discarded the new edit and jumped back to
  the stale future snapshot. Reordered two interspersed imports/interface
  for readability while in the file.
- footer-section: removed a local HTTP_URL regex + duplicate isValidUrl
  logic (its "shared/ui can't import features" justification didn't apply -
  this file already lives in features/project-editor/sections/, the same
  feature as schema/validators/). Now calls isValidHttpUrl from
  schema/validators/primitives, closing a validator duplication the sprint's
  "no duplicated validators" requirement was meant to catch.

Gate: tsc --noEmit, npm test (33/33), arch:check, build all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:10:54 +04:00
sdarbinyan
8d317f043c docs(project-editor): document config schema, form engine, validation
Milestone 6 (final) of the Configuration Engine sprint.

- docs/EDITOR.md: new "Configuration schema, form engine, and validation
  architecture" section covering the field-schema registry, centralized
  validators, live inline feedback, undo/redo, modified-field tracking, and
  pre-publish preview added in M1-M5. Updated the facade signal list and
  folder tour to include schema/.
- ADR-0002 (docs/context/adrs/): records the metadata-augmented-vs-fully-
  schema-driven decision, why severity splits blocking/advisory, and the
  accepted debt (partial [error] binding coverage, schema not yet driving
  template labels).
- FACTS.jsonl (project-editor): decision fact pointing at the ADR.

Note: `barry-cache` is a phantom devDependency (no bin resolves, confirmed in
M1) - ADR/FACTS were authored by hand matching the existing schema/format
rather than via `npm run barry -- adr new` / `validate`.

Gate: tsc --noEmit, npm test (33/33), arch:check, build all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:05:32 +04:00
sdarbinyan
96b12a1fbe feat(project-editor): pre-publish change + validation preview
Milestone 5 of the Configuration Engine sprint.

- Facade: changeSummary computed - per modified field, before/after values
  (schema label + stringified diff vs originalBootstrap), reusing
  modifiedFields from M4.
- preview-section: new "changes since last publish" card ahead of the
  existing export/import/live-preview card - validation issue list
  (warning/error styled) plus a before/after change table. Reuses the
  existing, non-destructive ProjectEditorPreviewService.preview() call.
- i18n: previewChangesTitle/NoIssues/NoChanges/ChangeField/Before/After in
  interface + en/ru/hy.

Gate: tsc --noEmit, npm test (33/33), arch:check, build all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:00:47 +04:00
sdarbinyan
1db0d4dfea feat(project-editor): session undo/redo and modified-field tracking
Milestone 4 of the Configuration Engine sprint.

- Add schema/history.util: pure undo/redo reducer (commit/undo/redo, depth cap)
  with full spec coverage.
- Facade: debounced snapshot history (~300ms coalesce so a typing burst = one
  undo step); undo()/redo() route through the draft-save path so autosave never
  desyncs; canUndo/canRedo; history cleared on load/publish/resetDraft.
  modifiedFields (schema-diff vs original) + modifiedSections computeds.
- save-bar: Undo/Redo buttons. Page: Ctrl/Cmd+Z / Shift+Z / Y shortcuts
  (skipped while a text field is focused so native text undo is preserved);
  beforeunload guard already present.
- nav: amber modified-field dot per section (when no blocking badge).
- i18n: builder.undo / builder.redo in interface + en/ru/hy.

Gate: tsc --noEmit, npm test (33/33), arch:check, build all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 02:13:34 +04:00
sdarbinyan
a7bab6be52 feat(project-editor): live inline validation and publish gating
Milestone 3 of the Configuration Engine sprint.

- Facade fieldError(key) accessor over issuesByField for inline field errors.
- Bind [error] on schema-backed fields: theme palette colours, general
  name/domain, branding logo (translated via each section).
- project-editor-nav: per-section blocking-issue count badge (issuesBySection).
- save-bar: Publish now disabled on hasBlockingIssues() (errors only, so new
  warnings no longer block); issue list tags warning vs error severity.

Editor verified rendering at /ru/edit/theme with the new nav + save bar;
validation logic covered by the 25 unit tests.

Gate: tsc --noEmit, npm test (25/25), arch:check, build all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 02:07:14 +04:00
sdarbinyan
7b8382131d feat(project-editor): centralize schema-driven validation
Milestone 2 of the Configuration Engine sprint.

- Add schema/validators/primitives: pure isValidHexColor/HttpUrl/Email,
  validateJson, validateCss, extractStyleBlocks, normalizeRoute. One function
  per concern, no duplicated validator logic.
- Refactor ProjectValidator to compose the primitives and tag every issue with
  section + fieldKey + severity ('error' blocks publish, 'warning' advisory).
  Preserves all existing codes/messages; adds duplicate-routes, invalid-css,
  invalid-widget-config checks.
- Facade: issuesByField, issuesBySection, blockingIssues, hasBlockingIssues;
  publish() now gates on severity==='error' instead of any issue.
- i18n: add validationInvalidJson/Css/DuplicateRoutes/InvalidWidgetConfig to
  the Translations interface + en/ru/hy.
- Specs: primitives + ProjectValidator (25 passing total).

Gate: tsc --noEmit, npm test (25/25), arch:check, build all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:58:36 +04:00
sdarbinyan
3bfe820443 feat(project-editor): add field-schema registry + test harness
Milestone 1 of the Configuration Engine sprint.

- Add schema/ registry: FieldSchema model, SECTION_FIELD_SCHEMAS covering
  every editable field per section, and EditorSchemaService (getFields,
  getField, all, getByPath). Single source of truth for labels, defaults,
  and validator references; sections stay hand-authored (metadata-augmented).
- Stand up Karma + Jasmine (ng test) with a headless, sandbox-free Chrome
  launcher; add tsconfig.spec.json, karma.conf.js, angular.json test target,
  and npm "test" script. First spec: editor-schema.service.spec (7 passing).

No behavior change. Gate: arch:check, tsc --noEmit, npm test (7/7), build all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:51:13 +04:00
sdarbinyan
54725c624e docs: add backend integration guide + implementation prompt for B2B
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Document how the B2B storefront sends/gets data vs main (base-URL
resolution, bootstrap fetch, interceptor chain, headers) and the new
builder/backoffice surface awaiting a real API. Add a self-contained
hand-off prompt. Login and payments left untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 00:29:26 +04:00
sdarbinyan
3474581122 docs: add backend diff-vs-main + sales guide, document editor motion & HTML editor
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
- docs/BACKEND-DIFF-VS-MAIN.md: backend handoff summary framing BACKEND.md
- docs/SALES-GUIDE.md: non-technical demo/enablement guide
- docs/EDITOR.md: document interaction/motion pass and HTML editor status

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:37:29 +04:00
sdarbinyan
ee1cbdf38b style(ui): full UX/UI + motion pass across storefront, admin, editor
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
- fix save-bar buttons to use shared app-button primitive (were unstyled)
- fix platform-nav-group border/radius via structural selectors, drop dead
  -middle/-right classes
- storefront widgets (hero/categories/product-carousel/footer-nav): design
  tokens, hover/focus states, 44px touch targets, entrance motion, reduced-
  motion guards
- admin dashboard cards + quick-actions: hover lift, entrance animation
- admin product-form gallery remove badge: hover/focus + expanded hit area
- project-editor section.shared button styles: hover/active/focus/disabled
  states + reduced-motion; section-switch fade-in motion

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:32:31 +04:00
sdarbinyan
897c1f3196 docs(project-editor): document new shared/ui primitives and fixed bugs
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 02:16:29 +04:00
sdarbinyan
b61bf0e5bc chore(project-editor): remove now-redundant .editor-section-card rule
All 11 section components use app-section-card now; the raw shell class
had no remaining consumers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 02:14:40 +04:00
sdarbinyan
8021b362cf style(project-editor): align nav with design-system tokens, add aria-current
- use ariaCurrentWhenActive on routerLinkActive so the active tab exposes
  aria-current="page"
- restyle editor-nav-link with the same --primary-color/--bg-primary/
  --border-color/--text-primary/--space-*/--transition-fast tokens used
  across shared/ui/*

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 02:14:03 +04:00
sdarbinyan
da0f0cd4a4 feat(project-editor): adopt SectionCard in preview section
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 02:14:03 +04:00
sdarbinyan
058e2d571f refactor(project-editor): toggles + remove as-any casts in features section
- checkboxes -> app-toggle, wrap in SectionCard
- toggleUserExperience/toggleProductFeature typed without 'as any' (optional
  chaining on the already-typed UserExperienceConfig/ProductPageConfig union)
- removed dead, broken toggleRecentViewed method (unused, not wired to the
  template; toggleUserExperience('recentlyViewed', ...) is the live path)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 02:12:25 +04:00
sdarbinyan
0c9a855232 fix(project-editor): editable nav link labels per locale, add LocaleTabs
- navigation-section: add LocaleTabs; label input now reads/writes the
  active locale's translation via a new editableLabel() helper instead of
  always the default locale. facade.updateNavLinkLabel() gained an optional
  locale param (defaults to current default locale, so existing callers
  are unaffected) and correctly promotes a plain-string label into a
  per-locale map when writing a non-default locale.
- languages-section: wrap in SectionCard, add LocaleTabs for consistency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 02:10:38 +04:00
sdarbinyan
1c51a819ed feat(project-editor): adopt Toggle/SectionCard in homepage and widgets sections
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 02:07:41 +04:00
sdarbinyan
475e5ad528 fix(project-editor): validate footer payment/social links, add footer logo picker
- header-section: raw checkboxes -> app-toggle, wrap in SectionCard
- footer-section: replace unvalidated pipe-delimited textareas for payment
  icons/social links with KeyValueEditor + MediaPickerComponent, add missing
  footer logo picker, validate social link URLs (http/https), wrap in SectionCard
- i18n: add footer logo / key-value-editor labels and URL validation message

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 02:06:26 +04:00
sdarbinyan
ccbf8c6e5c feat(project-editor): migrate theme section to Select/ColorPicker/SectionCard
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 02:01:34 +04:00
sdarbinyan
3bf1f31a3c feat(project-editor): adopt SectionCard in general/branding sections
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 01:59:54 +04:00
sdarbinyan
e18f542357 feat(shared-ui): add toggle, select, color-picker, section-card, locale-tabs, key-value-editor primitives
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 01:59:45 +04:00
sdarbinyan
b8d89ca8e7 docs: mark Sprint 30 final verify pass complete
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 00:25:00 +04:00
sdarbinyan
2a133fb40b chore: release candidate
Sprint 29, scoped to what wasn't already covered by concurrent work in
this session.

- dead code check: grepped console.log/console.debug/console.warn/
  debugger/TODO/FIXME across features/admin/** - none found. tsc
  --noUnusedLocals --noUnusedParameters over features/admin/** - clean,
  no dangling imports/params.
- verified tsc --noEmit, ng build, arch:check:boundaries, and
  arch:check:cycles all pass against the current working tree
- added CHANGELOG.md and RELEASE-NOTES.md at repo root summarizing
  Sprints 20-28

Translation validation is deliberately not duplicated here - the ~178
missing adminXxx.* i18n keys are already logged in docs/KNOWN-ISSUES.md
and being addressed there. No lint script exists in package.json, so a
lint pass isn't applicable.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 00:22:01 +04:00
sdarbinyan
173ceb8081 feat(seo): tenant-driven meta tags, sitemap/robots, reduced-motion, docs
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Completes the rest of Sprint 28 on top of the earlier admin-scoped
a11y/skeleton pass (576f260):

- SeoService.resetToDefaults() now reads real bootstrap.seo.default /
  branding instead of hardcoded placeholder text + a broken
  /og-image.jpg reference; auto-reapplies via an effect() whenever
  bootstrap (re)loads, same pattern as UiRuntimeFacade.
- New public/sitemap.xml (static baseline, documented per-tenant-dynamic
  limitation) + public/robots.txt Sitemap directive and admin/editor
  Disallow rules.
- Global prefers-reduced-motion override in styles.scss covering every
  existing hover-transform/fade-in/shimmer animation in one place.
- New adminProducts/adminUsers/adminMonitoring/adminAnalytics
  empty-state i18n keys (en/ru/hy) for this sprint's skeleton/empty-state
  consistency fixes.
- docs/KNOWN-ISSUES.md: logged a newly-found, much larger pre-existing
  gap (~178 missing adminXxx.* i18n keys across the whole admin
  backoffice) - deferred to Sprint 29's translation validation, not
  fixed here.
- docs/BACKEND.md: new item 17 (sitemap generation gap).
- docs/ADMIN.md, docs/SPRINT-PLAN.md: rewritten Sprint 28 sections to
  describe the full, combined scope (both commits) instead of the
  earlier admin-only framing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 00:19:41 +04:00
sdarbinyan
576f2600a5 refactor: marketplace release polish
Sprint 28, scoped to admin/* (user decision — full marketplace audit
declined in favor of a bounded pass over the 8 admin features from
Sprints 20-27).

- a11y: aria-label added to every bare <select> not already inside a
  <label> across categories/products/orders/transactions/users/monitoring
- loading states: app-skeleton rows/cards added to list pages that
  previously rendered blank during the initial fetch (categories, orders,
  transactions, users, monitoring's event feed, analytics summary cards)
- admin-dashboard-card's custom shimmer CSS replaced with the shared
  SkeletonComponent (same visual result, one less duplicated animation)
- bundle-size budget warning (~198kB over) confirmed pre-existing —
  present at Sprint 20's first build before any admin/* code existed,
  and new admin pages are all lazy-loaded — documented as out of scope
  for this pass rather than chased

docs/ADMIN.md + docs/SPRINT-PLAN.md updated with the scope decision and
what was explicitly not done (Lighthouse, animations, SEO/sitemap,
storefront/editor a11y).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 00:14:46 +04:00
sdarbinyan
1db63ac99d fix(i18n): add missing actionUsers/Monitoring/Analytics dashboard keys
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Quick Actions rendered raw i18n keys instead of translated labels for the
Users/Monitoring/Analytics actions. Also reverts useMockData back to its
pre-session value (false) after manual local verification.

Adds docs/KNOWN-ISSUES.md to track bugs found during manual QA, deferred
for a batch fix after the sprint.
2026-07-15 20:25:12 +04:00
sdarbinyan
88cc131fdc feat(admin): analytics dashboard
Sprint 27.

New features/admin/analytics/ module + net-new /:lang/backoffice/analytics
route + Dashboard Quick Action.

- revenue/orders/avg-order-value/sales-over-time/top-products computed by
  composing AdminOrdersLocalGateway (Sprint 23's seeded mock orders) - real
  aggregation over mock data, not a separate fabricated dataset
- products/categories counts from AdminProductsLocalGateway/
  AdminCategoriesLocalGateway
- visitors/funnels/heatmaps render pending-backend badges (no analytics
  pipeline exists anywhere in this system) rather than fabricated numbers,
  same convention as the Sprint 19 dashboard's pre-Sprint-23 Orders/Revenue
  cards
- plain div-bar chart (no charting library), 7/30/90-day range toggle,
  CSV export

docs/ADMIN.md + docs/BACKEND.md (new item 16) updated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 11:17:55 +04:00
sdarbinyan
a67ea17ad2 feat(admin): monitoring center
Sprint 26.

New features/admin/monitoring/ module + net-new /:lang/backoffice/monitoring
route + Dashboard Quick Action.

- Health section reuses AdminDashboardFacade.healthChecks directly (real
  data, unchanged since Sprint 19) instead of duplicating the logic
- unified AdminMonitoringEvent feed covering audit/security/login/
  failed-login/api/error/warning, category filter + search, 40 seeded
  synthetic entries (no logging backend exists anywhere in this system)
- mock queue depth/status cards, mock webhook delivery log
- intentionally kept separate from Sprint 24's per-transaction audit and
  Sprint 25's per-user audit - different scopes, no consolidation attempted

Also fixed a real type error: AdminDashboardQuickActionId's union was
missing 'users' and 'monitoring' (added when wiring those Quick Actions),
caught by ng build's template type-checking even though plain tsc --noEmit
passed - a reminder that ng build is the authoritative check here.

docs/ADMIN.md + docs/BACKEND.md (new item 15) updated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 11:12:47 +04:00
sdarbinyan
17adc9e9fd feat(admin): users and permissions
Sprint 25.

New features/admin/users/ module, net-new /:lang/backoffice/users route +
Dashboard Quick Action.

- users: name, Telegram username, scope (marketplace vs office admin),
  role (inline change), status (active/invited/suspended), last login
- 4 built-in roles (owner/admin/editor/viewer) with flat permission lists
- invitations: email + role + scope form, pending list + revoke (no email
  actually sends - local record only)
- passwordless login confirmed already real (AdminAuthService Telegram QR,
  docs/BACKEND.md item 1) - linked, not reimplemented
- per-user mock session list (device/IP/last-active, revoke) - flagged as
  mock since the real AdminAuthService only ever tracks the current
  browser's session
- per-user audit log dialog (role/status changes), same pattern as
  Sprint 24's per-transaction audit, intentionally separate from the
  system-wide log planned for Sprint 26

docs/ADMIN.md + docs/BACKEND.md (new item 14) updated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 11:05:21 +04:00
sdarbinyan
7d65913245 feat(admin): transaction management
Sprint 24.

New features/admin/transactions/ module. AdminTransactionsLocalGateway
derives one synthetic transaction per Sprint 23's seeded mock order rather
than a separate dataset, keeping order numbers/totals consistent across
the two mock feature areas.

- list: search, status filter, type filter (payment/refund/qr_payment),
  pagination, CSV export
- retry failed transactions (appends an audit entry)
- fraud flag toggle
- per-transaction audit log (creation/retry/fraud-flag-change), viewed via
  dialog - intentionally separate from the system-wide audit/security log
  planned for Sprint 26 (Monitoring)
- wired into /:lang/backoffice/transactions, replacing the coming-soon
  placeholder

docs/ADMIN.md + docs/BACKEND.md (new item 13) updated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 10:57:29 +04:00
sdarbinyan
2d8d6b6dc4 feat(admin): order management
Sprint 23.

New features/admin/orders/ module, same container/facade/service split as
admin/products and admin/categories.

- AdminOrder model + AdminOrdersLocalGateway seeding 24 deterministic
  synthetic orders (no real order data source exists anywhere in this
  repo - explicitly a placeholder, not a mock of production volume)
- list: search, status filter, pagination, CSV export (client-side Blob
  download)
- detail: customer/payment/shipping, itemized total, status timeline,
  change-status dropdown, refund request + cancel (window.confirm-gated),
  separate customer-facing vs internal notes, print invoice via
  window.print() with @media print hiding non-invoice chrome
- wired into /:lang/backoffice/orders(/:id), replacing the coming-soon
  placeholder

docs/ADMIN.md + docs/BACKEND.md updated; dashboard's Orders/Revenue cards
(Sprint 19) remain intentionally un-wired to this mock and still render
pending-backend.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 10:50:59 +04:00
sdarbinyan
30afb5d778 feat(media): reusable media management
Sprint 22.

- MediaAsset gains folder (flat) and MediaListParams gains folder/tag
  filters; MediaRepository.listFolders() derives the folder list from
  existing records
- upload validation: 10MB size cap, mime allow-list (jpeg/png/webp/gif/
  svg+xml/pdf), real error messages surfaced through MediaLibraryFacade
  instead of a generic swallowed string
- SVG uploads are sanitized (script tags and on*= attributes stripped)
  before storage
- raster images (excl. gif) are downscaled to a 2000px max dimension and
  re-encoded via canvas before storage - compression, not a crop UI
- tag editing (window.prompt, comma-separated) via
  MediaLibraryFacade.updateTags()
- MediaPickerComponent wired into Project Editor branding (logo, compact
  logo, favicon) alongside its existing category/product usage - confirmed
  no image fields exist on Static Pages or as a dedicated hero field to
  wire

docs/ADMIN.md updated with the new Sprint 22 section including the storage
abstraction note (MediaRepository was already the abstraction).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 10:42:23 +04:00
sdarbinyan
60c63a0d2a feat(admin): complete product management
Sprint 21.

- archived (soft archive/restore, distinct from visible) with an
  include-archived list filter
- barcode field alongside sku
- variants: lightweight name|price|quantity list, same textarea-parse
  convention as specifications/attributes
- relatedProductIds: checkbox picker in the editor
- gallery images now added/removed via the shared MediaPickerComponent
  instead of a raw URL textarea
- read-only discounted-price preview in the editor
- infinite-scroll toggle on the list (loadMore() appends a page instead
  of replacing it; pagination UI swaps for a Load more button)
- category dropdown now sourced from AdminCategoriesGateway (Sprint 20)
  instead of AdminProductsLocalGateway's own BackofficeDataService seed

docs/ADMIN.md + docs/BACKEND.md updated with the new field list and the
known trade-off that related-products search is scoped to the currently
loaded page, not the full catalog.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 10:31:00 +04:00
sdarbinyan
6a8c4a549a feat(admin): complete category management
Sprint 20. Adds features/admin/categories/ (model, gateway interface +
local gateway, facade, list/editor pages), mirroring the admin/products
container/facade/service split.

- indented hierarchy view + native HTML5 drag-and-drop reorder
- visibility toggle, item counter, empty state, include-deleted filter
- editor: slug uniqueness validation, translations, SEO fields, breadcrumb
  preview, image via existing MediaPickerComponent
- soft delete/restore, blocked when a category has children or items
- draft/publish status + localStorage draft recovery (mirrors Project
  Editor autosave) + CanDeactivate unsaved-changes guard
- wired into app.routes.ts (replaces the categories coming-soon placeholder)
- docs/ADMIN.md + docs/BACKEND.md updated with the new gap detail

Not yet done: admin/products' category dropdown still reads from its own
AdminProductsGateway.loadCategories() rather than this gateway (Sprint 21).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 09:34:13 +04:00
sdarbinyan
dbd905b02f fix(design-system): wire label/hint/error accessibility from FormField to Input
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
FormFieldComponent's label had [for]=fieldId but nothing ever gave the
projected control a matching id, and aria-describedby was set on a wrapper
div instead of the actual input - so screen readers announced neither the
label association nor the hint/error text.

Added FormFieldContext, an injectable abstract class FormFieldComponent
provides via its own providers array (visible to content-projected children,
same mechanism Angular Material's mat-form-field/matInput relationship
relies on). InputComponent optionally injects it and self-applies id and
aria-describedby when nested inside app-form-field.

This retroactively fixes every form built across Sprint 3-5 (Static Pages
editor, Media Manager, Products List/Form, Project Editor sections) with
no template changes needed anywhere else.

Verified in browser: input id matches label's for attribute, and
aria-describedby correctly points to the rendered hint/error text.

Completes Sprint 5 (UI/UX Polish): Admin Products List, Admin Product Form,
Project Editor sections (9 of 11), and this accessibility fix.
2026-07-15 08:01:54 +04:00
sdarbinyan
f82344e0dd feat(project-editor): adopt Design System primitives across editor sections
Applied app-input/app-form-field/app-button to 9 of 11 Project Editor
sections: general, branding, theme, footer, homepage, widgets, languages,
navigation, preview. header and features sections were left unchanged -
they contain only checkboxes and selects, and no Checkbox/Select primitive
exists yet.

Theme section's 8 color pickers stay native <input type=color> (app-input's
type union doesn't include 'color') but are now wrapped in app-form-field
for consistent label/hint treatment. Added app-form-field.full grid-column
rule to section.shared.scss (shared by all 11 sections) alongside the
existing label.full rule, since the custom element doesn't match that
selector.

Production build green, arch:check passes.
2026-07-15 07:55:38 +04:00
sdarbinyan
53d6a8e169 feat(admin-products): adopt Design System primitives in Product Form
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Replace single-line text/number inputs with app-form-field/app-input across
name, slug, sku, brand, priority, pricing, quantity, availability, per-locale
translation name/shortDescription, SEO metaTitle/keywords, and badges fields.
Save button now app-button. Textareas, selects, and checkboxes stay native -
no Textarea/Select/Checkbox primitive exists yet. Added app-form-field.full
grid-column rule alongside the existing label.full one (custom element,
different selector).

Production build green, arch:check passes.
2026-07-15 07:39:56 +04:00
sdarbinyan
dbb22d1392 feat(admin-products): adopt Design System primitives in Products List
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Replace raw button/table/input elements with app-button, app-table, app-badge,
app-input, app-pagination. Removed now-redundant button/table/th/td CSS from
the stylesheet (would have double-styled the primitives' projected content
under Angular's emulated encapsulation). Native selects and checkboxes kept
as-is - no Select/Checkbox primitive exists yet.

Production build green, arch:check passes. In-browser verification hit a
dev-server routing hiccup unrelated to these changes (a temporary unguarded
preview route 404'd despite compiling correctly); relied on the identical,
already-verified primitive usage pattern from the Static Pages editor and
Media Manager instead.
2026-07-15 07:33:25 +04:00
sdarbinyan
9ab807342e feat(media): add reusable MediaPickerComponent
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Standalone dialog (app-dialog, size lg) reusing MediaLibraryFacade so it
shares the exact same asset list/search/pagination/upload state as the
Media Manager page. Emits (selected) with the chosen MediaAsset and
(closed) to dismiss - selecting a tile emits both.

Completes Sprint 4 (Media Manager): ADR-0002 contract, MediaRepository +
mock IndexedDB adapter, Media Manager UI, reusable Media Picker.

Not yet wired into Product Editor or Static Pages editor - that's the
next natural step when those features gain image fields, not part of
this sprint's scope.
2026-07-15 07:20:42 +04:00
sdarbinyan
5ffb353011 feat(media): add Media Manager UI (grid, upload, delete)
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
MediaLibraryPageComponent replaces the coming-soon placeholder at
/backoffice/media. Built entirely on Sprint 6 Design System primitives
(app-card, app-button, app-input, app-empty-state, app-dialog, app-pagination,
app-skeleton) and Sprint 4 Task 2's MediaRepository/MockMediaRepository.

- Grid view with per-tile filename/size and delete action
- Hidden native file input triggered by an app-button, uploads via
  MediaLibraryFacade -> MediaRepository.upload()
- Delete requires confirmation through app-dialog (destructive action)
- Search + pagination wired to MockMediaRepository's list() params
- Loading state shows app-skeleton tiles; empty state shows app-empty-state
- New mediaLibrary.* translation namespace across en/ru/hy

Verified in browser (via a temporary unguarded route, reverted before
commit - /backoffice/media itself requires Telegram QR admin auth not
available in this session): empty state renders correctly with translated
copy, search input and upload button present, no console errors.
2026-07-15 07:14:16 +04:00