isSlugTaken previously caught network/API errors and returned false,
letting the save proceed as if the slug were free. Now the error
propagates and blocks save via a distinct slugCheckError state,
surfaced in the category form with its own hint.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Removes all tracked repo documentation (root status docs, docs/,
docs/architecture/foundation/**, docs/archive/**, docs/context/BACKEND-AUDIT.md
+ adrs, src/assets/mock/README.md) and replaces it with:
- GAPS-AND-IMPROVEMENTS.md — role-based findings (user, PO, QA, backend,
accessibility, engineering) plus automated code-review passes over the
storefront and backoffice, each with file:line references. Findings only,
no fixes applied.
- BACKEND-API-REFERENCE.md — single consolidated backend contract: auth
(both mechanisms), bootstrap, pagination/sorting/filtering conventions,
error model, every live/mock-only endpoint with JSON examples, and the
admin-domain DI-token seam gaps.
Open items and unresolved decisions from the deleted docs (KNOWN-ISSUES,
PRODUCT_BACKLOG, SPRINT-PLAN-NEXT, Seller-Management audits, etc.) were
harvested into the two new files before deletion, not lost.
CLAUDE.md/AGENTS.md/GEMINI.md/.claude/ and docs/context/{INDEX,LOG,
MAINTENANCE,README}.md are untouched — confirmed gitignored, never part of
git history, outside this cleanup's scope.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Third attempt, done properly this time - first two were reverted
(one stopped cleanly on real conflicts, one botched sequencing and
deleted the old focus-trap before finishing the swap).
DialogComponent gains closeOnEscape/closeOnBackdropClick (default true,
backward-compatible with its 13 other call sites) and ariaLabel (for
dialogs with no visible title header). FOCUSABLE_SELECTOR now includes
iframe for the bank-payment panel's focus trap.
Cart wires closeOnBackdropClick=false on both dialogs (in-flight payment
shouldn't cancel on a stray click) and closeOnEscape tied to the bank
popup's open state, so Escape closes the nested bank iframe first and
falls back to the QR view - matches the original priority exactly.
Original geometry (500px QR modal/40px padding, 960x760 bank modal/
56-16-16 padding, both mobile breakpoints) preserved via :host ::ng-deep
overrides scoped per dialog instance - same pattern already used by
product-carousel-widget.component.ts.
cart.component.ts loses ~90 lines of hand-rolled ViewChild/HostListener/
focus-trap code - app-dialog owns all of it now.
Verified live in browser: dialog sizing/padding/aria-label correct at
mobile+desktop, backdrop-click confirmed inert, Escape-priority confirmed
(bank closes first, then QR), initial focus lands on close button.
83/83 tests pass, tsc/build clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@lucide/angular shipped its entire ~1500-icon set in the initial bundle
despite the app only using 85 named-imported icons - confirmed upstream
tree-shaking failure (sideEffects:false, clean named imports, single
non-splittable fesm file). Replaced icon-registry.ts/icon.component.ts
with hand-rolled inline SVG rendering of just the 85 used icons,
transcribed from lucide's own node data for pixel-identical output.
Zero call-site changes - AppIconName and app-icon's public API unchanged.
Also: karma-coverage wired (npm run test:coverage), baseline captured
in docs/SPRINT-PLAN-NEXT.md (32% statements / 18.5% branches).
Cart-modal -> app-dialog migration was attempted and reverted - real
conflicts (backdrop-close, nested-modal escape priority, iframe sizing),
documented in docs/FUTURE_FEATURES.md for a properly scoped follow-up.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
11MB of the ~14MB initial bundle is the full @lucide/angular icon set
despite clean named imports for ~85 icons - confirmed by build inspection,
previously undocumented (existing note only covered the two lazy chunks).
Root cause is upstream tree-shaking, not app code. Real fixes (package
upgrade or dropping the dependency) need sign-off before touching.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sprint G: audited every BootstrapConfig field for a real runtime consumer
(docs/DEAD-CONFIG-AUDIT.md). Wired 3 previously-dead editable fields:
footer.logoUrl, company.address.street/contacts.phone, catalog.suggestionsEnabled.
Remaining dead fields needing a business/design decision tracked in
PRODUCT_BACKLOG.md/KNOWN-ISSUES.md, not silently left.
Sprint H: 6 new spec files (test count 57 -> 83), covering ProjectEditorFacade
(undo/redo, draft persistence, publish gating), AdminAnalyticsFacade
(never-fabricate-a-number contract), and regression coverage for this
session's carousel/hero/profile-toggle fixes.
Sprint I: widget settingsSchema (declared in widget-manifest.json, never
validated) now enforced via a new lightweight schema check in
ProjectValidator, surfaced through the existing issuesByField pipeline.
Same check reused in diagnostics so editor and diagnostics can't disagree.
Verification: tsc clean, ng build clean, 83/83 tests pass, barry-cache
validate clean (2 pre-existing unrelated warnings only).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sprint E: homepage section editor now filters the layout-strategy picker
to each widget's widget-manifest.json supportedLayouts instead of always
showing all 5 strategies. columns field gated to widgets that read it
(hero, product-collection carousel).
Sprint F: closes client bug report (no items-per-page control, hero
carousel not manually/automatically scrollable, no 1-2 slide big-carousel
option). Product carousel item width now driven by layout.columns
(reused, was already editable but dead). Hero widget gains prev/next
arrows, touch swipe, and 1-2 panel mode via the same field.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sprint A: storefront header profile control (login/logout only, no menu),
wired to existing customer Telegram auth (AuthService).
Sprint B: backoffice/reports page, reuses AdminAnalyticsFacade (Sales,
Top Products, Marketplace Health cards + CSV export).
Sprint C: backoffice/settings page, admin UI density preference
(comfortable/compact), localStorage-persisted, applied to app-table
across all admin list pages.
Sprint D: admin bottom-nav Help -> mailto using existing supportEmail,
Documentation -> external link via new TenantConfig.documentationUrl.
AdminNavLink gains externalHref for non-routerLink nav entries.
Docs: docs/GLOBAL-SPRINT-PLAN.md tracks the full sprint breakdown.
docs/COMING-SOON-AUDIT.md removed, folded into docs/KNOWN-ISSUES.md.
docs/BACKEND.md updated with the new documentationUrl bootstrap field.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- ProjectEditorFacade.resetField(key): field-level revert-to-original, reusing getByPath + a new immutable setByPath (write-side counterpart, scalar/object dot-paths only).
- app-form-field gains showReset/resetLabel/(resetClicked) - a reusable per-field reset affordance, wired on branding logo/title and theme primary/background color.
- Save bar: unsaved-changes count is now clickable, expanding a field-level diff list (reuses facade.changeSummary(), already built for the Preview tab) with jump-to-section links.
- BuilderPropertySearchComponent: filters EditorSchemaService.all() by translated label/hint, jumps to the owning section - no new registry, reuses the existing schema.
- navigation-section: app-empty-state (existing component) added for empty header/footer link lists.
- Reset Section and Draft/Published badge were already implemented; not touched.
- New copy added as translation keys (en/ru/hy).
- PreviewHighlightService + appHighlightSource directive: shared hover/focus bridge between editor fields and the schematic live preview.
- BuilderLivePreviewComponent: in-page schematic homepage render (header/hero/blocks/footer) reading the same bootstrap the sections mutate, highlighting the area matching the active field.
- VisualLayoutPickerComponent: card-based layout picker (ControlValueAccessor, same shape as app-select) replacing the raw layout <select> in homepage-section.
- app-form-field gains optional usedBy/usedByLabel inputs for the "Where is this used?" helper text, wired into aria-describedby.
- Wired homepage/branding/theme sections with highlight sources + usedBy hints; live preview panel shown in project-editor-page for those three sections.
- All new copy added as translation keys (en/ru/hy).
Principal-architect-level review of the entire Seller Management body
of work (7 prior docs + all touched code), verified against fresh
tsc --noEmit and arch:check runs, not recalled from memory.
Real bug found and fixed (in scope per this mission's "unless
absolutely required" carve-out - a one-line correctness fix to
already-committed code, not new feature work):
AdminSellerManagementPageComponent.sellerManagementEnabled read the
bootstrap snapshot once via a plain signal() at construction, not
reactively via bootstrapRevision() the way UiRuntimeFacade/SeoService
both correctly do elsewhere in this codebase. Fixed to computed() keyed
on bootstrapRevision(). Currently invisible (flag is always false,
signal was never even read in the template) but would have gone stale
the moment bootstrap ever reloaded with the flag true. tsc clean after
the fix.
Findings documented in Seller-Management-Final-Design-Review.md (no
Critical/High severity found anywhere):
- Medium: SellerConfig (bootstrap wire shape) and Seller/SellerBranding
(domain entity) are two unreconciled type hierarchies for the same
concept - self-flagged already in BACKEND.md SS11.6, restated here as
an independently-confirmed finding rather than letting it drift.
- Medium: no reusable capability-guard abstraction exists anywhere in
the codebase, despite ADR-009/ADR-011 both prescribing "check the
flag in one place" - ADR-009's own described FeatureFlagService was
never built. Fine with one consumer, a real drift risk the moment a
second one needs the same check.
- Medium: the flag's true branch has never been exercised, even
manually - every verification claim in this whole body of work was
tested at the flag's real value (false).
- Low/nice-to-have: sellerId typed as bare string instead of the UUID
alias used everywhere else in the new sellers domain; MarketplaceRef
vs TenantConfig overlap (deliberate, documented, but worth watching);
documentation-to-code ratio (8 docs, zero backend bytes) carries a
consolidation-burden risk, especially the Unified/Split-Orders
question restated independently in 4 different docs.
- Explicitly checked for and did NOT find: circular dependencies,
scattered tenant/seller conditionals, over-engineering relative to
the typed-models-only mandate, or any auth/payment code touched.
Verdict: not an unqualified "ready for implementation" - two Medium
findings should be resolved by decision/small build before real
backend work starts, not because they block anything today but
because both compound in cost the longer they're left unresolved.
Everything actually built (typed foundation, disabled-by-default flag,
Phase 1 UI, plus the bug this review fixed) is solid and ready to
stay exactly as-is. No Critical or High-severity issue found anywhere.
Documentation only, no code. Synthesizes the 3 prior audits
(Seller-Management.md, the Backoffice readiness audit, the Storefront
audit) plus BACKEND.md SS11 into one migration plan covering all 17
requested modules: Authentication, Authorization, Bootstrap, Products,
Categories, Orders, Payments, Transactions, Reviews, Analytics, Media,
Search, CMS, Builder, Settings, Notifications, Emails, Audit Logs.
Per module: current behavior, future behavior, migration strategy,
backward compatibility, risk, effort, and an endpoint classification
(No change / Minor change / Major change / New endpoint) grounded in
facts already established in the prior audits - no new exploration,
no invented specifics.
Headline finding: Orders (the Unified-vs-Split-Orders decision) is the
single highest-risk, most consequential item in the whole plan -
payments/refunds/reporting all depend on it, and it can't be resolved
by an additive field the way every other domain's seller-scoping can.
Payments stays untouched (ADR-010, frozen) under the Unified path;
only Split Orders would ever touch the payment flow, and only then
with the same scrutiny the original frozen implementation got.
Cross-cutting sections included per mission: Database changes
(one nullable seller_id column touches existing tables, everything
else is new tables - no NOT NULL migration ever required), Permission
changes, Caching (bootstrap cache key must include resolved seller
identity), Indexes, Security (seller-to-seller isolation treated with
tenant-isolation rigor), Performance, API Versioning (ties to the
already-open BACKEND.md SS2.10 decision), Migration order (14 numbered
dependency steps), and 6 recommended implementation phases (A:
Foundation through F: Operational polish).
Every phase explicitly re-asserts the non-negotiable constraint:
modules.sellerManagement.enabled=false must show zero behavioral
difference before/after each phase ships.
Linked from docs/architecture/foundation/README.md alongside the
other Seller Management docs.
docs/BACKEND_API.md no longer exists as a live file (merged into
BACKEND.md in an earlier consolidation pass, per that doc's own intro
- only docs/archive/BACKEND_API.md remains, historical only). This
mission's "update BACKEND_API.md" instruction is fulfilled by
extending the doc that actually supersedes it: new §11 "Seller
Management (Optional Capability)", added to the top-of-file table of
contents, no existing section renumbered or altered.
Every subsection explicitly tagged Implemented / Planned / Future,
matching the same legend used in docs/architecture/foundation/
Seller-Management.md (the frontend-side capability doc this section
is the backend counterpart to):
- 11.2 Future entities: Marketplace (Implemented, existing
TenantConfig unchanged), Seller/SellerBranding (Planned - frontend
types exist, no backend schema), SellerUser/SellerSettings/
SellerInvitation (Future - no type, no concept, named for roadmap
completeness only).
- 11.3 Future endpoints: Seller CRUD/Activation/Invitations/Branding/
Analytics/Dashboard - all Future, none designed, each noted as
following the existing mock-to-API-gateway pattern (SS8) once built.
- 11.4 Authentication: SellerPermissionRole (4 roles) explicitly
flagged as a separate vocabulary from the existing, live AdminRole -
not merged, no guard wired, zero auth change.
- 11.5 Domain resolution: market.com -> Marketplace is Implemented
today (ADR-001, backend-Host-resolved); nike.market.com -> Marketplace
-> Seller is Future, no backend resolves it - and per the storefront
audit, needs no frontend routing change once it does.
- 11.6 Bootstrap additions: modules/modules.sellerManagement documented
as Implemented-as-contract (typed, always false/absent today);
sellerScope/sellerBranding as Planned with an explicitly flagged open
question (SellerConfig vs SellerBranding nesting not reconciled);
permissions noted as existing/unrelated today.
- 11.7 Checkout modes: Unified Order vs Split Orders - Future, not
designed, flagged as the single most consequential undecided item
for backend design given payments/refunds/reporting all depend on it.
- 11.8 Product ownership: sellerId? on Item/AdminProduct/AdminOrder -
Implemented as schema only (optional, absent = marketplace-owned,
verified backward-compatible via tsc staying clean). Existing
products remain valid with no migration required - NULL/absent
ownership documented as a permanent state, not transitional.
No backend implemented. No frontend code touched. Documentation only.
Audit only, no code changed - facts gathered by reading current
source (routes, containers, header/footer, SeoService), not assumed.
Covers Homepage, Categories, Products, Search, Favorites, Cart,
Checkout, Reviews, SEO, Breadcrumbs, Header, Footer.
Core finding: tenant resolution is already entirely backend-side by
request Host (ADR-001) - the frontend just consumes whatever bootstrap
comes back for whatever hostname it's running on. A seller subdomain
is architecturally closer to already working than any part of the
Backoffice audit found; the real gaps are all about whether the
*data* rendered carries a seller-aware value, not about routing/
hosting.
Key findings:
- Canonical URLs already correct today - SeoService.siteUrl derives
from location.origin dynamically, not hardcoded. Nothing to change.
- Header/Footer/SEO branding all read through one shared facade
(UiRuntimeFacade.reloadFromBootstrap()) - a single future injection
point that would cascade to all three for free, rather than three
separate fixes.
- SeoService.setItemMeta() (per-product OG/canonical tags) is defined
but never called anywhere in the codebase today - a pre-existing
dead hook, unrelated to seller-scoping but blocking any future
per-product/per-seller SEO work until wired.
- No dedicated breadcrumb component/service exists anywhere in the
storefront - the only breadcrumb logic in the app is one local
signal in catalog-container.component.ts.
- Checkout is not a separate route - it's an inline popup flow in
cart.component.ts, with no multi-vendor/multi-seller cart concept
at all. This is where Checkout Modes and Unified/Split Orders (both
marked Future in Seller-Management.md) would actually need to land.
- Structured data (JSON-LD) and sitemap generation don't exist for
anyone today, marketplace or seller - net-new work either way, not
seller-specific gaps.
- One pre-existing, unrelated issue noted in passing: og:locale is
hardcoded 'ru_RU' in SeoService - flagged, not fixed (out of scope).
Linked from docs/architecture/foundation/README.md alongside the
other Seller Management docs.
Audit only, no code changed - every fact gathered by reading current
facades/gateways/components on this branch, not assumed. Covers all
13 admin modules (Dashboard, Products, Categories, Orders, Customers,
Users, Analytics, Reviews/Moderation, Media, CMS, Builder, Settings,
Monitoring, Transactions).
Per module: answers the 3 readiness questions (does Marketplace Owner
see everything / would Seller see only their own / would Seller Staff
be limited), documents where a future scope would be injected (an
existing method/interface parameter to extend - no "if seller" checks
introduced anywhere), lists components that currently assume global
ownership, and classifies Ready / Needs scope / Needs permissions /
Needs API change.
Key findings:
- Only 3 of 13 gateways (Categories, Dashboard-metrics, Media) are
DI-token-swappable today; everything else needs that seam added
first, independent of seller scoping.
- Orders is the load-bearing blocker: Customers, Transactions, and
half of Analytics all derive from its same unscoped full-fetch order
list, and AdminOrderItem has no per-item seller attribution at all -
the concrete gap behind Seller-Management.md's open Unified-vs-Split-
Orders question.
- Users already carries an AdminUserScope/AdminRole concept (label-
only today) - the natural future home for the Marketplace Owner/
Seller/Seller Staff/Platform Admin role vocabulary.
- CMS/Static Pages and Builder/Project Editor are structurally not
about data scoping at all (marketplace-wide content, single global
config document respectively) - seller-level work there is new
product surface, not an extension.
- No admin module anywhere does role-based hiding of buttons or data
today - confirmed, not assumed.
Linked from docs/architecture/foundation/README.md alongside the
other Seller Management docs.
Master entry-point doc (Seller-Management.md) consolidating everything
built across the prior 4 commits (ADR-011, domain models, Phase 1 UI,
UX review) plus the full roadmap, with every section explicitly
tagged Implemented / Planned / Future so nothing reads as built that
isn't.
Covers: Overview, Architecture & Hierarchy, Marketplace, Seller,
Roles & Permissions, Feature Flags, Bootstrap, Future API, Seller
Storefronts, Seller Branding, Seller Ownership, Checkout Modes,
Unified/Split Orders, Migration & Compatibility (why existing
marketplaces stay unchanged, with the concrete verification evidence
for each claim), Developer Notes, Builder Notes, Backend Notes.
Explicitly marked Future (not designed, no shape decided) rather than
documented as if real: the API surface, seller storefronts, checkout
modes, and the unified-vs-split-order decision - none of these have
any code or ADR behind them yet, unlike the typed models/feature flag/
Phase 1 UI which are genuinely Implemented.
Added a rollout-stage diagram (types+flag -> Phase 1 UI -> backend
decisions -> CRUD -> branding/storefronts -> checkout modes) showing
work stops after "Phase 1 UI" today. Linked as the entry point from
docs/architecture/foundation/README.md and docs/PROJECT_INDEX.md,
ahead of ADR-011/diagrams/domain-models/UX-review which stay as
detail references.
No code changed.
Reviewed the Phase 1 UI against every other Backoffice page. Found and
fixed 2 real issues; everything else verified already consistent
(built entirely from shared components, so hover/focus/dialog-a11y/
dark-readiness/contrast come from those components, not reinvented).
Fixed:
- Message textarea had no id/aria-describedby wiring (app-input
self-wires this via injected FormFieldContext; the raw textarea -
no dedicated textarea component exists yet - never got it, so the
visible label's `for` pointed nowhere). Added explicit aria-label
bound to the same translation key as the visible label.
- Learn More dialog's feature list would render native browser
bullets (no global list-style reset exists outside details>summary
in styles.scss). Replaced with checkCircle icon + text rows,
consistent with how the rest of the app pairs icons with list/status
meaning.
Added docs/architecture/foundation/Seller-Management-UX-Review.md
documenting both fixes plus everything checked and confirmed already
consistent (empty-state usage, icon reuse, translations completeness
across en/ru/hy, responsive at 1280px/375px, dialog a11y verified via
accessibility tree not assumed).
tsc --noEmit clean, arch:check (boundaries + cycles) clean. Live-
verified: Learn More dialog shows all 6 items each with an icon
(confirmed via DOM query), textarea aria-label confirmed
"Сообщение", no console errors.
Typed models only, per mission. Nothing outside the new files reads
or writes any of this yet.
New core/sellers/models/ (mirrors core/products/models,
core/auth/models convention):
- MarketplaceRef - minimal {id,slug,name} reference from a seller
back to its marketplace, distinct from bootstrap's TenantConfig.
- SellerStatus - 'pending'|'active'|'suspended'|'disabled', no
transition logic.
- SellerScope - {sellerId, marketplaceId}, domain-level counterpart
to BootstrapConfig.seller (SellerConfig from the ADR-011 pass).
- SellerBranding (+SellerContact/SellerAddress/SellerThemeOverrides)
- logo/banner/description/contacts/address/theme overrides, every
field optional. Marketplace branding/theme remain default;
nothing consumes this yet.
- SellerPermissionRole/SellerPermissions - marketplaceOwner/seller/
sellerStaff/platformAdmin. Separate vocabulary from the existing
AdminRole (core/auth/models/permission.model.ts) - not merged, not
wired into any guard, zero auth behavior change.
- Seller - the eventual entity, composed from the above.
Changed (optional-only, verified backward compatible):
- Item (models/item.model.ts) gained sellerId?: string
- AdminProduct (features/admin/products/models/) gained
sellerId?: string
- AdminOrder (features/admin/orders/models/) gained sellerId?: string
Absent means marketplace-owned in every case, exactly like every
existing product/order today. No consumer of any of these three
models needed updating. AdminOrderItem (per-line-item ownership) and
the existing PermissionsConfig/AdminRole system were deliberately not
touched - out of scope for this pass.
Added docs/architecture/foundation/Seller-Management-Domain-Models.md
documenting every new type, every changed field, and the explicit
non-goals list. Linked from the foundation README alongside ADR-011
and the diagrams doc.
tsc --noEmit clean, arch:check (boundaries + cycles) clean.
No backend, no CRUD, no API, no business logic - production-quality
UI only, built entirely from existing shared components (app-dialog,
app-empty-state, app-button, app-form-field, app-input, app-icon,
app-badge). Gated per ADR-011: reads
modules.sellerManagement.enabled from bootstrap (always false today,
no backend sets it) rather than hardcoding disabled state.
New:
- AdminSellerManagementPageComponent (features/admin/seller-management/
pages/) - renders the specified empty state (title/description/
Request Access + Learn More buttons) using existing shared/ui
primitives only, no new UI infrastructure.
- Request Access dialog: Company/Email/Message form via
app-form-field + app-input + a plain textarea (no dedicated
textarea component exists yet, styled to match app-input's own
tokens exactly). Submission is mocked (setTimeout), no API call.
On submit: closes and opens a success dialog ("Thank you...").
- Learn More dialog: 6 capability bullets (seller dashboards,
storefronts, permissions, analytics, product ownership, marketplace
administration) under a "Coming Soon" badge.
- New admin nav group "Partners" > "Seller Management" link
(admin-nav.model.ts), new route /backoffice/partners/seller-management
(app.routes.ts), using the same loadComponent/breadcrumb pattern as
every other admin route.
Translations: full en/ru/hy coverage, zero hardcoded strings - new
adminShell.nav.{partnersGroup,sellerManagement},
adminShell.pages.sellerManagement, and a new adminSellerManagement.*
namespace (emptyState/requestDialog/requestSuccessDialog/
learnMoreDialog) added to translations.ts (types) and all three
locale files.
Accessibility: inherited from app-dialog (role="dialog",
aria-modal, focus trap on Tab/Shift+Tab, Escape to close, focus
restored to trigger on close) - no new a11y code needed, reused as-is.
Responsive: existing --space-*/--font-size-* tokens throughout,
flex-wrap on button row, mobile breakpoint stacks actions full-width.
Verified live (ru locale, devBypassAdmin): nav group/link render
correctly, breadcrumb shows "Управление продавцами", empty state
copy matches spec exactly, Request Access dialog opens with all 3
fields + Cancel/Send Request, filled + submitted -> success dialog
with exact spec copy, Learn More dialog shows all 6 bullets + Coming
Soon badge, no console errors, verified again at 375px mobile
viewport. tsc --noEmit clean, ng build clean (pre-existing bundle-
budget warning only), arch:check (boundaries + cycles) clean.
Pointer-only updates, no rewrite: added ADR-011 to both docs' existing
ADR lists/counts, plus a one-line Seller Management entry in
PROJECT_INDEX.md's capability summary noting it's typed-foundation-
only, disabled by default, not implemented.
Documents the decision behind the typed contracts added in the
previous commit: Seller Management is an optional platform capability
module (Platform -> Marketplace -> Seller, 0..N per marketplace), not
a second tenancy tier. Backend resolves seller scope the same way it
already resolves tenant (ADR-001); frontend never resolves it itself.
Gated by one typed flag (modules.sellerManagement.enabled), same
capability-guard discipline as ADR-009, defaulting to disabled/absent
so existing marketplaces are byte-identical.
Explicitly scopes out UI, backend, and business logic as future work
requiring its own ADR/implementation pass once the module is actually
built out.
Added companion diagrams (Seller-Management-Diagrams.md): hierarchy,
bootstrap module-gate flow, and the type-contract class diagram.
Registered ADR-011 in the foundation README's ADR index.
Architectural foundation only - no UI, no backend, no business logic.
Per ADR-001 (Platform -> Marketplace -> Seller hierarchy) and ADR-009
(feature flags / capability guards): Seller is an optional child scope
beneath a marketplace, not another tenant.
New:
- PlatformModulesConfig / SellerManagementModuleConfig
(shared/models/config/platform-modules.model.ts) - the
modules.sellerManagement.enabled contract, defaults to disabled
(DEFAULT_PLATFORM_MODULES_CONFIG).
- SellerConfig (shared/models/config/seller.model.ts) - typed shape for
the resolved seller scope, mirroring TenantConfig's fields at the
subset a seller needs. Frontend never resolves this itself; it only
reads what the backend already decided (same convention as tenant
resolution, ADR-001).
Changed:
- BootstrapConfig gained two optional fields: modules?, seller?. Both
absent by default - every existing marketplace's bootstrap response
is untouched, TypeScript-checked backward compatible (all new fields
optional, no existing field types changed).
tsc --noEmit clean. No component, facade, service, or route touched -
this commit is pure type contracts.
Ran via ng update @angular/cdk@22 - no code migrations required.
Verified after full upgrade (core/cli/animations/common/compiler/
forms/platform-browser/router/service-worker/cdk all now 22.0.8/
22.0.6): tsc --noEmit clean, ng build --configuration=production
clean (same pre-existing bundle-budget warning, size unchanged),
arch:check:boundaries and arch:check:cycles both pass. Live-verified
in browser: storefront home renders correctly (categories/products/
i18n all working), /backoffice/dashboard (admin shell, lazy-loaded
per the earlier routing fix) renders correctly, no console errors on
either.
Required a Node.js upgrade on the dev machine first (Angular 22 CLI
needs Node >=22.22.3 or >=24.15; machine had v22.16.0) - done by the
user before this update ran.
Ran via ng update @angular/core@22 @angular/cli@22 (schematics applied
automatically). TypeScript bumped 5.9.3 -> 6.0.3 as a required peer.
Migrations applied:
- provideHttpClient() calls gained withXhr() where HttpXhrBackend is used
(app.config.ts)
- optional-chaining expressions wrapped in $safeNavigationMigration()
(language-selector.component.html)
- nullishCoalescingNotNullable/optionalChainNotNullable extended
diagnostics disabled in tsconfig.app.json/tsconfig.spec.json (matches
the new stricter default the migration works around)
Next: ng update @angular/cdk@22, then verify tsc/build/tests.
Goal: this document alone, pasted into a fresh Claude session, should
be enough for a backend dev to implement against without getting
stuck or inventing conventions the frontend doesn't actually need.
Fixed a real bug: ~65 cross-references throughout the document pointed
to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md
- three sibling docs that were deleted and fully merged into this
document's own §4/§6/§10 during the earlier doc-consolidation pass, but
the in-text references were never updated. A fresh agent following
those links would hit dead ends repeatedly. Bulk-replaced with in-
document section references; hand-fixed ~4 sentences that framed §6/
§10 as "sibling task, in progress" (stale - both are complete, this
document's §6/§10 already are the settled contract, nothing to wait on).
Added "Recommended default" to every item across the document's 5
consolidated "Requires backend decision" registers (§2.12 framework,
§3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items,
§6 error-model summary - 9 items, §10 maintenance-mode list - 5 items).
Each default is derived from what the frontend already implies or
standard REST/security convention - no invented APIs or business
rules. ~6 items are explicitly flagged as real business/security
decisions instead (order state-machine rules, Ed25519 cutover
strategy, refresh-token reuse-detection posture) since those carry
consequences no amount of frontend-code-reading can resolve.
Added a "How to use this document" preamble up front: work in §9's
dependency order not document order, apply recommended defaults and
keep moving, only stop for the explicitly-flagged business/security
items, don't invent beyond what's written or directly implied.
Frontend side: confirmed nothing else is left. docs/TODO.md already
has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable
Ed25519 error-code UI) is correctly left open and documented rather
than faked closed - fixing it needs a real backend emitting real
distinguishable error codes, which doesn't exist yet and can't be
fabricated without inventing an API contract.
No frontend code touched. No architecture changed. No APIs invented.
Reviewed BACKEND.md top to bottom (4775 lines, 10 sections) against
the full backend-handoff checklist (auth, bootstrap, every endpoint,
media, all domains, pagination/filter/sort/search, error contract,
maintenance mode, status codes, versioning, rate limits, CORS,
security, websocket/events, mock-to-api migration).
Confirmed already covered, no action: Authentication (§4, all
sub-items), Bootstrap (§1, full), every domain's CRUD contract (§3.1-
3.20, includes Moderation under 3.17.b), Media (§7), SEO (bootstrap
SeoConfig + per-page seo + sitemap tracked as remaining work), Error
Model (§6), Maintenance Mode (§10), Migration guide (§8).
Added (genuine gaps, not covered anywhere in the doc):
- §2.10 API path versioning - no endpoint has a version segment/header
anywhere; only BootstrapConfig.schemaVersion exists and that only
versions the bootstrap payload shape, not the API surface. Flagged
as a backend/infra decision with zero frontend impact either way.
- §2.11 Real-time/WebSocket - confirmed no WebSocket/SSE exists
anywhere in the frontend; consolidated the 5 places that look "live"
(QR/Telegram login, payment status, session validity, maintenance
notice, admin monitoring) into one table, all client-side polling.
Flagged push-vs-poll as a backend decision, most relevant to payment
latency and the session-revocation propagation delay.
- Renumbered the section's "Consolidated requires-backend-decision"
list 2.9 -> 2.12 (moved after the two new subsections, no other
content changed) and added both new items to it. No other §2.x
cross-references existed elsewhere in the doc to update.
No duplication found requiring merge; docs/archive/BACKEND_API.md
cross-references are intentional (superseded-but-kept historical
detail, per the doc's own stated design), not obsolete/duplicate
content.
Phase 3 (Performance). Bundle-stats analysis (esbuild metafile) found
AdminLayoutComponent statically imported and used as component: in
app.routes.ts, the only route in the file not using loadComponent -
pulled the whole backoffice shell into the initial bundle even for
storefront-only visitors, even though every child route under it was
already lazy.
Fixed: component: AdminLayoutComponent -> loadComponent(). Verified
live at /backoffice/dashboard - admin-layout-component now its own
21.86kB lazy chunk, no console errors, dashboard renders correctly.
Initial bundle over-budget shrank from 438.68kB to 417.53kB.
Investigated and deliberately left as-is (not bugs, documented/
legitimate):
- src/app/i18n/ru.ts (272kB) eagerly bundled - explicit, commented
tradeoff in translate.service.ts (ru is platform default language,
avoids extra round-trip for majority of users; en/hy already
code-split). Changing this trades bundle size for default-language
UX regression - a product call, not a cleanup item.
- @lucide/angular (182kB) - verified tree-shaking works correctly
(1750 icons in the package, ~85 actually imported by name in
icon-registry.ts, sideEffects:false). Cost is genuine icon usage,
not dead weight. A further win exists (splitting the icon registry
into storefront-critical vs admin-only sets so backoffice-only
icons don't ride the eager header/footer import chain) but touches
every icon consumer across the app - flagging as a scoped follow-up
rather than attempting blind in this pass.
Remaining bundle-budget warning after this fix: 1.12MB vs 700kB
budget. Given ~350kB is unavoidable Angular framework/router/zone.js
floor, +272kB deliberate ru.ts, +182kB legitimate icon usage, the
700kB budget itself looks stale/unrealistic for this app's actual
floor - flagging for a business decision on raising it rather than
chasing further cuts.
tsc --noEmit clean, ng build clean (warning only, no errors), live
browser-verified.
Ran knip to find unused exports/dependencies (deps already clean, no
unused packages/files found).
Removed genuinely dead code (verified zero references anywhere,
including templates):
- 4 unused constants in config/constants.ts (scroll/pagination/search
thresholds never consumed)
- isAdminRole(), toSearchResult(), createInitialSearchState(),
getTranslatedCategoryName() - unused utility functions
- DEFAULT_EDITOR_HEADER_CONFIG - unused constant
- TelegramService - entire file deleted; cart.component.ts/
cart.service.ts already implement the same window.Telegram.WebApp
access directly, this was an unused duplicate
Real bug fix found during the sweep: SeoService has providedIn:'root'
with a live effect() meant to sync <title>/OG/canonical tags to
tenant bootstrap config, but nothing in the app ever injected it, so
Angular never instantiated it and the effect never ran - the SEO sync
a prior sprint reported as "done and verified" was actually dead on
arrival. Fixed by injecting SeoService in the root App component.
Left alone: ~125 knip-flagged "unused exported types" - overwhelmingly
config/schema interfaces for the widget/theme/admin domain models,
high false-positive rate for this kind of interface-heavy Angular app,
deleting blind risks breaking structural type contracts. Also left
locally-used-but-over-exported helpers (toCssColor/toBackendColor,
HTML_EDITOR_TOOLBAR*, HISTORY_LIMIT, DEFAULT_CATALOG_PAGE_SIZE) - real
code, not dead, just exported wider than needed.
tsc --noEmit and ng build --configuration=production both clean (only
pre-existing bundle-budget warning, unrelated).
Files changed: src/app/app.ts, src/app/config/constants.ts,
src/app/core/auth/models/permission.model.ts,
src/app/core/products/models/catalog-experience.model.ts,
src/app/core/search/models/search-state.model.ts,
src/app/features/project-editor/models/project-editor.model.ts,
src/app/services/index.ts, src/app/utils/item.utils.ts,
src/app/services/telegram.service.ts (deleted)
Audited every *.md in docs/ and root. Merged five overlapping backend
docs (BACKEND_INTEGRATION.md + AUTHENTICATION.md + ERROR_CONTRACT.md +
MAINTENANCE_MODE.md + the already-archived BACKEND_API.md/
BACKEND_API_REMAINING_WORK.md) into one canonical docs/BACKEND.md
(4775 lines, 10 numbered sections) - deleted the four standalone
files outright now that their content is fully inlined.
Archived (not deleted - real historical value): ADMIN.md (Sprint
19-28 build log, sprint-report-shaped, not a living reference) and
FRONTEND-ROADMAP.md (despite its name, a shipped-history changelog
with detail no other doc has - not a forward roadmap, so keeping it
in root alongside NEXT_PHASE.md was exactly the "10 roadmaps"
confusion being cleaned up).
Deleted outright (zero value): SPRINTS.md - a leftover copy-pasted
sprint-kickoff prompt saved as a file, not documentation.
Rewrote docs/PROJECT_STATUS.md with completion-percentage estimates
per area (frontend/backend/UI/admin/storefront) and an explicit
first-customer-readiness call. Rewrote docs/NEXT_PHASE.md to the
strict 5-phase structure (backend integration -> production testing
-> performance -> monitoring -> v2 ideas), pointing to PRODUCT_BACKLOG
.md/FUTURE_FEATURES.md for phase 5 detail instead of duplicating it.
Rewrote root README.md - was stale (referenced deleted pages/info,
pages/legal folders from a prior RC pass), now covers architecture,
frontend/backend status, how to run, mock<->API switch mechanism
(useMockData in environment.ts), current folder structure, and a
documentation map.
Updated docs/PROJECT_INDEX.md (the stated entry point) to link only
the surviving doc set - every remaining document is reachable from it.
Fixed every broken/stale cross-reference to the deleted/renamed
backend docs across ARCHITECTURE.md, EDITOR.md, FRONTEND.md,
PROJECT-STRUCTURE.md, StaticPages.md, KNOWN-ISSUES.md (10 individual
link fixes, verified by repo-wide grep before and after). Left
CHANGELOG.md's two historical entries untouched - changelogs are
append-only history, not live navigation, editing past entries would
misrepresent what was true at the time.
Not touched (explicitly out of scope): docs/architecture/foundation/**
(enforced ADRs/governance, permanent not sprint-shaped),
docs/context/** (Barry Cache infrastructure, "do not edit by hand"
per CLAUDE.md), .claude/worktrees/** (separate git worktrees
containing an unrelated project's docs, not this repo's documentation).
docs/ root: 22 files -> 16. Plus 5 in docs/archive/ (was 3).
Classified every TODO.md item into one of DONE/BACKEND/PRODUCT
DECISION/FUTURE VERSION/BUG, verified against source, not against
prior docs:
- BACKEND items (bootstrap content, builder draft/publish, 6 admin
CRUD domains, media pipeline) confirmed already covered by
BACKEND_INTEGRATION.md; appended a mapping appendix rather than
duplicating raw bullets. Fixed 22 stale internal BACKEND_API.md
cross-references left over from before that file was archived.
- PRODUCT DECISION items (dark mode, brand-color WCAG contrast,
stars.component token gap, footer Contacts content, advanced
analytics, payment providers) moved to new docs/PRODUCT_BACKLOG.md.
- FUTURE VERSION items (Angular 22, bundle splitting, cart-modal
composition cleanup, hero-spacing investigation) moved to new
docs/FUTURE_FEATURES.md.
- BUG: rewrote docs/KNOWN-ISSUES.md down to the one real, verified,
currently-reproducible frontend bug (Ed25519 admin-auth error codes
session-expired/invalid-signature are unreachable -
toAuthErrorShape() never reads a body error code, only maps HTTP
status, and no status ever produces those two codes - confirmed by
reading auth.service.ts + auth-error.model.ts). Condensed the
"Fixed" history instead of carrying full verbose repro text forward.
- DONE items removed outright (dead-code deletion, dashboard false
positive, RC-02 fixes, stale "dynamic-renderer unwired"/"178 missing
keys" claims already disproven by source).
docs/TODO.md rewritten to the exact "no blockers" template - nothing
left qualifies as a release blocker.
New docs/PROJECT_STATUS.md: honest per-area status (frontend/backend/
docs/auth/builder/storefront/admin), known limitations, and explicit
production/backend/demo readiness calls - including correcting an
initial draft's unpushed-commit count (53, not 10, per git log
origin/B2B..HEAD).
New docs/NEXT_PHASE.md: work that can only start once a real backend
exists (gateway swap-in, mock removal, dormant-auth activation, role
enforcement, integration/E2E tests, perf profiling, monitoring,
maintenance-mode UI).
docs/PROJECT_INDEX.md (the stated entry point) updated to link the new
doc set and stop pointing at the now-archived BACKEND_API.md/AUTH.md.
docs/FRONTEND-ROADMAP.md's "Known open items" replaced with pointers
to the new category-split docs instead of a duplicated mixed list.
Not swept: a handful of low-traffic docs (architecture ADRs,
FRONTEND.md, EDITOR.md, ARCHITECTURE.md, PROJECT-STRUCTURE.md,
StaticPages.md, ADMIN.md) still reference the old BACKEND_API.md/
AUTH.md filenames - noted as a known gap in PROJECT_STATUS.md rather
than touched blindly, since they're historical-context docs, not the
navigation entry point.
4349 lines, 9 numbered sections per the Backend Finalization Sprint
spec: Bootstrap, Endpoint Framework, CRUD Contracts (~102 endpoints
across 20 domains), Authentication (spliced from AUTHENTICATION.md),
Security, Error Model (spliced from ERROR_CONTRACT.md), Uploads, Real
Backend Implementation Guide, Backend Checklist (34 items).
Everything traced to docs/context/BACKEND-AUDIT.md and actual current
source - proposed (unverified) paths explicitly marked as such,
everything the frontend has no opinion on marked "Requires backend
decision" rather than invented.
Archived the three docs this supersedes (BACKEND_API.md, AUTH.md,
BACKEND_API_REMAINING_WORK.md) to docs/archive/ with pointers back to
this file. AUTHENTICATION.md, ERROR_CONTRACT.md, MAINTENANCE_MODE.md
kept in place as standalone companion references (their content is
also inlined/cross-referenced here). ADMIN.md left untouched - it's a
frontend admin-UI sprint doc, not a backend spec, no overlap.
Verified via repo-wide search: no other backend/API spec docs remain
outside archive/ and this canonical file.
Unified API error envelope + full HTTP status catalogue (401/403/404/
409/422/429/500/503, maintenance, validation, tenant-disabled,
rate-limit, expired-token, invalid-signature) with JSON examples and
current frontend reaction behavior, including two flagged pre-existing
frontend bugs (expired-token/invalid-signature body-code handling is
currently dead code - toAuthErrorShape() ignores fallbackCode for real
HTTP errors).
Maintenance-mode contract (global/per-tenant/per-module/read-only/
scheduled/feature-disable) with proposed 503 response shapes and an
explicit split between "requires backend decision" and "no frontend UI
exists yet, requires a future frontend task."
These two agents wrote their files before hitting a session usage
limit that killed the process before final report-back; content
verified complete on disk before committing.
bootstrap.json's footer.paymentIcons referenced /assets/images/
mir-logo.svg, visa-logo.svg, mastercard-logo.svg - none of that
directory's files existed until the RC-02 placeholder fix, and these
three were still missing. Site-wide broken-image icons in every page
footer. Added neutral labeled-badge SVGs (not reproductions of the
actual trademarked logo artwork) at the exact referenced paths, plus
an onerror fallback on the footer <img> for defense in depth.
Exhaustive inventory of every HTTP call, gateway (interface + mock +
real impl), facade, and model/DTO the frontend defines or expects,
grouped by domain. Primary input for the remaining Backend
Finalization Sprint docs.
Key findings:
- Only AdminCategoriesGateway and AdminDashboardMetricsGateway are
DI-token-bound; every other admin domain (orders, products, users,
transactions, monitoring, moderation) injects its *LocalGateway
class directly - a real backend swap needs a token added first, not
just a rebind.
- Only one real admin API impl exists (AdminCategoriesApiGateway);
everything else admin is in-memory/localStorage mock.
- Content-management/project-editor have no save/publish HTTP call at
all - builder writes are in-memory + localStorage draft only.
- No literal /admin|/builder|/backoffice CRUD paths exist in source;
concrete admin paths are proposals, not verified literals.
Analytics, Reports, and Diagnostics were already clean (no raw
HTTP/queue-worker strings found on audit). Monitoring had three spots
speaking developer language by default:
- Background queue names ("order-notifications") -> friendly labels
("Order notifications").
- Webhook event keys ("order.created") -> friendly labels ("New order
placed").
- Activity log's "api" category showed the raw HTTP line
("GET /api/products responded 200 in 84ms") as the primary message.
Now shows a plain-language summary by default ("Product data
refreshed successfully"), with the raw string moved to a collapsed
"Technical details" <details> per event (api/error/warning rows).
getMainImage() referenced /assets/images/placeholder.svg as the no-image
fallback, but src/assets/images/ never existed - any item with zero
photos rendered a browser broken-image icon instead of a placeholder.
Added the asset.
Also added an (error) handler (onImageError) on every dynamic <img> that
renders a user/admin-supplied URL (product card, cart line item, cart
payment QR code, product gallery main + thumbnails) so a 404'd/broken
image URL swaps to the shared placeholder instead of shipping broken.
pages/category, pages/search, pages/item-detail, pages/info/**,
pages/legal/** (40+ files) were entirely unrouted dead code:
- category/:id, category/:id/items, search all redirect/route to
CatalogContainerComponent
- product/:id routes to ProductDetailsContainerComponent, not item-detail
- cmsContentRoutes (meant to route info/legal) is a literal empty array;
static/legal content is served by the CMS-driven :staticPath ->
StaticPageComponent route instead
dynamic-renderer/ is unrelated and stays - confirmed active, it's the
live homepage rendering pipeline (HomeComponent -> WebsiteRuntimeFacade
-> PageRendererService/PageResolverService -> DynamicPageLayoutComponent).
Updated docs/TODO.md, docs/KNOWN-ISSUES.md, docs/FRONTEND-ROADMAP.md,
docs/PROJECT_INDEX.md to reflect the resolution.
- Route aria-label/alt/title strings (rating, discount, carousel arrows,
hero slides, dialog close, toast dismiss, QR code, bank payment iframe,
guest checkout fallback) through the translate pipe/service instead of
literal English.
- Drop the "Oops!"/"Упс!" apology framing from category/subcategory empty
states (en/ru/hy) - zero results is not an error.
Builder static-page editor marked done (Phase 6). Added 4 new items
found during RC-01 Phase 12 final walkthrough, not fixed this pass:
hardcoded 'Featured Products' heading, dashboard false-Problem status
on empty stores, Monitoring's raw developer text, category image 404s.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Phase 8 (RC-01): these 4 list/dashboard pages had no error-state handling
on their primary data-load subscriptions — on a gateway error, `loading`
was either never reset (Users, Monitoring, Analytics: genuine infinite-
spinner risk, nested subscribe chain in Analytics never resolved on
failure) or there was no loading/empty/error handling at all (Reports
queue: raw table with zero skeleton or fallback).
- admin-users.facade.ts, admin-monitoring.facade.ts: add `error` signal,
error callback on the primary load subscribe so `loading` always
resolves.
- admin-analytics.facade.ts: add `error` signal; every level of the
4-deep nested gateway subscribe chain (orders -> products ->
categories -> reviews) now has an error handler that resolves loading
instead of leaving it stuck true.
- admin-moderation.facade.ts: add `reportsLoading`/`reportsError` signals
(reports list had none previously).
- Templates: reuse existing `app-skeleton`/`app-empty-state`/`app-button`
primitives for the new error branch, `common.retry` label, two new
generic `common.errorTitle`/`common.errorDescription` i18n keys added
to en/ru/hy (reused across all 4 fixes instead of one-off per-page
copy).
Verified: tsc --noEmit clean, `npm run build` green (pre-existing bundle-
budget warning only, unrelated). Live-checked Home (375px) and Backoffice
Products (1024px) — no console errors, tables/cards render without
overflow.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Replaced hardcoded English audit/timeline text (order status changes,
review moderation events, user role/status changes) with proper
adminXxx.timelineEvent.*/adminUsers.audit.* i18n keys, so Recent
Activity/Timeline/Audit panels no longer mix English into ru/hy UI.
- Translated raw internal codes rendered directly to users: transaction
payment method ('card'/'qr'/'cash_on_delivery' -> adminTransactions.methodValue.*)
and user roles/permissions ('products.manage' etc -> adminUsers.roleValue.*/
adminUsers.permission.*), replacing developer-facing enum leakage with
real copy.
- Fixed wrong-noun list-footer counts: Orders/Transactions/Moderation
list pages all reused adminProducts.items ("N товаров"/"N products")
regardless of what was actually listed; each now has its own itemsCount
key ("N заказов", "N транзакций", "N отзывов").
- Fixed customer detail page's "Back" button reusing adminOrders.back
("Back to orders") instead of a customers-specific label.
- Added translation keys to en/ru/hy + translations.ts interface for all
of the above.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- KNOWN-ISSUES item 11: the WYSIWYG page content editor was buried inside
a collapsed Advanced <details>, labeled 'Raw HTML (advanced)'. Moved it
to the top of the Content tab, unwrapped, relabeled 'Page Content' /
'Содержимое страницы' / 'Эջի բովանդակություն' with a plain-language
description. Advanced tab keeps genuinely technical fields (id, slug,
route, custom template).
- Publish (save-bar) and Remove language (languages-section) had no
confirmation despite being destructive/high-impact — added
window.confirm guards using the existing builder.confirm* i18n pattern
(matches resetDraft/resetSection/dirty-guard precedent), all 3 locales.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- product-carousel-widget: implicit CSS grid track had no min-width:0,
so the flex scroller's intrinsic content width (fixed 220px product
cards) overflowed the grid item and pushed body width to ~2187px on
a 1440px viewport, squeezing the entire homepage into a ~340px column.
Added min-width:0 to the track and scroller (standard grid/flex
overflow fix).
- bootstrap.json mock fixture: about-us/privacy-policy/terms-of-service
static pages (the real CMS pages served via bootstrap.staticPages,
per docs/PROJECT_INDEX.md) used a 'content' field, but
content-page.service.ts's normalizePage() only reads 'html' -
ContentPageBootstrapInput has no 'content' field. Title rendered,
body was always empty. Renamed the 3 fixture entries' field from
content to html to match the schema; content now renders.
- compare-table: colour row rendered raw hex/name values as plain text
(e.g. '#fCfCfC') with no swatch, inconsistent with variant-selector's
established colour-swatch pattern used on the product page. Added a
small circular swatch (reusing the same border-radius:50% pattern)
next to the value.
Verified live via ng serve: overflow gone (body/viewport width match
at 1440/1280/375), static pages render real content, compare swatch
displays. tsc --noEmit and ng build both clean (pre-existing bundle-
budget warning only, already tracked in KNOWN-ISSUES item 12).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
/search routes to CatalogContainerComponent + CatalogSearchResultsComponent
(app.routes.ts:45-47) - confirmed the live search surface (pages/search/* is
unrouted dead code per KNOWN-ISSUES.md item 13, not touched here).
- search-results.component: hand-rolled `.skeleton-card` shimmer (hardcoded
hex gradient colors, duplicate keyframes) replaced with the shared
app-skeleton primitive the dead pages/search copy already used, but the
live component never got. Bare `<div class="empty-state"><h3>/<p></div>`
replaced with app-empty-state + app-icon, matching CatalogEmptyStateComponent's
established pattern elsewhere in the same feature.
- Added distinct empty-state messaging: a too-short query (<3 chars, mirrors
the existing minSearchLength/isQueryTooShort convention from the dead
pages/search/search.component.ts) now shows "Enter at least N characters"
instead of being indistinguishable from a genuine no-results-for-X state,
which now shows the query and a retry hint (search.noResults/noResultsFor/
noResultsHint/minLength i18n keys already existed, just unused on this path).
- catalog-container.component.html: icon-only close/remove buttons (filter
drawer, sort sheet, grid sheet, saved-search chip) rendered a literal "x"
text character with no app-icon - now use app-icon name="x".
Debounce (220ms, search.facade.ts), URL query-param sync, keyboard
arrow-key suggestion navigation (role=combobox/aria-activedescendant), and
filter/sort discoverability were all verified already correct on this path,
no changes needed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>