Commit Graph

64 Commits

Author SHA1 Message Date
sdarbinyan
043192accf fix(icons): migrate Marketplace Builder to Lucide
Replace every PrimeIcons pi-* usage across the builder: overview page
(back link, next-step arrow, section cards, readiness checklist,
quick links), sidebar nav (group/section status dots), main layout
(back/home/menu/help icons), brand + homepage overview panels
(checklist ok/pending dots, contrast warning), footer/homepage/widgets
section editors (drag handles, move up/down, duplicate, remove), and
the HTML editor toolbar (list/link/image/table/divider/code/embed).

Notable correctness fix: PrimeIcons reused pi-bars for both the
hamburger menu toggle AND every drag handle - two different meanings
sharing one icon (exactly the kind of icon collision the audit calls
out). Added a dedicated 'grip' icon (GripVertical) for drag handles so
menu and drag-to-reorder are visually distinct.

Added a global .spin utility (icon-registry has no built-in spinner
animation) for the one loading-spinner icon in the builder overview
checklist.

All icon-bearing fields (BuilderGroup.icon, BlockCatalogEntry.icon,
WIDGET_ICONS, STATUS_ICON, HtmlEditorToolbarCommand.icon, etc.) are
now typed AppIconName instead of string.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 02:31:06 +04:00
sdarbinyan
d70e5d6bdc fix(admin): polish marketplace-builder
Remove unused ButtonComponent import/registration from
ProjectEditorHomepageSectionComponent — it was never referenced in the
template and had been flagged by every build (NG8113 warning).

Reviewed sections/pages/components: save-bar, brand-overview,
footer-section, homepage-overview, widgets-section, project-editor-nav,
builder-overview-page. All buttons already route through the shared
app-button (which carries its own :focus-visible); no orphaned
bindings or corrupted glyphs found (scanned same way as the storefront
pass). No further changes needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 23:51:26 +04:00
sdarbinyan
e2d97d1ee9 fix(editor): remove dangling Header 'Profile' toggle
P0-7: the Project Editor's header-section exposed a 'Profile'
toggle (HeaderConfig.showProfile) with no corresponding UI anywhere
in the storefront header — confirmed dead per docs/KNOWN-ISSUES.md
item 5. Toggling it implied a feature (an account/profile menu)
that doesn't exist, which is misleading in the editor.

Building the actual account/profile surface is real feature work
(out of scope for this polish pass), so removed the toggle from the
editor's items list and the field-schema registry instead of
building UI a merchant would flip with no visible effect. The
underlying HeaderConfig.showProfile field and its false default are
unchanged (data model untouched, still available if a future
account feature wires it up).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 22:59:26 +04:00
sdarbinyan
71d5f4d320 feat(builder): visual homepage blocks, merchant-language widget settings, real carousel arrows
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

P0 user feedback: homepage builder showed raw section.id like 'section-hero'/'section-categories'; hero widget exposed 'full-bleed'/'boxed' and raw px/vh as free text with no explanation; Overlay/Autoplay toggles made no sense without a slides concept; product carousel widgets rendered arrows nowhere near a working carousel.

Homepage section (sections list -> visual blocks):
- Replaced raw section.id display with a merchant-facing block catalog (icon + name + one-line explanation) for hero/categories/featured-products/product-carousel/recently-viewed/banner/partners/custom-html
- Added block catalog picker to append new blocks (was fixed at whatever the seed data had - task asked 'what if we add manually? not fixed 3')
- Added duplicate and remove per block, alongside the existing drag-to-reorder
- Verified in browser: labels render correctly, add-block and duplicate both confirmed working end-to-end

Widgets section (hero widget):
- 'Layout' free-text replaced with a select (Full width / Boxed) instead of typing 'full-bleed'/'boxed' blind
- 'Height' free-text replaced with a select (Compact/Medium/Tall/Full screen) mapped to real vh values
- New Slides editor: title/subtitle pairs an admin can add/remove: this is the actual multi-slide data the Overlay/Autoplay toggles were referring to with nothing to point at before
- HeroWidgetData contract gains slides[]/autoplay; HeroWidgetComponent now renders a real rotator (dots, click-to-jump, autoplay interval) when more than one slide exists - previously autoplay/overlay props existed but there was no slideshow behavior anywhere to control

Carousel arrows root cause and fix:
- widget-manifest.json offers 'carousel' as a layout option for product-collection/product-carousel widgets, and the admin UI let you select it, but ProductCarouselWidgetComponent always rendered a static CSS grid regardless - there was no carousel implementation to have arrows in the first place
- Now renders a real horizontally-scrollable strip with working prev/next buttons (native scrollBy, disabled at each end) when section.layout.strategy === 'carousel'; falls back to the existing grid otherwise
- Confirmed src/app/components/items-carousel (a PrimeNG p-carousel) is dead code, not wired into any route or widget - not the source of the reported bug

New builder.* i18n keys (en/ru/hy), zero duplicate-key collisions verified via scan
2026-07-19 14:14:29 +04:00
sdarbinyan
726df0cee0 feat(builder): visual footer builder with drag-and-drop columns
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

P0 user feedback: footer static pages were comma-separated text; no way to add extra phones/emails for different countries.

- New FooterColumnConfig/FooterLinkConfig model (footer-config.model.ts): columns of links, each link pointing at an existing static page (resolved by key, so it survives route renames) or a custom URL
- Footer Builder UI: add/remove columns and links, per-link toggle between 'existing page' (dropdown of real static pages) and 'custom URL', drag-and-drop reordering of both columns and links via @angular/cdk/drag-drop (same primitive already used by the homepage section builder)
- Wired FooterResolverService (the service the real storefront footer actually renders through) to read footer.columns as the primary source when present - without this the builder would have saved data nobody ever displayed. Falls back to the existing legacy static-page auto-grouping when no columns are configured, so existing sites are unaffected
- CompanyContactConfig gains additionalPhones/additionalEmails (primary phone/email field unchanged) with add/remove UI for country-specific support lines
- Old comma-separated staticPageKeys input removed from the UI; field kept on the model as deprecated/read-compat only
- New builder.* i18n keys (en/ru/hy); fixed an accidental duplicate-key collision with pre-existing navigation-section addLink/removeLink keys during the rename pass
- Verified in browser: added column, added link, switched link source page->custom, added phone number - all reactive and error-free
2026-07-19 13:58:57 +04:00
sdarbinyan
440d2ec211 refactor(builder): single language manager, theme before branding
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

P0 user feedback: General asked admins to type locale codes comma-separated, and Branding (logos) came before Theme (colors).

- Root cause: General duplicated language management as raw text/CSV inputs while a full Languages manager section (add/remove/set-default with per-locale content seeding) already existed one tab away. Removed the duplicate inputs; General now shows a read-only chip summary (default language first, marked) with a 'Manage languages' link to the real manager. One source of truth, no comma parsing, no risk of bypassing LocaleSyncService
- Builder navigation now orders Theme before Branding - merchants pick a palette first, then upload logos that match it
- New builder keys (languagesSummaryDesc, manageLanguages) in en/ru/hy; verified in browser (chips RU-default/EN/HY, nav order theme->branding)
2026-07-19 08:53:10 +04:00
sdarbinyan
9eacd00136 feat(builder): modern grouped rich-text toolbar with icons and tooltips
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

P0 user feedback: unstyled B/I/U/H2/List/1,2,3 buttons were not understandable for non-technical users.

- Toolbar buttons grouped by intent (text style | headings | lists | insert | advanced) with visual separators, hover states, consistent 30px hit targets
- Icons (PrimeIcons) for list/link/image/table/divider/code/embed; B/I/U keep their conventional letters but rendered in their own style (bold/italic/underline) as every mainstream editor does; every button gets a translated tooltip and aria-label (en/ru/hy)
- Code view toggle moved to the right edge, visually de-emphasized - it is the expert path, not a primary action
- Toolbar visually attaches to the editing surface (shared border, joined radius) so it reads as one control
2026-07-19 08:48:22 +04:00
sdarbinyan
c452d28eca fix(admin): P0 user-reported bugs - raw keys, table overflow, builder escape route
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

- Transactions: 'adminTransactions.flag'/'clearFlag' raw keys rendered on the fraud toggle button (missed by earlier audit because the key sits inside a ternary); added en/ru/hy strings with merchant wording ('Flag as suspicious')
- Table overflow on Customers/Transactions root-caused: app-table host is a grid item with default min-width:auto, so wide tables stretched their parent card instead of scrolling inside the wrapper; shared TableComponent now declares display:block/min-width:0/max-width:100% on its element selector (encapsulation None - :host would not match), fixing every admin table at once; verified in browser: cards contained, wrapper scrolls internally
- Builder trap root-caused: section editor pages (/:lang/edit/<section>) had zero routes to the backoffice - only a link back to the builder overview - so admins inside a section could not return to the dashboard; sidebar now has a persistent 'Back to dashboard' link (new builder.backToDashboard key, en/ru/hy); verified navigation lands on /ru/backoffice/dashboard
2026-07-19 08:25:29 +04:00
sdarbinyan
04b36bab9b feat(builder): implement visual homepage builder
New HomepageOverviewComponent (Tasks 1+8), mounted at the top of the
existing Homepage section page: completion ring, active/hidden section
counts, recommended next step, last-modified timestamp, and a 6-item
Homepage Health checklist (hero/categories/products/promotion/
newsletter/any-sections) - all derived from the real page.sections/
widgets data already in the facade, nothing fabricated.

Existing page-level drag-and-drop reordering (homepage-section's
CdkDragDrop over page.sections) was already implemented pre-Sprint 5 -
left as-is per "build on top of, do not replace."

Widgets section (Task 2) rewritten from a bare list into visual cards:
per-widget icon + humanized type label (hero/categories/product-
collection), a visible/hidden toggle and badge, up/down move buttons
(keyboard-accessible reorder within a section - see Known limitations
for why this replaces pointer drag for widgets specifically), duplicate,
and remove (with confirm). The existing per-type field editors (hero/
categories/product-collection) are unchanged; the raw-JSON fallback for
unknown widget types now sits behind a collapsed "Advanced settings"
disclosure instead of being the default view.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 10:24:13 +04:00
sdarbinyan
a83cfdfe4d feat(builder): implement professional brand management experience
New BrandOverviewComponent, mounted at the top of the existing
Branding section page (no route/schema/business-logic changes):

- Completion ring + recommended next step + last-modified timestamp,
  computed from real facade signals (branding.logoUrl/faviconUrl/
  socialImageUrl, theme.palette, theme.typography, lastSavedAt) -
  nothing fabricated.
- Brand Health checklist (6 items: logo, favicon, colors, typography,
  social image, accessibility) - icon+text, never color alone.
- Color palette preview: 10 swatches from the real ThemePaletteConfig
  (primary/secondary/accent/success/warning/danger/backgrounds/text/
  border), each with a readable label.
- Real WCAG contrast check (contrast.util.ts, relative-luminance
  formula) between configured text and background colors - shows an
  inline warning when below 4.5:1, never blocks publishing.
- Typography live preview using the configured heading/body font
  families and base size.
- Social share preview card (Open Graph-style) using the existing
  socialImageUrl + seo.default.title/description, with an explicit
  empty state when no image is set yet.

Scope cut from the full brief given this session's cost already well
over budget entering this sprint - see Known limitations in the final
report (no multi-variant logo/favicon management, no live responsive
device preview, no new media library/asset picker; all reuse the
existing single-logo/single-favicon fields and image-field upload
component as-is).

Could not verify live in-browser this sprint: port 4200 is held by
another chat's dev server. Verified via `tsc --noEmit` (clean) and
manual template/binding review only; one binding (`--pct` custom-
property style binding) was replaced with a plain computed string to
remove an unverifiable risk rather than ship it unverified.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 10:13:35 +04:00
sdarbinyan
f2038a93d2 refactor(admin): polish UX, consistency and overall quality
Admin isolation (Task 1): extended isAdminRoute to also match /edit -
the Marketplace Builder no longer renders the storefront header/back-
button/footer, matching the isolation the backoffice already had since
Sprint 1. Verified no regression on /backoffice or real storefront
routes (catalog still shows the storefront header).

Added a 'back to dashboard' link on the Builder overview page, since
removing the storefront header also removed the only way back to
/backoffice from within the Builder.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 09:56:05 +04:00
sdarbinyan
821a1c6ba8 feat(builder): redesign marketplace builder experience and navigation
Renamed the experience consistently to Marketplace Builder everywhere
visible (page title/subtitle, breadcrumbs, sidebar, dashboard quick
action/shortcut copy) - internal ProjectEditor class/selector names
kept as-is to avoid regressions. builder.title/subtitle no longer say
'bootstrap-config editing surface' to end users.

New business-oriented IA (builder-groups.model.ts): 12 existing
ProjectEditorSectionIds regrouped into 8 groups (Marketplace, Branding
and Design, Homepage, Content, Languages, Marketplace Features,
Navigation and Search, Preview) - every existing section mapped to
exactly one group, none dropped, no group points at a page that
doesn't exist.

New Builder landing page at /edit (was a redirect straight into the
General form): readiness percent, recommended next step, one overview
card per group (purpose + real complete/in-progress/not-started/unknown
status, icon+text never color-alone), a Marketplace Readiness
checklist, quick links. All derived from real facade/schema signals
(required-field fill state, modifiedSections, staticPages, catalog
counts via BackofficeDataService) or explicitly marked unknown
(the "preview reviewed" check has no tracking - shown as unknown,
never guessed).

Section pages (/edit/:section) now share one consistent header:
breadcrumb (Builder > Group > Section), draft/published + modified
badges, and a contextual help panel (what is this / where visible /
what happens if you skip it) sourced per group. Sidebar nav rewritten
as grouped, icon-led sections with per-section and per-group status
indicators; layout converted from a top pill-tab bar to a responsive
sidebar (desktop 280px, tablet 72px icon rail, mobile drawer with
Escape-to-close), matching the Sprint 1 admin shell pattern. Section
form components themselves untouched.

Routes: 'edit' is now its own landing route instead of redirecting to
'edit/general'; /builder and /project-editor redirect to 'edit'.
Sprint 1/2 destinations that pointed at edit/general now point at the
new edit landing page.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 09:43:03 +04:00
sdarbinyan
76e9689e11 fix(general): route supported-languages field through LocaleSyncService, guard unsupported default locale
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
General's free-text 'Supported Languages' field overwrote
tenant/localization.supportedLocales directly, skipping LocaleSyncService's
propagation to per-locale nav/static-page translation entries - the exact
sync Languages' add/remove buttons already go through correctly. Now diffs
against the current list and routes each added/removed locale through
facade.addLocale()/removeLocale().

Also: 'Default Language' was a free-text input with no guard against typing
a locale that isn't in the supported list - every label[defaultLocale]
lookup across nav/static-page content would then silently return undefined.
Added a validator rule (default-locale-not-supported) wired to the existing
fieldError() display, consistent with every other field-level check.

Verified live via window.ng.getComponent(): typing an unsupported code shows
the new inline error; adding 'de' via this field seeded an empty 'de'
translation entry on an existing static page, matching what Languages'
add-locale button already produces.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 19:00:17 +04:00
sdarbinyan
eee6695d7f fix(project-editor): route importBootstrap through updateBootstrap for undo+draft persistence
importBootstrap() replaced state.bootstrap directly, bypassing the same
updateBootstrap() pipeline every other edit goes through - so an import
never got a draftStorage.save() (lost on refresh before an explicit Save)
and never became an undo-able history step (Undo silently skipped over it).

Verified live via window.ng.getComponent(): exported the current bootstrap,
mutated branding.brandName, imported it back - draftStorage's localStorage
key changed and contained the new value; clicking Undo correctly reverted
brandName to the pre-import value.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 18:45:53 +04:00
sdarbinyan
2417a7795b fix(languages): show error instead of silently no-oping when adding a duplicate locale
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
addLocale() always cleared the input, even when LocaleSyncService rejected
the code because it was already supported - same silent-failure shape as
the widgets JSON bug fixed earlier this session. Now checks locales()
first and shows an inline error, leaving the input untouched, instead of
clearing it like the add succeeded. Verified live: typing an existing
locale code and clicking Add now shows 'This language is already supported.'

Navigation-section was also audited (id generation, label locale-migration,
reorder swap, grouped-footer read-only fallback) - no defects found, it's
solid as-is.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 18:31:01 +04:00
sdarbinyan
ff53265fc0 fix(widgets): stop silently discarding invalid JSON edits in the props fallback textarea
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
updateJson() caught JSON.parse failures and did nothing, but the textarea
was bound to propsJson(committed props) - so on the next change-detection
pass, any in-progress invalid edit snapped back to the last-saved value
with zero feedback. Verified live via window.ng.getComponent(): typing
invalid JSON now keeps the user's draft on screen with an inline error;
fixing it commits and clears the draft/error.

Also: homepage-section drop() used CdkDragDrop<any[]> - switched to
unknown[] per the no-any rule, no behavior change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 18:14:58 +04:00
sdarbinyan
1c71f8e83e fix(features): keep featureFlags and userExperience enabled flags in sync for wishlist/compare
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
FeatureConfigService gates wishlist/compare visibility on BOTH
featureFlags.<key> and userExperience.<key>.enabled, but the features
editor only exposed one toggle wired to featureFlags. Both default to
true so this was silent, but a config with userExperience.wishlist.enabled
(or compare) explicitly false would show the editor toggle as checked
with no way to actually turn the feature back on from this screen.

toggleFeatureAndUserExperience() now updates both flags from the single
toggle, in one updateBootstrap call.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 17:55:08 +04:00
sdarbinyan
b05278c061 fix(footer): stop generating collision-prone social-link ids, fragile track key
createSocialLinkRow derived the new id from the current array length
(social-${length+1}). Add/remove/add cycles reliably reproduce a duplicate
id: add,add -> social-1/social-2; remove social-1 -> array length 1; add
-> social-2 again, colliding with the surviving row. footer.component.html
tracks footer nav items by id (@for ... track item.id), so a duplicate id
there corrupts Angular's DOM reuse on the public storefront footer.

Also switched the payment-icon @for from track icon.src to track $index -
two icon rows sharing a src (most commonly two blank ones) hit the same bug.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 17:52:43 +04:00
sdarbinyan
9e44215dd5 feat(project-editor): footer validation rules (contact email, social link URLs, payment icons)
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Real gaps, not fabricated: isValidEmail existed in primitives.ts but was
never called anywhere; social-link URL check only lived as a per-row
template hint (never blocked publish or set the nav badge); payment icons
with only src or only alt set were silently accepted.

- invalid-contact-email: company.contacts.email must be a valid email (error)
- invalid-social-link-url: footer.socialLinks entries need a valid http(s) URL (warning)
- incomplete-payment-icon: a payment icon needs both src and alt, or neither (warning)

Wired into footer-section via the existing fieldError() pattern. Header has
no equivalent gap today (every header field is a bool/enum, always valid by
construction) so nothing was added there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 16:57:43 +04:00
sdarbinyan
2a8c1166b1 feat(project-editor): syntax-highlighted code editor for HTML raw mode
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
- shared app-code-editor: overlay textarea + highlighted <pre> layer,
  no external dependency (Monaco/CodeMirror)
- tokenizeCss: selector/property/value/string/comment/at-rule aware,
  brace-depth state machine
- tokenizeHtml: tags + comments colored, delegates <style> block content
  to tokenizeCss (that's where static-page CSS is actually authored)
- marketplace-html-editor raw-code mode now uses app-code-editor instead
  of a plain textarea

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 16:28:53 +04:00
sdarbinyan
4543a6b6b6 feat(project-editor): per-field inline validation for languages, homepage, widgets, navigation, static-pages
Extends the fieldError() wiring pattern (already used in theme/general/branding)
to the remaining sections that have matching ProjectValidator fieldKeys:
- languages: localization.supportedLocales (no-languages, missing-translations)
- homepage: pages (empty-homepage, missing-widget, duplicate-routes)
- widgets: pages (invalid-widget-config)
- navigation: navigation.header (duplicate-nav-links)
- static-pages: staticPages (duplicate-slugs, invalid-css)

Header/footer/features sections have no matching validator issues today,
so nothing to wire there yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 16:18:22 +04:00
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
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
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
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
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
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
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
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
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
170243a480 feat(project-editor): finish field descriptions + enum dropdowns across all sections
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Wires up the remaining editor sections (theme, header, footer, homepage,
widgets, features, languages, navigation) with the description text and
dropdown UX started for General/Branding:

- Every enum-backed field is now a <select> with a description per option,
  not free text: theme.mode, the new site-wide layout.type (previously had
  no editor at all - added to the Theme section, and added to that
  section's reset-scope), homepage section layout.strategy, and
  catalog.navigationMode (also previously unedited, added to Features).
- Every other field (colors, header toggles, footer contact/company fields,
  widget props, feature flags, language add, nav link label/url/visible)
  gets a one-line plain-language description under its label via the new
  *Desc i18n keys (en/ru/hy) prepared earlier.
- Genuinely open text (widget layout variant strings, JSON props) stays
  free text, description-only, per the existing widgetLayoutDesc/widgetJsonDesc
  wording - not force-fit into a dropdown.

Verified: tsc --noEmit and ng build both clean.
2026-07-15 00:45:41 +04:00
sdarbinyan
ac83c4f57f feat(project-editor): add field descriptions to General and Branding sections (partial)
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Adds a short plain-language description under each field label (new
.field-desc style, section.shared.scss) so a non-developer admin
understands what each field affects, per i18n (en/ru/hy) convention.

Only General and Branding sections are wired up so far - the *Desc i18n
keys for the remaining sections (theme/header/footer/homepage/widgets/
features/languages/navigation) were prepared in translations.ts/en/ru/hy
but not yet wired into their templates. Follow-up work.
2026-07-14 16:05:09 +04:00
sdarbinyan
325dc17911 feat(admin): sprint 19 admin dashboard, routing, i18n
- Add admin dashboard feature (models/gateway/facade/components/page)
- Wire admin/products routes and backoffice coming-soon placeholders
- Add lastPublishedAt to ProjectEditorFacade/state
- Add dashboard i18n keys (en/ru/hy) and docs/ADMIN.md

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 12:21:33 +04:00
sdarbinyan
3877b70fdf feat(sprint18): editor autosave/reset, admin auth, QR reuse, Ed25519 prep
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
- Project editor: persist draft to localStorage, restore on reload,
  last-saved/draft-restored status indicators, section/whole-draft reset
  with confirmation.
- Extract shared QR/polling/expiry engine from TelegramLoginComponent
  (shared/qr-login) and reuse it for a new admin login flow.
- Admin authentication kept fully separate from customer session:
  own cookie/localStorage keys, signals, guard, and header interceptor
  (core/admin-auth).
- ?login=true / ?adminLogin=true open the respective login dialog for
  manual testing.
- Ed25519 challenge/verify interfaces (fail-closed no-op binding) ready
  for backend delivery.
- Document autosave/reset/admin-auth/QR-reuse/Ed25519 model and the
  remaining full-field-coverage gap in docs/Project-Editor.md.
2026-07-14 09:50:03 +04:00