docs: add TODO.md checklist, delete archived reports

- docs/TODO.md: checklist of every open item from KNOWN-ISSUES.md/
  FRONTEND-ROADMAP.md/BACKEND_API_REMAINING_WORK.md/ANGULAR22_PLAN.md,
  re-verified against current repo state (git ahead count, package.json,
  app.routes.ts) rather than copied blind. Backend items kept but
  marked skipped per user request (doing together separately).
- Deleted docs/archive/ (19 files) now that every open finding was
  confirmed already merged into KNOWN-ISSUES.md/FRONTEND-ROADMAP.md.
  Full original text recoverable via git history
  (git log --diff-filter=D -- docs/archive).
- Fixed the resulting dangling docs/archive/* references in
  PROJECT_INDEX.md/KNOWN-ISSUES.md/FRONTEND-ROADMAP.md.

Verification: tsc --noEmit clean, npm run build green, 0 broken
markdown links across 49 files (checked programmatically). No
application code touched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-07-25 19:24:29 +04:00
parent 5374401257
commit 46c7358dde
23 changed files with 79 additions and 2548 deletions

View File

@@ -1,61 +0,0 @@
# Accessibility Report — RC A11Y-01
WCAG 2.1 AA audit + fixes across the whole application: Storefront, Builder (Project Editor), Backoffice (Admin). No redesign, no layout changes, no route/business-logic changes. Three sequential commits on branch `B2B` (not pushed):
| Commit | Scope |
|---|---|
| `fb1afb7` | Storefront |
| `a03260e` | Builder |
| `565fd9b` | Backoffice |
`npm run build` verified green after every commit. `npx tsc --noEmit` clean throughout.
This audit goes deeper than the prior composition pass (`docs/UI-COMPOSITION-REVIEW.md` — skeletons/empty-states/`scope="col"`) into categories that pass didn't cover: skip links, keyboard-operable drag-and-drop, dialog focus management, color contrast ratios, form label association, live-region announcements, and combobox/tablist ARIA patterns.
## Fixes by area
### Storefront (`fb1afb7`)
- **Skip link** — didn't exist anywhere in the app. Added as the first focusable element (`app.html`), targeting a new `#main-content` landmark. Single most commonly-missing WCAG 2.4.1 fix, present nowhere before this.
- **Keyboard/focus traps** — header mobile menu kept its items tabbable/screen-reader-reachable while visually collapsed; fixed via `visibility` + delayed transition. Header's search input had no keyboard activation path.
- **Dialog accessibility** — cart's custom payment/bank-payment modals had *no* focus-trap, Escape handling, or return-focus, despite prior docs assuming `app-dialog` coverage — they're custom, not `app-dialog`. Ported the confirmed-correct `app-dialog` pattern directly onto them.
- **Screen-reader announcements** — payment status screens (creating/waiting/success/error/timeout) got `role="status"`/`role="alert"` + `aria-live`.
- **Search combobox** — added `role="combobox"`/`aria-autocomplete`/`aria-controls`/`aria-activedescendant` so the existing arrow-key navigation is actually announced to screen readers (previously silent).
- **Tabs** — product tabs had `role="tab"` with no `tablist`/`tabpanel` wrapper; completed the pattern.
- **Forms** — review-form errors now `aria-describedby` + `role="alert"`; `aria-required` on review textarea and delivery select.
- **Icons** — `app-icon` (shared, app-wide) claimed "decorative by default" in its own contract but never actually applied `aria-hidden` — fixed for real, affects every icon usage across the app.
- **Contrast** — `--text-light` measured 3.39:1 (dexar theme) / 2.54:1 (lavero/novo) against the 4.5:1 requirement. Switched the 2 in-scope usages to `--text-secondary` (7.56:1 / 4.55:1).
### Builder (`a03260e`)
- **Skip link** — Builder routes render outside the storefront app-shell entirely (bare `<router-outlet>`), so the storefront fix didn't cover it. Added separately with its own `#builder-main-content` landmark.
- **Keyboard-operable drag-and-drop (WCAG 2.1.1)** — Homepage section's block list and Footer section's columns/links use Angular CDK drag-drop with zero keyboard equivalent. Added move-up/move-down button fallbacks (Navigation and Widgets sections already had this from an earlier sprint).
- **Undefined `--color-primary` token** — same bug class as the prior composition pass, missed there: 7 usages across 3 files silently fell back to hardcoded hex, never responding to tenant theming. Remapped to the real `--primary-color` token.
- **Forms** — color-picker's `<input type="color">` swatches (8 instances) had no accessible name; new-locale input relied on placeholder text alone (invisible to screen readers once a value is typed).
- **Screen-reader announcements** — save-bar status/draft-restored notice and media-picker upload errors had no `aria-live`/`role`.
### Backoffice (`565fd9b`)
- **Sidebar nav mislabeled** — `<nav aria-label>` reused the "Dashboard" translation key, so the entire admin sidebar announced itself as "Dashboard" to screen readers regardless of which section a user was in. New dedicated label.
- **Categories tree drag-and-drop (WCAG 2.1.1)** — native HTML5 DnD reorder had no keyboard path; arrow keys only expand/collapse/select. Added move-up/move-down buttons per row, boundary-disabled, reusing the existing `reorder()` facade method (no logic change).
- **Screen-reader loading announcements** — `app-skeleton` is `aria-hidden` by design (correct for visual-only use), but 9 list pages (Products, Categories, Customers, Orders, Transactions, Users, Reviews, Monitoring, Analytics) had *no* accompanying live-region text, so screen-reader users got total silence during loads. Added `role="status"`/`aria-live="polite"`/`aria-busy` + sr-only text.
- **Table row headers** — `scope="col"` was already complete from the prior pass; added `scope="row"` to the primary identifying cell on 9 tables (product/category/customer name, order/transaction number, etc.) with matching CSS resets so the shared table's header styling doesn't visually leak onto them.
- **Dialogs, forms, `scope="col"`** — already correct across the board (all route through `app-dialog`/`app-form-field`), confirmed not re-touched.
## Deliberately not fixed (flagged for a design-decision follow-up)
Failing color contrast that is a **genuine brand/semantic color**, not a bug — fixing these means changing what the palette looks like, which needs sign-off from whoever owns the theme, not an accessibility pass's unilateral call:
- `--border-color` fails 3:1 UI-component contrast in every theme (1.241.42:1) — pervasive token, hundreds of usages across the whole app.
- `--success-color`/`--warning-color`/`--error-color`/`--info-color` fail 4.5:1 when used as plain text-on-white in a handful of places.
- Builder save-bar's warning/error/info text-on-white — same category.
Other flagged items:
- Locale-tabs' partial tablist pattern (no roving-tabindex) — accepted precedent across all 3 surfaces, not fixed in any of them.
- No toast/success-error notification system exists anywhere in the app (backoffice save/delete has nothing to announce — a feature gap, not an accessibility regression of existing UI).
- Dashboard's per-card mini-skeletons — would need a page-level live region rather than per-widget, deferred.
- A handful of already-known partial-widget gaps carried from `docs/UI-COMPOSITION-REVIEW.md` (monitoring events table, notifications dropdown, analytics `lowStockProducts` missing skeleton) — pre-flagged, not newly found, not re-litigated here.
## Verification
`tsc --noEmit` and `npm run build` green after all three commits. No live browser/screen-reader session available this session — recommend a manual pass with an actual screen reader (NVDA/VoiceOver) and automated tooling (axe-core/Lighthouse a11y score) against a deployed build before the client demo referenced in `docs/FRONTEND-ROADMAP.md`, since static-analysis ARIA correctness doesn't guarantee real-world screen-reader UX.

View File

@@ -1,194 +0,0 @@
# Admin Panel UX/Product Audit — First-Time Customer Walkthrough
**Date:** 2026-07-17
**Role played:** Senior Product Designer / UX Researcher / SaaS PM / Accessibility reviewer, evaluating the admin panel as a store owner opening it for the first time, knowing nothing about this codebase.
**Method:** Live walkthrough of every routed backoffice screen (`/:lang/backoffice/**`) via `devBypassAdmin=true`, reading rendered DOM/accessibility tree and page text, cross-checked against source (`app.routes.ts`, i18n files, guards, components). No fixes applied — audit only, per request.
**Screens covered:** Dashboard, Categories (list + create), Products (list + create), Orders (list + detail), Transactions, Users & permissions, Monitoring, Analytics, Media Library.
> Scope note: the ~178-key missing-translation bug (every `adminProducts.*`, `adminCategories.*`, `adminOrders.*`, `adminTransactions.*`, `adminUsers.*`, `adminMonitoring.*`, `adminAnalytics.*` string rendering as a raw key like `adminProducts.create`) is **already tracked** in [KNOWN-ISSUES.md](../KNOWN-ISSUES.md#2) and deferred to Sprint 29. This audit does not re-report it as a new bug, but treats it as the single biggest blocker to a first-time user understanding *any* admin screen, and uses the raw keys as a proxy for the intended copy when judging IA and labeling choices below.
---
## 0. The one thing that matters most
**There is no admin shell.** No sidebar, no top nav, no breadcrumbs, no "you are here" indicator anywhere in the codebase (confirmed: no `admin-shell`/`admin-nav`/`admin-sidebar`/layout component exists at all). Every backoffice page is: the **public storefront header** (logo, catalog, search, wishlist, cart, region/currency/language switcher — all customer-facing) + a single unlabeled **"Вернуться" (Back)** button + raw page content. To get from Products to Orders, a user must click Back all the way to the Dashboard and click a link there, or hand-edit the URL.
This one gap is why almost every screen below scores badly on "can a first-time user understand where they are and where else they can go." Fixing it is the highest-leverage single change in this plan (see Task R1).
---
## 1. Screen-by-screen findings
### 1.1 Dashboard (`/backoffice/dashboard`)
1. **What is this page?** Landing page after admin login; store status + shortcuts.
2. **Goal:** Give an at-a-glance health check and jump-off points to every admin section.
3. **Understandable first-time?** Partially. The quick-actions list (translated: "Категории, Товары, Транзакции, Заказы..." etc.) is clear. The stat cards are not.
4. **Confusing:**
- Metric cards for Categories/Products say **"Не удалось загрузить карточку"** ("Card failed to load") — reads as a broken feature, not as "no data yet." A first-time user with zero products would reasonably think the dashboard is malfunctioning.
- Orders/Revenue cards say **"Ожидает интеграции с бэкендом"** ("Awaiting backend integration") — raw internal/dev language shown to an end user. No store owner should ever see the word "backend integration."
- **System status list is unreadable as a checklist.** Header says "Все проверки пройдены" (All checks passed) directly above six items — "Bootstrap корректен", "Конфигурация корректна", "Отсутствуют переводы", "Некорректные цвета", "Некорректные ссылки виджетов", "Некорректные макеты" — with **no pass/fail icon or state per item**. Two of six items are literally negative-sounding phrases ("Missing translations", "Invalid colors") sitting under an "all passed" banner with no visual distinction from the two genuinely-positive ones. A user cannot tell which of the 6 is good and which is bad without reading a color they can't see in the accessibility tree (needs live visual check — flag for a11y contrast/icon-only-signal audit too).
5. **Missing:** No onboarding / "getting started" checklist for a marketplace in Draft status with 0 products. No empty-state guidance ("Add your first product", "Publish your store"). No timestamp/freshness indicator on the stat cards so users know if "failed to load" is real-time or stale.
6. **Meaningless inputs:** None (read-only page).
7. **Technical labels:** "Версия конфигурации: 1.0.0", "Активный макет: default", "Активные виджеты: 3" — config version, layout name, and widget count mean nothing to a non-technical merchant. No tooltip/explainer.
8. **Dangerous actions:** None on this page.
9. **Missing explanations:** Every stat card is a bare label + value, zero context ("Заказы" with no count and no explanation of why").
10. **What would Shopify do:** Home screen would show a setup checklist (theme published? first product added? payment configured?), real numbers or a clean "—" with a tooltip explaining why, and would never expose the words "backend integration" or "bootstrap" to a merchant.
### 1.2 Categories — List (`/backoffice/categories`)
12. Manage the category tree; filter/search/create.
3. Not understandable today — see i18n gap (search box placeholder, visibility filter, "show deleted" checkbox, and Create button are all raw keys).
4. **Confusing beyond i18n:** the visibility filter on the *Categories* list reuses `adminProducts.*` keys (`adminProducts.allVisibility`, `.visible`, `.hidden`) — i.e. the categories filter is either copy-pasted from Products and never renamed, or is genuinely sharing the wrong translation namespace. Either way it signals categories were bolted on after products without a shared, deliberate filter component.
5. **Missing:** No bulk actions (bulk hide/delete/move), no category count-of-products column, no drag-to-reorder indicator visible from the list itself (reorder exists per git history but isn't surfaced here as an affordance).
6. **Meaningless inputs:** "Show deleted" checkbox with a plain checkbox + label, no explanation of what "deleted" means here (soft-delete? Restorable? For how long?).
9. **Missing explanations:** None of the destructive-adjacent controls (deleted-items toggle) explain retention/recovery.
10. **Shopify:** Category/collection list has search, a clear empty state with a CTA, and inline reorder — no separate hidden filter-vs-products confusion.
### 1.3 Category editor — Create/Edit (`/backoffice/categories/create`)
12. Create or edit one category: title, slug, parent, icon, image, description, per-locale translations, SEO fields.
3. **Not understandable at all today** — this form is close to 100% raw i18n keys: `adminCategories.title *`, `adminCategories.slug *`, `adminCategories.parent`, `adminCategories.icon`, `adminProducts.visible` (again, wrong namespace bleeding from Products into Categories), `adminCategories.image`, `adminCategories.description`, `adminProducts.translations`, three repeated blocks of `adminCategories.title ru/en/hy` + `adminCategories.description ru/en/hy`, `adminProducts.seo` / `.metaTitle` / `.metaDescription` / `.keywords`, `adminCategories.saveDraft` / `.publish`.
4. **Confusing (design-level, independent of the i18n bug):**
- **Slug field has no explanation.** A first-time non-technical user does not know what a "slug" is, why it's required (marked `*`), or that it drives the public URL.
- **Three full translation blocks (ru/en/hy) are shown inline, always, for every category**, even for a store that may only ever sell in one language. No progressive disclosure, no "same as default" shortcut, no indication of which language is required vs optional.
- Two save actions — "Save draft" and "Publish" — with no explanation of what "draft" means for a category (is it visible in the storefront tree while draft? Can customers browse it?).
5. **Missing:** No live slug preview (e.g. `yoursite.com/catalog/slug-here`), no character-count/length guidance on SEO meta fields, no validation-error summary, no "unsaved changes" warning surfaced in the UI (a dirty-guard exists in code but its user-facing message wasn't visible in this pass).
6. **Meaningless inputs:** SEO block (`metaTitle`, `metaDescription`, `keywords`) presented as three bare text inputs with zero preview of how they'll appear in a Google search result — this is the #1 place a preview snippet pays for itself.
7. **Technical labels:** "Slug" itself is the clearest example — should be "URL page address" or similar with the slug shown live, not a bare technical term as the primary label.
8. **Dangerous:** Not surfaced in create mode; needs a follow-up pass on the delete/archive flow from the list page specifically (confirm dialogs, soft-delete recovery path).
10. **Shopify equivalent (Collections):** single-language-first form with an explicit "Search engine listing preview" card, auto-slug from title with an "Edit" link (not a required raw field), and translations handled in a separate, optional side panel — not three duplicated blocks in the main form.
### 1.4 Products — List (`/backoffice/products`)
Same shape and same issues as Categories list (filters/search/create button all raw keys; "Products" empty state copy is one of the few translated strings that actually exists — `adminProducts.emptyTitle`/`emptyDescription` — showing the team did design *some* empty states, just not the majority of the surface).
### 1.5 Product editor — Create/Edit (`/backoffice/products/create`)
12. The most complex form in the admin: identity, media, pricing, inventory, content, per-locale translations, SEO, marketplace badges, variants, related products, and a live preview + read-only reviews/questions panel.
3. **Not understandable today** — effectively every label on this page is a raw key (55+ distinct keys rendered literally in this one pass alone).
4. **Confusing (design-level):**
- **One long unsegmented scroll** with no section navigation (no tabs, no sticky sub-nav, no "jump to Pricing/Inventory/SEO"). For a form this large, a first-time user has to scroll past media, pricing, inventory, content, three language blocks, SEO, and marketplace badges before reaching Save.
- **SKU is the only field shown untranslated as plain "SKU"** — every other field label is a raw key, but SKU renders correctly because it's literally the string `"SKU"` in the template, not a translation call. Accidental correctness, not intentional design — worth noting as a code smell alongside the i18n gap.
- **Stock status has three raw states** (`inStock`/`lowStock`/`outOfStock`) plus a separate `quantity` field and a separate `availability` field — three overlapping concepts (quantity, status, availability) with no visible explanation of how they relate to each other or which one wins if they disagree.
- **Badges section** (`featured`/`recommended`/`new`/`bestseller`) — four toggles with no explanation of where these badges actually appear to customers or what they do.
- **Variants section** has a `variantsHint` key specifically for this purpose (good instinct by whoever built it) but it too renders as a raw key, so the one field meant to reduce confusion currently adds to it.
5. **Missing:** No autosave/save-progress indicator on a form this long, no field-level validation feedback visible in this pass, no image alt-text field (accessibility gap — product images need alt text for screen-reader shoppers, not just an admin convenience).
6. **Meaningless inputs:** `Priority` field with no explanation of what it sorts or ranks. `Barcode` and `Brand` presented with equal visual weight to `Name`/`Price` — no visual hierarchy signaling which fields are essential vs optional for launch.
9. **Missing explanations:** SEO block, same gap as categories — no search-result preview.
10. **Shopify equivalent (Product editor):** clearly hierarchized sections with a persistent right-rail (status/organization/inventory) separate from a left-column content flow (title → media → description → pricing), inline "Preview" always visible, and every ambiguous field (SKU, barcode) has inline help text.
### 1.6 Orders — List (`/backoffice/orders`)
12. Browse all orders, filter by status, export.
3. Table structure itself (order #, customer, price, date, view) is legible even with raw status-key values, because the *layout* (a plain table) is a familiar pattern.
4. **Confusing:**
- Every status badge renders as `adminOrders.status.pending` / `.processing` / etc. instead of a real label — and there's no color-coding visible in the text-only pass to compensate (needs a live visual check for whether color alone carries the status, which would itself be an accessibility problem if true).
- Dates render as **`7/17/26, 11:39 PM`** — US month/day/year format — on a `ru` locale page. Confirmed locale-hardcoded date formatting bug, same family of issue as the previously-fixed `en/ru/hy` tab-hardcoding bug noted in [KNOWN-ISSUES.md](../KNOWN-ISSUES.md).
- "24 `adminProducts.items`" — the pagination footer on the *Orders* list borrows the word "items" from the *Products* translation namespace. Third confirmed instance (after Categories' visibility filter and this) of admin modules cross-borrowing copy from Products instead of having their own strings — suggests Products was built first and every later module was scaffolded by copy-pasting it without renaming keys.
5. **Missing:** No bulk status update, no date-range filter (only a status filter is present), no order search by customer name/number.
8. **Dangerous:** "Export" action with no visible confirmation or indication of what leaves the system (all orders? filtered view? does it include customer PII, and if so is that flagged for the user?). Worth a data-privacy pass, not just a UX one.
10. **Shopify:** Orders list has saved filter views, bulk actions, and a search bar as first-class citizens, not an afterthought.
### 1.7 Order detail (`/backoffice/orders/:id`)
**This screen is broken, not just confusing.** Clicking "View" on any order in the list navigates to a detail page that shows **"Загрузка..." (Loading...) forever** — confirmed via network trace: no `/api/backoffice/orders/:id`-shaped request is ever fired, and the console logs two swallowed `HttpErrorResponse` errors with no resulting error state shown to the user. A first-time admin's very first attempt to inspect an order dead-ends on an infinite spinner with no error message, no retry button, and no way back except the browser Back button (there's no in-app back link visible on this screen in the current DOM). This is the most severe single finding in the audit — **flagging for engineering, not just design**, since it blocks the #1 reason an admin opens Orders at all.
### 1.8 Transactions (`/backoffice/transactions`)
12. Payment/refund ledger with fraud flags and retry/audit actions.
34. Same i18n gap as Orders, plus:
- **Payment method column shows raw backend enum values** — `card`, `qr`, `cash_on_delivery` — instead of humanized labels. `cash_on_delivery` in particular is a raw snake_case API value leaking straight into the UI.
- **Fraud flag column** shows `adminTransactions.flagged` for one row and is blank for the rest, with no legend explaining what "flagged" means, who flagged it, or why.
8. **Dangerous actions, under-protected:** `Flag`, `Clear flag`, `Retry`, `Audit` are all small inline row actions on financial transactions with no confirmation step visible and no explanation of consequence (does "Retry" re-charge the customer's card? Does "Clear flag" affect fraud reporting to a payment processor?). This is real financial admin surface and it currently reads like a debug table, not a finance tool.
10. **Shopify Payments equivalent:** every retry/refund action requires an explicit confirm step and states the exact monetary consequence before the click is committed.
### 1.9 Users & Permissions (`/backoffice/users`)
12. Manage admin team members, roles, invitations.
34. i18n gap again, plus:
- **Role permissions shown as raw dot-notation scope strings**: `"Admin — products.manage, categories.manage, orders.manage, media.manage"`, `"Viewer — products.view, orders.view"`. This is API/RBAC internal vocabulary shown directly to a business owner deciding who gets what access — genuinely unusable for the intended audience without translation into plain sentences ("Can edit products and categories, cannot manage users or see financials").
- `adminUsers.passwordlessHint` renders raw right next to the invite form — whatever it's meant to explain about the passwordless Telegram-based login flow is currently invisible.
8. **Dangerous actions, under-protected:** `Suspend` sits as a plain inline row action next to `Sessions` and `Audit` with equal visual weight — suspending a teammate's access should read as more consequential than viewing their session history, and today it doesn't.
10. **Shopify (Staff/permissions):** permission scopes are grouped into named presets with checkboxes and one-line plain-English descriptions per permission, never raw API scope strings.
### 1.10 Monitoring (`/backoffice/monitoring`)
1. **What is this page, really?** This is the single biggest audience-mismatch finding in the whole audit. The page shows **raw HTTP request logs** (`GET /api/products responded 200 in 84ms`), **queue depths** (`webhook-delivery`, `depth: 12`, `degraded`), and **webhook delivery status to partner endpoints** (`https://partner.example.com/orders`). This is an SRE/engineering observability dashboard, not a marketplace-owner feature.
2. **Goal:** unclear whether the intended audience is the store owner or the platform's own engineers.
3. **Understandable to a first-time non-technical user?** No — "queue depth", "webhook delivery", raw latency numbers, and stack-trace-style event messages ("Unhandled exception in checkout flow (#5)") are meaningless and mildly alarming to a merchant with no engineering background.
45. If this page is meant for merchants, it needs to be replaced with something like "system health at a glance" (payments working? notifications sending?) in plain language. If it's meant for engineers/support staff, it should be gated behind a separate internal-tools role, not sit as a peer nav item next to Products and Orders.
10. **Shopify** has no merchant-facing equivalent to this at all — this level of detail lives in an internal ops tool, never in the merchant admin.
### 1.11 Analytics (`/backoffice/analytics`)
12. Sales chart, top products, funnels/heatmaps, date-range toggle (7d/30d/90d).
34. Currently just renders `adminAnalytics.pendingBackend` / `.pendingBackendHint` — i.e. this whole section is an explicit "not built yet" placeholder. That's honestly fine *if* it's labeled as coming soon in real copy; today (raw keys) it looks like every other broken screen rather than an intentional placeholder, which is a missed opportunity — a well-designed "coming soon" state builds more trust than a silent one that looks identical to a bug.
10. **Shopify:** ships a real, if basic, analytics overview from day one (sessions, conversion rate, top products) — this is normally considered table-stakes, not a phase-2 feature, for a marketplace admin.
### 1.12 Media Library (`/backoffice/media`)
Best screen in the audit. Title ("Медиатека") and empty-state copy ("Пока нет файлов" / "Загрузите изображение или документ, чтобы начать") are actually translated and read like real product copy — this is proof the team can write good empty-state UX when the i18n step isn't skipped. Two folder-related labels (`mediaLibrary.allFolders`, `mediaLibrary.newFolder`) still leak raw, but the page overall is the clearest single evidence of the *intended* quality bar for the rest of the admin.
### 1.13 Admin login dialog
Attempting to reach any `/backoffice/**` route while unauthenticated shows a dialog titled **"Требуется авторизация"** (Authorization required) with body copy **"Для оформления заказа войдите через Telegram"** — *"To place an order, sign in via Telegram."* This is the **customer checkout login dialog's copy**, reused verbatim for the admin login gate. A store owner logging into their own admin panel is told they need to log in "to place an order," which is simply wrong context and actively confusing on the very first screen an admin ever sees.
---
## 2. Cross-cutting issues (apply to nearly every screen above)
| # | Issue | Where seen |
|---|---|---|
| A | No persistent admin navigation shell (sidebar/topbar/breadcrumbs) — every page is an island reachable only from the Dashboard's quick-action links | All screens |
| B | ~178 raw i18n keys across products/categories/orders/transactions/users/monitoring/analytics (tracked in KNOWN-ISSUES.md, Sprint 29) | Nearly all screens except Media Library |
| C | Copy-paste key bleed between modules (Products keys used in Categories/Orders/Transactions/Users tables and forms) — signals no shared, deliberately-named translation vocabulary for common table/action words ("items", "actions", "visible") | Categories, Orders, Transactions, Users |
| D | Raw backend/API vocabulary shown to end users (payment method enums, RBAC scope strings, "backend integration", "bootstrap", HTTP status/latency) | Dashboard, Transactions, Users, Monitoring |
| E | Locale-hardcoded date formatting (US format on `ru` pages) | Orders, Transactions, Monitoring |
| F | Dangerous/irreversible actions (suspend user, retry payment, clear fraud flag, export financial data) presented with the same visual weight as safe actions, no confirm step observed | Transactions, Users, Orders |
| G | No section navigation on long forms (product editor is one long scroll with 55+ fields) | Product editor, Category editor |
| H | No SEO/search-result preview despite collecting meta title/description/keywords | Product editor, Category editor |
| I | Ambiguous pass/fail status list with no per-item visual state | Dashboard system status |
| J | Audience mismatch — engineering-grade observability data presented as a merchant feature | Monitoring |
| K | Wrong-context dialog copy (checkout copy shown on admin login) | Login gate |
---
## 3. Redesign plan, prioritized by user impact
### P0 — Blocking (a first-time user cannot complete their core job without these)
| Task | Description | Why P0 |
|---|---|---|
| **R1. Build an admin shell** | Persistent sidebar or topbar with all 9 sections, active-state highlighting, and a clear "Admin" vs "Storefront" mode indicator (replace the reused public header entirely inside `/backoffice/**`, or clearly demarcate it). Add breadcrumbs on editor/detail pages. | Nothing else in this plan matters if users can't navigate between sections without returning to Dashboard each time. |
| **R2. Fix the Order detail infinite-loading bug** | Order detail must resolve to real data or a clear error/not-found state; never hang on "Loading..." forever. | Orders is a top-3 reason to open an admin panel at all; this is currently 100% broken. |
| **R3. Ship the missing i18n content** | Author the ~178 keys already scoped in KNOWN-ISSUES.md Sprint 29, and while doing so, de-duplicate/rename the cross-module key bleed (C above) into a proper shared vocabulary (`common.actions`, `common.itemsCount`, etc.) instead of copy-pasting Products' keys. | Every other design fix is invisible until real copy exists to evaluate it against. |
| **R4. Fix the admin login dialog copy** | Admin gate needs its own title/body copy ("Sign in to manage your store") distinct from the customer checkout dialog. | First screen every admin sees; currently tells them they're trying to place an order. |
### P1 — High impact (materially damages trust/usability but has workarounds)
| Task | Description |
|---|---|
| **R5. Humanize raw backend vocabulary** | Payment methods (`cash_on_delivery` → "Cash on delivery"), RBAC scopes (`products.manage, orders.manage` → "Can manage products and orders"), dashboard jargon ("backend integration", "bootstrap", "layout", "widgets") → plain merchant language with optional "advanced" tooltips for power users. |
| **R6. Redesign Dashboard stat cards and system-status list** | Distinguish "no data yet" from "failed to load" with different copy/iconography; give the system-status list explicit pass/fail icons per item, not a single "all passed" banner over mixed-sounding items; add a first-run onboarding checklist for a Draft-status store with 0 products. |
| **R7. Add confirm steps to dangerous actions** | Suspend user, retry payment, clear fraud flag, export data — each needs an explicit confirmation with a plain-language statement of consequence before it fires. |
| **R8. Fix locale-hardcoded date formatting** | Route all admin date rendering through the locale-aware formatter already used elsewhere in the app, not a hardcoded en-US format. |
| **R9. Reposition or gate Monitoring** | Decide the intended audience: if merchants, replace with a plain-language "system health" summary (payments OK / notifications OK); if internal engineering, move it out of the merchant-facing nav entirely and behind a separate internal role. |
### P2 — Meaningful polish (improves specific flows, not systemic)
| Task | Description |
|---|---|
| **R10. Segment the Product editor** | Sticky in-page section nav or tabs (Identity / Media / Pricing / Inventory / Content / Translations / SEO / Marketplace) instead of one long scroll; add a persistent Save/status rail. |
| **R11. Add SEO preview** | Live Google-style search-result preview wherever meta title/description/keywords are collected (products, categories). |
| **R12. Progressive disclosure for translations** | Default to the store's primary language; make additional-language fields collapsible/optional rather than always-expanded triplicate blocks. |
| **R13. Explain ambiguous fields inline** | Slug, Priority, Barcode, stock-status vs quantity vs availability, product badges (Featured/Recommended/New/Bestseller) — one-line inline help text or tooltip for each. |
| **R14. Turn Analytics' placeholder into a real "coming soon" state** | Once i18n exists, give it honest, designed placeholder copy and a rough timeline/CTA rather than a bare pending message indistinguishable from a bug. |
| **R15. Accessibility pass** | Product image alt-text field; verify status/fraud-flag badges don't rely on color alone (WCAG 1.4.1); verify all icon-only buttons (region selector, search) have accessible names — spot-checked via the accessibility tree during this audit but needs a dedicated pass with axe/AccessLint against real rendered pages. |
---
## 4. Suggested sequencing
R1R4 (P0) should land together or in immediate succession — R3 in particular unblocks a real visual/usability re-review of everything else, since today's raw-key state makes several judgments above (color-only status signaling, information density, visual hierarchy) impossible to fully confirm without live rendering. Recommend a follow-up visual (screenshot-based) pass once R3 ships, to validate color/contrast/hierarchy findings that this text-based audit could only flag as "needs live check."

View File

@@ -1,74 +0,0 @@
# Admin UI Polish — RC1 Review
Scope: production-ready UI/UX polish pass across the admin area, one page at a time.
Architecture, routing, facades, providers, APIs, and data models were left unchanged
per instructions. No `docs/DESIGN.md` exists in this repo (confirmed during the
storefront pass), so no page could be checked against it literally; polish decisions
here follow the shared component library (`app-button`, `app-table`, `app-empty-state`,
`app-skeleton`, `app-badge`, `app-card`, `app-toggle`) and existing conventions instead.
## Pages reviewed
| Page | Commit | Status |
|---|---|---|
| Dashboard | — | reviewed, no changes needed |
| Marketplace Builder | `fix(admin): polish marketplace-builder` | done |
| Products | `fix(admin): polish products` | done |
| Categories | `fix(admin): polish categories` | done |
| Orders | `fix(admin): polish orders` | done |
| Customers | — | reviewed, no changes needed |
| Reviews (moderation) | `fix(admin): polish reviews` | done |
| Reports | — | reviewed, no changes needed |
| Transactions | — | reviewed, no changes needed |
| Analytics | — | reviewed, no changes needed |
| Monitoring | — | reviewed, no changes needed |
| Media | — | reviewed, no changes needed |
| Settings | — | no implementation exists (see backlog) |
| Diagnostics | — | reviewed, no changes needed (dev-only, excluded from production) |
## Fixes
**Dead code:**
- Marketplace Builder: removed an unused `ButtonComponent` import/registration from
`ProjectEditorHomepageSectionComponent` — it was never referenced in the template
and had been silently flagged by every single build (`NG8113` warning) until now.
**Accessibility — recurring pattern across list pages:**
The admin list pages (Products, Categories, Orders, Reviews) share a common
table-view layout: a "select all" checkbox in the header and a per-row checkbox in
each `<tr>`. In every one of these tables, **the table view's checkboxes had no
accessible name** — a screen reader announced only "checkbox, not checked" with no
indication of what it selects. Notably, the **grid-view equivalent on the same pages
already had `aria-label`** (e.g. product name, category title, customer name), so this
was an inconsistency introduced when the table view was built, not a project-wide gap.
Fixed identically across all four:
- Products: `adminProducts.selectAllRows` / `selectRow`
- Categories: `adminCategories.selectAllRows` / `selectRow` — plus the per-row
**visibility-toggle** checkbox, whose `<label>` wrapped only the input with no text
content at all (empty accessible name), got `adminCategories.toggleVisibility`
- Orders: `adminOrders.selectAllRows` / `selectRow` (row label includes order number)
- Reviews: `adminModeration.selectAllRows` (row checkbox already had a label via
`review.customerName`, only select-all was missing)
All new keys added across `en`/`ru`/`hy`.
## Remaining backlog
- **Settings**: `src/app/features/backoffice/settings/` contains only a `.gitkeep`
no component, no route. Nothing to polish until the feature is built. (Same
situation as Checkout in the storefront pass.)
- Dashboard, Customers, Reports, Transactions, Analytics, Monitoring, Media, and
Diagnostics were already in good shape at review time — consistent use of shared
components, existing `:focus-visible` coverage on their buttons, empty/loading
states already using `app-empty-state`/`app-skeleton`. No action items logged for
these; re-review if they're touched again after backend integration lands new UI.
- The select-all/row-checkbox accessible-name gap was fixed everywhere it was found
in this pass (Products/Categories/Orders/Reviews). If any future admin list page is
built from the same table pattern, carry the `aria-label` over from day one rather
than relying on a follow-up audit to catch it.
- No `docs/DESIGN.md` exists for either the storefront or admin area. If literal
design-system enforcement (type ramp, radius scale, color palette) is wanted going
forward, author that file — `impeccable` hook findings are currently checked against
a document that doesn't exist.

View File

@@ -1,35 +0,0 @@
# Cleanup Report
> **Archived, correction (2026-07-25):** this pass concluded `pages/category`, `pages/search`, `pages/info/**`, `pages/legal/**`, `pages/item-detail` were live/routed and kept them — that conclusion was **wrong**. A later documentation-consolidation pass confirmed directly against `app.routes.ts` that all of these are genuinely unrouted dead code (`cmsContentRoutes` is a literal empty array). See `docs/KNOWN-ISSUES.md` item 13. Kept here for historical accuracy of the process description (the false-positive root-cause analysis re: knip's blind spot is still useful) — the conclusion about these files being "correctly kept" is superseded.
App-wide dead-code sweep: dead components/services/routes/CSS, duplicate models/interfaces, unused imports/variables/assets/icons/SCSS/directories/modules. Business logic untouched — deletion only, no refactoring. One commit on branch `B2B` (not pushed): `e0bcf9d`.
## Important note on process
The first attempt at this task was interrupted mid-run (hit a usage-limit cutoff) and left an **uncommitted, unverified mass-deletion staged**: 117 files, ~11,373 lines, including `pages/category`, `pages/search`, `pages/info/**`, `pages/legal/**`, `pages/item-detail` — all real, live, routed storefront pages (category/search were fixed for accessibility/skeleton bugs earlier in this same work cycle). This was caught before commit and fully reverted (`git restore`) — verified clean via `tsc --noEmit` afterward. The retry used a stricter standard: every deletion candidate had to be manually verified against `app.routes.ts`/DI registries/cross-repo grep before deletion, not just trusted from a dead-code tool's raw output, and every batch was build-verified immediately rather than at the end.
**Root cause**: `knip` repeatedly flagged the entire `pages/category`/`pages/search`/`pages/info/**`/`pages/legal/**`/`pages/item-detail` tree as unused, both times it was run. This is a confirmed **false-positive blind spot** in this codebase — likely the locale-nested component pattern (`xxx-en.component.ts` under an `en/` subfolder, loaded via a runtime locale switch rather than a static import knip can trace) combined with lazy `loadComponent` route bindings. Anyone running `knip` on this repo in the future should cross-check its `pages/**` output against `app.routes.ts` before trusting it — it is not reliable for this specific pattern.
## Deleted (26 files, `e0bcf9d`)
| Category | What | Verification evidence |
|---|---|---|
| Auth | `core/auth/guards/ed25519-auth.guard.ts`, `permission.guard.ts`, `core/auth/interceptors/auth.interceptor.ts` | `ed25519AuthGuard` appears only inside a doc-comment in `admin-login-page.component.ts`; `authInterceptor` absent from `app.config.ts`'s `withInterceptors([...])`; neither imported anywhere else. |
| Search | `features/search/services/search-analytics.service.ts`, `features/search/components/empty-results/*` | `SearchAnalyticsService` has zero importers; `app-search-empty-results` selector unused — `search-bar.component.html` implements its own inline `@if (noResults)` empty state instead. |
| Content management | `features/content-management/pages/content-management-page.component.ts` | Thin wrapper around `StaticPagesEditorComponent`, zero route in `app.routes.ts`. Rest of `content-management/*` (facade, static-pages-editor, page-editor) kept — used by `project-editor`. |
| Backoffice scaffolding | `features/backoffice/{categories,customers,inventory,orders,products,settings}`, `features/backoffice/shared/backoffice-coming-soon-page.component.*` | Re-verified `docs/UI-COMPOSITION-REVIEW.md`'s claim independently: `app.routes.ts` backoffice section only loads `features/admin/*` and `features/backoffice/media`. Each of the 6 deleted dirs contained **only a placeholder `.gitkeep`** from the original scaffold commit — never real components. The prior audit's "duplicate implementation" framing was itself stale; corrected here. |
| Shared barrels/models | `shared/index.ts`, `shared/models/index.ts`, `shared/types/index.ts`, `shared/models/domain/**` (incl. `user-preferences.model.ts`) | Unused barrels/model, zero importers. |
| Orphaned page | `pages/public/platform-home.component.ts` | No route, no import anywhere — distinct from the routed `pages/category` etc. that were correctly left alone. |
| Mock data | `assets/mock/backoffice/{customers,orders}/list.json` | Orphaned — `BackofficeDataProvider` only exposes `loadProducts()`/`loadCategories()`. |
## Flagged, NOT deleted (verified as false positives or out of scope)
- **`pages/category`, `pages/search`, `pages/info/**`, `pages/legal/**`, `pages/item-detail`** — knip's recurring false-positive (see Root cause above). All confirmed live and routed. **Note**: `docs/STORE_REVIEW.md` had previously flagged `item-detail` specifically as "confirmed dead code (unrouted)" during RC STORE-01 — that assessment needs re-verification against `app.routes.ts` given this tool's now-confirmed blind spot on this exact directory pattern; not re-checked in this pass, flagged for the next person touching that area.
- **`SearchHistoryService`** — knip flagged it, but it's actively injected in `search.facade.ts`. Another confirmed false positive.
- **`primeng`/`primeicons`/`barry-cache`** — knip flagged as unused dependencies. Not verified further here; `primeng`/`primeicons` removal is already tracked in `docs/KNOWN-ISSUES.md` (item 12) blocked on an unrelated `npm uninstall` failure. `barry-cache` is the repo's own memory tooling, out of scope for a dead-code pass regardless.
- **123 "unused exports/types" flagged by knip** — left alone. This is TypeScript interface/type surface cleanup, not dead files; a different, lower-risk category that wasn't prioritized in this pass given the tool's demonstrated unreliability here — recommend manual review, not knip's raw list, if pursued later.
- **5 branding SVGs** (`mastercard-logo.min.svg`, `dexar-logo*.svg`, `dexar-favicon.svg`, `novo-logo.svg`, `novo-favicon.svg`) — flagged in `docs/PERFORMANCE_REPORT.md` as possibly backend/tenant-driven, not statically referenced. Not touched, per that report's own caution.
## Verification
`tsc --noEmit` clean after every deletion batch (not just at the end — batches were verified incrementally so a bad deletion could be isolated and reverted immediately rather than debugged after the fact). `npm run build` green (only the pre-existing bundle-budget warning). `npm run arch:check:boundaries` and `arch:check:cycles` both pass (435 files processed, no circular dependencies). Working tree clean aside from the pre-existing unrelated `docs/backend/REMAINING-BACKEND-WORK.md` modification, untouched.

View File

@@ -1,55 +0,0 @@
# Performance Report — RC PERF-01
Production-readiness performance audit + fixes, application-wide. No redesign, no UI/visual changes, no route/API changes — pure performance/correctness work. Three sequential commits on branch `B2B` (not pushed):
| Commit | Scope |
|---|---|
| `61a5714` | Angular reactivity, change detection, rendering, memory |
| `4bf0666` | Bundles, lazy-loading, tree-shaking, large chunks |
| `2e931bb` | Assets: images, fonts, SVG, CSS |
`npm run build` verified green after every commit. `npx tsc --noEmit` clean throughout.
## Headline result
Initial bundle: **1.47 MB → 1.12 MB raw** (350 KB / 24%), transfer estimate **263.59 kB → 221.51 kB** (16%). Budget overage shrank from 769 KB over to 417 KB over (still a warning, not an error — the 700 KB budget itself is unchanged and still exceeded, see Remaining below). Global CSS bundle: 9.60 kB → 8.41 kB raw (12%).
## Findings and fixes by area
### Angular reactivity / change detection / memory (`61a5714`)
- **`.subscribe()` audit**: 149 call sites across 44 files. Spot-checked every long-lived stream in the highest-risk components (route params, router events, polling/intervals) — all already use `takeUntilDestroyed(this.destroyRef)` or an explicit `Subscription` + `ngOnDestroy` unsubscribe. One-shot HTTP-backed subscribes self-complete, no leak. **No leaks found** — the codebase's existing discipline here was already solid.
- **`ChangeDetectionStrategy.OnPush`**: 191 components, 190 already had it. Added to the 1 remaining (`app.ts`, the root component) — verified safe (signal-driven template, no direct DOM mutation).
- **`@for`/`track`**: fully compliant app-wide — the codebase is entirely on the new control-flow syntax, which the compiler requires `track` on, so there was nothing to fix. Legacy `*ngFor` without `trackBy`: zero occurrences.
- **Duplicate HTTP calls**: `CategoryFacade` and `ConfigService` already cache via `shareReplay({bufferSize:1, refCount:true})`, applied consistently — no duplicate-request pattern found.
- **Flagged, not fixed**: 2 `combineLatest` sites (`catalog-container.component.ts`, `subcategories.component.ts`) are narrow, already-minimal RxJS composition — converting to `computed()` would touch adjoining facade state shape, judged out of this pass's risk budget. Per-item template method calls in `@for` loops (`getDiscountedPrice`, `itemName`, `categoryName`, `stockLabelKey`) are cheap pure lookups on an already-OnPush host — flagged as pipe-conversion candidates for a future pass, not urgent.
### Bundles / lazy-loading / tree-shaking (`4bf0666`)
- **Lazy-loading boundaries**: already correct — every storefront/builder/backoffice route in `app.routes.ts` uses `loadComponent`, nothing eagerly imported.
- **i18n eager-loading — the biggest single win**: `TranslateService` statically imported all three locale packs (ru/en/hy, 346 KB raw combined) into the initial bundle regardless of visitor language. Fixed: `ru` (default) stays eager, `en`/`hy` are now `import()`ed on demand via a new `preloadLanguage()`, awaited inside `languageGuard` before route activation — router blocks navigation until the pack resolves, so there's no untranslated-flash risk.
- **Dead code**: `knip` found `src/app/components/items-carousel/*` (3 files) completely unreferenced anywhere, and the sole consumer of the `primeng` dependency. Deleted the component and its now-dead `primeicons.css` import from `src/styles.scss`.
- **Tree-shaking**: fixed one accidental barrel import in `widget-host.service.ts` (now imports `UnknownWidgetComponent` directly instead of through the `widgets/ui` barrel) — negligible measured impact, but correct going forward. `@lucide/angular` icons already use named tree-shakeable imports.
- **Duplicate deps**: `npm ls` confirms a single deduped `rxjs@7.8.2` — no duplicate module instances.
- **Flagged, not fixed**: `primeng`/`primeicons` remain in `package.json`/lockfile — `npm install`/`uninstall` fails in this environment (`ETARGET`) on a pre-existing, unrelated broken `barry-cache` devDependency, so the lockfile couldn't be safely regenerated by hand. **Follow-up needed**: fix the `barry-cache` devDependency resolution, then `npm uninstall primeng primeicons` to drop the dependency entirely (the code reference is already gone). Large chunks (`project-editor` 320 kB, `catalog-container` 126 kB, `product-details-container` 88 kB, `cart` 61 kB) unchanged — no mechanical split found inside their scope; a real reduction here would mean lazy-loading a sub-feature (e.g. an embedded editor), which is a larger, dedicated task.
### Assets: images, fonts, SVG, CSS (`2e931bb`)
- **Images**: storefront `<img>` tags already have `loading="lazy"`/`decoding="async"` on grids/galleries and CLS is already handled via CSS `aspect-ratio` (from prior RC-Visual-02/RC-Premium-01 passes) — nothing to fix. Flagged: a handful of single-image admin/editor previews lack `loading="lazy"` (page-editor, brand-overview, asset-details-drawer) — negligible traffic, left alone.
- **Fonts**: `index.html` already preconnects to Google Fonts, loads DM Sans 400/500/600/700 with `display=swap`; all 4 loaded weights are actually used. Flagged: weights 800/900 are referenced in some component SCSS but never loaded (pre-existing faux-bold rendering) — changing loaded weights risks a visible text difference, left out of scope for a perf-only pass.
- **SVG**: icon system is fully centralized through `@lucide/angular` via `icon-registry.ts` — no inline path duplication. `public/` SVGs checked for editor cruft (metadata/inkscape artifacts) — already clean. Flagged: 5 branding SVGs (`mastercard-logo.min.svg`, `dexar-logo*.svg`, `dexar-favicon.svg`, `novo-logo.svg`, `novo-favicon.svg`) appear unreferenced in `src`, left in place — may back tenant branding resolved from the backend at runtime, deletion needs a human decision, not this pass's call.
- **CSS**: manual grep-based cross-check (literal + hyphen-safe, checked for dynamic `[class.x]`/string-built classes too) of `src/styles.scss` utility classes against all templates. Confirmed-dead and removed: `.btn-primary`/`.btn-secondary`+hover, `.catalog-product-card`, `.item-badges-overlay`, `.item-simple-desc`, `.text-center`, `.mt-1..4`, `.mb-1..4`, `.p-1..4`.
## Remaining recommendations (not applied — flagged for follow-up)
1. **`primeng`/`primeicons` still in `package.json`** despite the only consumer being deleted — needs the pre-existing `barry-cache` devDependency resolution issue fixed first, then a clean `npm uninstall`. Highest-value remaining action: would likely close most of the remaining 417 KB budget overage in one move once confirmed.
2. **Large lazy chunks** (`project-editor` 320 kB, `catalog-container` 126 kB) — no mechanical fix found; needs a dedicated task to identify a genuinely lazy-loadable sub-feature inside each.
3. **2 `combineLatest` sites** — safe to leave, candidates for a `computed()` conversion in a future facade-focused pass.
4. **Per-item template method calls** in `@for` loops — pipe-conversion candidates, not urgent (already bounded by OnPush).
5. **Font weights 800/900** used in SCSS but not loaded — either load them explicitly or normalize the SCSS to a loaded weight; a design-system call, not a perf call.
6. **5 unreferenced branding SVGs** — confirm with backend/tenant-branding owner before deleting.
7. **Admin/editor preview images** missing `loading="lazy"` — low priority, low traffic.
## Verification
`tsc --noEmit` and `npm run build` run and green after each of the three commits. No live browser session available this session — real-world Core Web Vitals (LCP/CLS/INP) were not measured; recommend a Lighthouse/WebPageTest pass against a deployed build before the client demo referenced in `docs/FRONTEND-ROADMAP.md`.

View File

@@ -1,706 +0,0 @@
# PROJECT-STATE — Lead Architect Audit
**Audited:** 2026-07-19, branch `B2B`, commit `d853ecb` (HEAD at audit time).
**Method:** Direct source reading (components, facades, gateways, services, models, SCSS, routing) plus parallel research passes over the same source tree, cross-checked against `docs/ARCHITECTURE.md`, `docs/ADMIN.md`, `docs/EDITOR.md`, `docs/StaticPages.md`, `docs/KNOWN-ISSUES.md`, `docs/SPRINT-PLAN.md`, and `docs/backend/BACKEND-INTEGRATION.md`. Every claim below is either grounded in a specific file:line citation or explicitly marked "not verified" / "not exhaustively verified." Prior docs were treated as leads, not truth — every claim reused from them was re-checked against current code during this pass (several turned out to be stale, e.g. the ~178 missing admin i18n keys logged in `docs/KNOWN-ISSUES.md` were fixed by commit `574f038` and no longer reproduce).
No files were modified to produce this document except this one.
---
## 1. Overall architecture
### Stack
Angular 21.1.5, **100% standalone components** (`grep -rl "NgModule" src/app --include=*.ts` → 0 hits). No global state library — no `@ngrx/*`, `@ngxs/*`, or similar in `package.json`. State management is plain injectable Angular services combining RxJS and signals ("service-with-signals"), matching ADR-007 (`docs/architecture/foundation/adr/ADR-007-state-management-and-facade-boundaries.md`).
### Routing (`src/app/app.routes.ts`, 307 lines)
- Every route sits under a `:lang` prefix (`app.routes.ts:297-305`), guarded by `languageGuard` (`src/app/guards/language.guard.ts`), which validates the language code against `LanguageService.languages`, redirects disabled/unknown codes to the current default, and preserves the rest of the path. Bare URLs without a lang prefix redirect to `ru` (`app.routes.ts:307`) — `ru` is the hardcoded fallback default, not configurable from routing alone.
- **100% lazy-loaded**: every route entry uses `loadComponent(() => import(...))`; no eager route components exist anywhere in `coreRoutes` (`app.routes.ts:10-284`) or the `backoffice` children array (`app.routes.ts:56-252`).
- Admin (`/:lang/backoffice/**`) is a single `AdminLayoutComponent` shell (`app.routes.ts:58`, `src/app/features/admin/shell/admin-layout.component.ts`) behind `adminAuthGuard` (`app.routes.ts:57`), with ~20 lazy child routes.
- `canDeactivate` dirty-guards protect unsaved edits: `projectEditorDirtyGuard` (`app.routes.ts:53`), `adminCategoryDirtyGuard` (`app.routes.ts:118,128`). No equivalent guard exists for `admin/products` edit forms — confirmed by absence of a `canDeactivate` entry on the `products/:id/edit` route (`app.routes.ts:88-96`) despite `AdminProductsFacade` having its own dirty-tracking draft logic (per Sprint 21 in `docs/ADMIN.md`). This is an inconsistency: categories protect against navigating away with unsaved changes, products do not.
- `cmsContentRoutes` is a **deliberately empty array** (`app.routes.ts:286-289`) with a `TODO(CMS)` comment: "Disabled hardcoded pages: about, contacts, faq, delivery, guarantee, company-details, payment-terms, return-policy, public-offer, privacy-policy." These pages exist as fully-built components under `src/app/pages/info/**` and `src/app/pages/legal/**` (40 files, per-locale duplicated) but are **entirely unrouted dead code** today — see Section 6.
- Everything unmatched falls through to a catch-all `:staticPath` route (`app.routes.ts:280-283`) resolved dynamically by `StaticPageComponent` against `bootstrap.staticPages`.
- Dev-only `/__diagnostics` route is excluded from production via an `environment.production` ternary at the top of the routes array (`app.routes.ts:293-296`).
- **No dedicated 404/not-found route exists.** Unmatched paths under `:lang` redirect to home (`app.routes.ts:303`, `{ path: '**', redirectTo: '' }`) rather than rendering an error state — a user hitting a genuinely broken link is silently bounced to the homepage with no "page not found" feedback.
- **No maintenance-mode route or component exists anywhere in the codebase** (confirmed: no file matching `*maintenance*` under `src/app`).
### `app.config.ts` (35 lines)
- `provideRouter` with `withInMemoryScrolling({ scrollPositionRestoration: 'top' })`.
- `provideHttpClient` with a fixed, order-significant interceptor chain: `mockDataInterceptor → apiBaseUrlInterceptor → apiHeadersInterceptor → adminAuthHeadersInterceptor → cacheInterceptor` (`app.config.ts:25-27`).
- Two DI overrides hardcoded directly in `app.config.ts`, **not** switched by the runtime-provider-strategy mechanism used elsewhere: `Ed25519VerificationService → NoopEd25519VerificationService` (line 28 — a fail-open/no-op placeholder for a verification step that isn't implemented) and `MediaRepository → MockMediaRepository` (line 29 — the entire media library has no API implementation at all, see Section 5).
### Layered architecture (ADR-002/006/007)
```
Component (container) --> Facade --> Domain Service --> Repository/Provider (DI token) --> Mock | API
```
```mermaid
flowchart LR
A[Container/Page Component] -->|inject facade| B[Facade]
B -->|composes| C[Domain Service]
C -->|DTO to domain mapper| D[Repository / Provider Interface]
D -->|InjectionToken factory, mode from RuntimeProviderStrategyService| E1[Mock Provider]
D -->|InjectionToken factory| E2[Api Provider - HttpClient]
B --> F[Signals / Observables exposed to component]
G[Presentational Component] -->|Input/Output only| A
```
- **Presentational components** (`src/app/shared/ui/**`, `src/app/ui-library/{atoms,molecules,organisms}/**`) are `@Input()`/`@Output()`-only — no `HttpClient`, storage, or facade injection (ADR-006). Verified spot checks (dialog, table, skeleton, empty-state, button) confirm this.
- **Container/page components** (`src/app/pages/**`, `src/app/features/*/containers/**`, `src/app/features/*/pages/**`) own routing and DI of a facade.
- **Facades** live in `src/app/facades/{platform,runtime,website}/` plus feature-local `facade/` folders (e.g. `src/app/features/admin/products/facade/admin-products.facade.ts`). **Inconsistency found**: `ProductFacade` (`src/app/facades/platform/product.facade.ts`) uses constructor DI and is a thin pass-through; `CategoryFacade` (`src/app/facades/platform/category.facade.ts`) uses `inject()` and exposes **both** signals and RxJS observables side by side for the same data (`allCategories`/`allCategories$`, `selectedCategory`/`selectedCategory$`), with the signals populated imperatively inside subscriptions rather than derived via `computed()`/`toSignal()`. Two facades in the same directory follow two different reactive idioms — this is real internal inconsistency, not a style nit, since it means new contributors have no single pattern to copy.
- **Domain services** (`src/app/core/<domain>/*.service.ts`) convert DTOs to domain models via mappers (e.g. `src/app/core/categories/mappers/category.mapper.ts`); DTOs are not supposed to leak past this boundary.
- **Repositories/providers** are swapped via `InjectionToken` factories reading `RuntimeProviderStrategyService` — see Section 5 for the full mechanism and where it is (and is not) actually wired.
### DI style
Mixed: `inject()` appears in ~132 files, constructor DI in ~72 files (grep, file counts not occurrences) — no single convention is enforced project-wide, and both patterns appear within the same architectural layer (see `ProductFacade` vs `CategoryFacade` above).
### Signals usage
`signal(` appears in 53 files, `computed(` in 47 files. Representative: `AuthService` (`src/app/services/auth.service.ts`) exposes private writable signals (`sessionSignal`, `statusSignal`, `showLoginSignal`) via `.asReadonly()` plus `computed()` derivations (`isAuthenticated`, `displayName`) — this is the cleanest, most idiomatic example in the codebase and is mirrored almost exactly by `AdminAuthService` (`src/app/core/admin-auth/admin-auth.service.ts:31-39`, read in full during this audit — same shape, deliberately kept separate per the file's own doc comment explaining admin/customer session isolation).
### Change detection
`ChangeDetectionStrategy.OnPush` is used in 188 of 189 `@Component`-decorated files (grep count) — effectively universal. This is a genuine architectural strength; see Section 10.
### Dynamic page/section/widget rendering (ADR-005)
Pipeline: `page config -> section engine -> section renderer -> widget host -> registered widget component`.
- `SectionEngineService` (`src/app/dynamic-renderer/section-engine/section-engine.service.ts`, 130 lines) builds an ordered render model from `PageConfig.sections`.
- `PageRendererService` (`src/app/dynamic-renderer/page-renderer/page-renderer.service.ts`, **13 lines**) is a thin pass-through to the Section Engine.
- `WidgetHostService` (`src/app/dynamic-renderer/widget-host/widget-host.service.ts`, 77 lines) resolves each widget's component via `WidgetManifestService` (`src/app/widgets/registry/widget-manifest.service.ts`) and its data via `DataSourceResolverService` (`src/app/widgets/resolvers/data-source-resolver.service.ts`, 251 lines), which delegates to `CategoryFacade`/`ProductFacade` — widgets never call APIs directly, confirmed.
- **Critical finding**: this entire pipeline has real service files but **zero components or templates** — every one of `page-renderer/`, `section-renderer/`, `widget-host/` also contains only a `.gitkeep` alongside its service/model files (confirmed by directory listing). The storefront homepage instead renders through a separate, older path (`layouts/containers/dynamic-page-layout.component.ts`) that does not consume this pipeline at all. Two Project Editor fields — Theme section's "Site Layout" (`layout.type`) and each homepage section's own `type` field — write into a schema this unused pipeline was meant to consume, and currently have **no effect on the rendered storefront**. This is the single most consequential hidden gap in the platform: a merchant can configure two settings that persist, validate cleanly, and look fully functional in the editor, but do nothing at render time. See Sections 4 and 6.
### Theme engine (ADR-008)
`ThemeConfig` (`src/app/shared/models/config/theme.model.ts`): `themeId`, `mode` (`light|dark|system`), 12-color `palette`, typography, spacing, `borderRadiusScale`, shadows, `iconSet`. Palette colors are applied as real CSS custom properties and are genuinely live throughout the app. **The `mode` field is not** — see Section 4/6, `theme-engine.service.ts` sets a `data-theme-mode` attribute on `<html>` but no stylesheet anywhere reads it.
### Feature flags (ADR-009)
`bootstrap.featureFlags` (typed) plus the broader `bootstrap.features` (`MarketplaceFeaturesConfig`). Resolution falls back across older config surfaces for backward compatibility as the flag model evolved across sprints.
### Diagnostics (dev-only)
`src/app/features/diagnostics/` (route `/__diagnostics`, excluded from production) validates bootstrap structure and runtime health, scored 0-100 — genuinely useful for investigating an editor-authored bootstrap, and the only place in the codebase that programmatically cross-checks the dynamic-renderer gap described above (it flags unknown widget types and missing datasources).
### Provider-switching mechanism (detail carried into Section 5)
`RuntimeProviderStrategyService` (`src/app/core/providers/runtime-provider-strategy.service.ts`) is the intended single source of truth for mock-vs-API selection, exposing `getBootstrapProviderMode()`, `getBackofficeProviderMode()`, `getProductProviderMode()`, `getCategoryProviderMode()`, each returning `'mock' | 'api' | 'remote-config'` based on `environment.useMockData` (and, for bootstrap only, an additional `useMockBootstrapOnLocal` + localhost check). **`'remote-config'` is declared but never actually returned or consumed anywhere** — a dead enum value. Verified: `environment.ts` currently has `useMockData: false`, `useMockBootstrapOnLocal: true`, so in the checked-out state real API calls are the default everywhere except bootstrap-on-localhost.
Two more DI tokens exist that consult this strategy but whose `'mock'` branch is **dead code**: `PRODUCT_DATA_PROVIDER` (`src/app/core/products/product-data-provider.token.ts:12-19`) and `CATEGORY_REPOSITORY` (`src/app/core/categories/category-repository.token.ts:12-19`) both have a `switch` over the mode where **every case, including `'mock'`, returns the same `Api*` implementation** — no mock `ProductDataProvider`/`CategoryRepository` class exists in the codebase at all. This is not a bug in the sense of breaking anything (mocking for products/categories instead happens one layer down, via `mockDataInterceptor` faking HTTP responses — see Section 5), but it does mean the provider-token pattern documented in `docs/ARCHITECTURE.md` as the swap mechanism is only genuinely wired for **bootstrap** and **backoffice reads** — for storefront product/category data and for every admin domain, "mock" happens by a different mechanism than the architecture doc implies.
### Integrations placeholders
`src/app/integrations/{auth,authorization,payment}/` contain **only `.gitkeep` files** — scaffolded but entirely unused. The real, working auth code lives in `src/app/services/auth.service.ts` and `src/app/core/admin-auth/`; real payment code lives inline in `src/app/pages/cart/cart.component.ts` (see Section 2). These three directories are dead scaffolding that should either be deleted or the real code relocated into them — currently they mislead a reader into thinking auth/payment integration code is organized there.
---
## 2. Marketplace (customer-facing)
Customer routes render under `/:lang/**`. There is **no dedicated Checkout page**`src/app/features/website/checkout/` contains only a `.gitkeep`; checkout is folded entirely into `CartComponent` (664 lines, `src/app/pages/cart/cart.component.ts`, template 359 lines). There is **no dedicated Profile/Account or Orders (customer-facing order history) page** — no route, no component found under `src/app/pages/**` or `src/app/features/website/**` matching that purpose; a logged-in customer has no self-service order-history UI today (only the admin side has order management, and only for staff). There is **no traditional email/password login/register form** — the only auth surface is `TelegramLoginComponent` (`src/app/components/telegram-login/telegram-login.component.ts`), a QR-code Telegram session flow shared (with separate storage) between customer and admin. There is **no dedicated 404 page** (redirects home) and **no maintenance page** (confirmed above).
### Home
- **Files**: `src/app/pages/home/home.component.ts` (42 lines) / `.html` (11 lines) — intentionally thin, delegates to `DynamicPageLayoutComponent` (`layouts/containers/dynamic-page-layout.component.ts`) which composes the widget/section tree from `bootstrap.pages.home`.
- **Status: Ready**, with a known open bug: `docs/KNOWN-ISSUES.md` item 1 — a hero-to-categories dead-space gap on the storefront homepage, traced to bootstrap mock config padding values, not a code defect. Not independently re-verified visually in this audit (no live browser run performed).
- **Widgets rendered**: hero, categories, product-carousel, recently-viewed, footer-navigation (`src/app/widgets/ui/*`) — each receives `{ section config, resolved data }` only, per ADR-005.
- **Missing backend**: none directly — home composes already-covered product/category data.
### Catalog / Search
- **Files**: `CatalogContainerComponent` (`src/app/features/website/catalog/containers/catalog-container.component.ts`, **950 lines** — the single largest component in the entire `src/app` tree) serves both `/catalog`, `/catalog/:id`, and `/search` routes (`app.routes.ts:16-22,43-45`) — one container handling three distinct route paths and URL-param shapes.
- **Status: Needs work.** A 950-line container is a strong maintainability red flag on its own (nearly 2.5x the project's stated 400-line convention ceiling referenced elsewhere in `docs/EDITOR.md`); it composes filters-panel, product-grid, category-grid, sorting-control, layout-switcher, search-box, search-results, and catalog-empty-state (`src/app/features/website/catalog/components/*`) — a reasonable component breakdown one level down, but the container itself is a single point of complexity for three different page contracts (catalog listing, category filtering, free-text search).
- `category/:id` and `category/:id/items` both `redirectTo: 'catalog/:id'` (`app.routes.ts:23-32`) — legacy URL compatibility, working as intended, not dead code.
- **Search facade**: `src/app/features/search/facade/search.facade.ts` (592 lines) — second-largest facade in the codebase; handles suggestions, history (via `LocalSearchHistoryRepository`, `localStorage`-backed), and a `BackendSearchHistoryRepository` whose every method is a stubbed no-op placeholder (confirmed by the architecture research pass; "Placeholder for future backend endpoint").
- **Missing backend**: storefront product/category reads go through `PRODUCT_DATA_PROVIDER`/`CATEGORY_REPOSITORY`, which — per Section 1 — always resolve to the real `ApiProductDataProvider`/`ApiCategoryRepository` (real `HttpClient` calls); "mock mode" for these is achieved instead by `mockDataInterceptor` (`src/app/interceptors/mock-data.interceptor.ts`, 923 lines of hand-authored mock category/product JSON) intercepting the HTTP call before it reaches the network.
### Product detail
- **Files**: `ProductDetailsContainerComponent` (`src/app/features/website/product/containers/product-details-container.component.ts`, 683 lines) plus a well-decomposed set of presentational children (`product-gallery`, `product-information`, `product-actions`, `product-description`, `product-specifications`, `product-warranty`, `variant-selector`, `related-products`, `delivery-information`) and an `engagement/` subtree for reviews/questions (`review-list`, `review-form`, `review-card`, `question-list`, `question-form`, `question-card`, `rating-summary`, `star-selector`, `stars`).
- A legacy duplicate exists: `src/app/pages/item-detail/item-detail.component.ts` (368 lines, template 356 lines) — appears to predate the `features/website/product` rebuild. `app.routes.ts` routes `/product/:id` to `ProductDetailsContainerComponent` (line 34-36) and redirects `/item/:id → /product/:id` (line 38-41), so `ItemDetailComponent` is **not reachable via any route** — likely-unused, not exhaustively verified (no other route or component reference to it was found in this pass, but a full cross-repo import search was not run for this specific file).
- **Status: Ready** for the primary container; reviews/Q&A engagement components are functionally present and wired to `ApiService` (`submitReview`, `submitQuestion`).
- **XSS surface**: `item-detail.component.html:208` renders `[innerHTML]="getSafeHtml(item()!.description)"` via `DomSanitizer.bypassSecurityTrustHtml` — since this component is unrouted this is currently inert, but if ever re-routed it inherits whatever sanitization `getSafeHtml()` performs (not independently verified for correctness).
### Cart / Checkout (merged)
- **Files**: `src/app/pages/cart/cart.component.ts` (664 lines) / `.html` (359 lines).
- **Status: Ready but does double duty as checkout** — no separate checkout step/route exists. Payment is embedded directly: `bankPaymentFrameUrl = this.sanitizer.bypassSecurityTrustResourceUrl(bankUrl)` (`cart.component.ts:371`) renders a bank-hosted payment iframe inline in the cart page — a real, working integration (`ApiService.createCartPayment`/`checkCartPaymentStatus`), not a placeholder, but architecturally unusual to have zero dedicated checkout flow (shipping address, order review, confirmation) separate from the cart itself.
- **Missing backend**: none — cart/payment is one of the few genuinely backend-wired storefront flows (`ApiService`, real `HttpClient`, per Section 5).
### Wishlist / Compare
- **Files**: `WishlistPageComponent` (`src/app/features/website/user-experience/wishlist/containers/wishlist-page.component.ts`), `ComparePageComponent` (`.../compare/containers/compare-page.component.ts`) plus `compare-table.component.ts`.
- **Status: Ready**, but **entirely local-only**`USER_EXPERIENCE_REPOSITORY` unconditionally binds `LocalUserExperienceRepository` (pure `localStorage`), with no API alternative and no provider-switching at all (per the architecture research pass). Wishlist/compare state does not sync across devices or survive a cleared browser.
- Gated by a documented double-flag bug (fixed per `docs/KNOWN-ISSUES.md`/`docs/EDITOR.md`): visibility used to be driven by only one of two runtime flags (`featureFlags.<key>` and `userExperience.<key>.enabled`); the Project Editor's Features section now toggles both together.
### Static Pages (customer-rendered)
- **Files**: `StaticPageComponent` (`src/app/pages/static-page/static-page.component.ts`) resolves `bootstrap.staticPages` dynamically for both `page/:key` and the catch-all `:staticPath` route.
- **Status: Ready**, sanitizes rendered HTML via `DomSanitizer.bypassSecurityTrustHtml` after passing through a `sanitized` step (`static-page.component.ts:103` — the sanitization call itself was not traced to its implementation in this pass; flagged for Section 11).
- **Dead parallel system**: `src/app/pages/info/**` (about/contacts/delivery/faq/guarantee, each with `en/`/`ru/`/`hy/` sub-variants) and `src/app/pages/legal/**` (company-details/payment-terms/privacy-policy/public-offer/return-policy, same per-locale structure) are ~40 files of hardcoded, per-locale-duplicated legal/info content that predates the dynamic Static Pages builder and are **confirmed unrouted** (`cmsContentRoutes = []`, `app.routes.ts:289`). `public-offer-{en,ru,hy}.component.html` alone are 529 lines each (1,587 lines of duplicated legal boilerplate across 3 files for one page). This is the single largest concentrated block of dead code in the repository — see Section 6.
### Authentication
- **Files**: `TelegramLoginComponent` (`src/app/components/telegram-login/`), `AuthService` (`src/app/services/auth.service.ts`).
- **Status: Ready** as a QR-login flow, but is the *only* login method — no password/email fallback exists for customers without Telegram.
### 404 / Maintenance
- **Status: Missing.** No dedicated 404 component (silently redirects home); no maintenance-mode component or route exists at all. Both are real gaps for a production e-commerce platform (a merchant cannot show a "we'll be back" page during a deploy/migration, and a broken link gives no feedback).
### Cross-cutting UX/accessibility/performance for Marketplace
- `<img>` lazy-loading: only 17 of 35 template `<img>` occurrences across `src/app` use `loading="lazy"` or Angular's `ngSrc` (`NgOptimizedImage`) — roughly half. Not broken down per-page in this pass; a per-page audit would need to check `product-gallery`, `product-card`, `category-grid`, and widget templates individually (not exhaustively done here).
- Change detection: `OnPush` is used almost universally (188/189 components) including in the marketplace surface — a genuine performance strength (Section 10).
- Accessibility: per `docs/ADMIN.md`'s Sprint 28 note, every `<img>` in `src/app/**` was checked for a missing `alt` attribute and none were found — this claim covers the whole app including marketplace pages and was not re-run independently in this pass, but the methodology described (grep for `<img` without `alt`/`[alt]`/`[attr.alt]`) is sound and reproducible.
---
## 3. Admin (backoffice)
All admin routes live under `/:lang/backoffice/**`, guarded by `adminAuthGuard` (`src/app/core/admin-auth/admin-auth.guard.ts` — 15 lines, straightforward `isAuthenticated()` check, `requestLogin()` on failure). Admin auth is fully isolated from customer auth: separate cookie name (`adminSessionID` vs the customer session cookie), separate `localStorage` keys (`adminToken`/`adminRefreshToken`), separate service class (`AdminAuthService` vs `AuthService`), separate signals — confirmed by reading both files in full. **Backend gap, confirmed real and unresolved**: both admin and customer login hit the identical Telegram session endpoint (`TelegramSessionApiService`, `{authApiUrl}/users/sessions`) — there is no server-side concept of "this session is an admin session," so authorization is enforced nowhere but the frontend guard today. This is flagged repeatedly and consistently across `docs/ADMIN.md`, `docs/EDITOR.md`, and `docs/backend/BACKEND-INTEGRATION.md` — a genuine, still-open security gap (see Section 11).
Every admin domain follows the same container/facade/local-gateway split (`features/admin/<domain>/{models,services,facade,pages,components}`), and — per the architecture research pass — **every one of the 8 admin gateway implementations is local/in-memory, none use `HttpClient`**:
| Domain | Gateway | Data reality |
|---|---|---|
| Dashboard | `admin-dashboard-metrics.local.gateway.ts` | Derived counts from `BackofficeDataService` (itself mock/API-switchable) |
| Products | `admin-products-local.gateway.ts` (197 lines) | Seeded from `BackofficeDataService.loadProducts()`; all writes in-memory only |
| Categories | `admin-categories-local.gateway.ts` (120 lines) | Seeded from `BackofficeDataService.loadCategories()`; all writes in-memory only |
| Orders | `admin-orders-local.gateway.ts` | **24 fully synthetic orders**, no backend derivation at all |
| Transactions | `admin-transactions-local.gateway.ts` (91 lines) | Derived from the 24 synthetic orders (mock-on-mock) |
| Users/roles | `admin-users-local.gateway.ts` | **Hardcoded 4-user roster**, fabricated |
| Moderation | `admin-moderation-local.gateway.ts` (189 lines) | 32 synthetic reviews + 10 reports from canned arrays + real product names |
| Monitoring | `admin-monitoring-local.gateway.ts` (82 lines) | **40 fully fabricated events**, hardcoded queues/webhooks |
Analytics has no dedicated gateway file — it aggregates real arithmetic (revenue/orders/AOV/sales-over-time) over the mock Orders/Products/Categories data (confirmed in `docs/ADMIN.md` Sprint 27, plausible given Orders/Products data shapes, not independently re-derived in this pass). Visitors/funnels/heatmaps render an explicit `pending-backend` badge rather than fabricated numbers.
### Dashboard
- **File**: `admin-dashboard-page.component.*` + `AdminDashboardFacade` (201 lines).
- **Implemented ~90%.** Cards render one of `loading`/`empty`/`error`/`pending-backend`/ready states — a genuinely well-designed state model (`AdminDashboardCardComponent`), and Orders/Revenue cards correctly show `pending-backend` rather than fake zeros, since no order/revenue data model existed at the time (now partially superseded by Sprint 23's Orders gateway, but the dashboard cards' own honesty pattern is worth noting as a good practice other sections don't all follow as visibly).
- **System Health** reuses `ProjectValidator` (6 real checks: bootstrap valid, configuration valid, missing translations, invalid colors, invalid widget references, invalid layouts) — genuinely computed, not fabricated.
- **Recent Activity** is `localStorage`-only per tenant (`AdminDashboardHistoryService`) — will not show another admin's activity until a real audit-log endpoint exists.
- **Known bug class**: none open specific to dashboard beyond the general admin i18n/backend gaps.
### Products
- **Files**: `admin-products.facade.ts` (316 lines), `admin-product-form.component.ts` (243 lines / `.html` 291 lines), `product-variants-editor.component.ts` (169 lines).
- **Implemented ~85%.** Archive/restore (soft delete distinct from `visible`), barcode, lightweight variants (`name|price|quantity` parsed from delimited text, not a full options-matrix), related-products checkbox picker (scoped to whatever page is currently loaded in the facade — not a full catalog search, explicitly documented trade-off), gallery via shared `MediaPickerComponent`, infinite-scroll toggle.
- Recent commits (`3b955b1 feat(admin): Shopify-style variant attributes matching production data shape`, `afaf79d feat(admin): visual badge manager`, `31c64e9 feat(admin): collapse product translations behind default-language fields`, `b16e300 feat(admin): product editor auto-slug, SKU helper, one-click SEO fill`) indicate active, recent UX investment beyond what `docs/ADMIN.md`'s Sprint log describes — these post-date the doc and were not individually re-verified line-by-line in this pass beyond confirming the files exist and are wired into the routed component tree.
- **No `canDeactivate` dirty guard** on the product editor route (confirmed absence in `app.routes.ts:88-105`) despite categories having one — an inconsistency (see Section 1).
- **Known bugs (fixed, historical)**: none open for products specifically per `docs/KNOWN-ISSUES.md`.
### Categories
- **Files**: `admin-categories.facade.ts` (**514 lines** — largest admin facade), `admin-categories-list.component.ts` (174 lines).
- **Implemented ~90%.** Hierarchy via `parentId` with flattened indented-tree rendering; native HTML5 drag-and-drop reorder (not CDK, unlike the Project Editor's footer/homepage builders — an inconsistency in DnD implementation choice across the codebase); soft delete blocked client-side if the category has children or `itemsCount > 0`; draft/publish workflow with `localStorage` autosave recovery; `canDeactivate` dirty guard present.
- **Two real bugs found and fixed** during a documented 2026-07-17 bug-hunt (`docs/ADMIN.md` "Bug-hunt audit pass"), both verified plausible from the current code structure: (1) create-mode draft recovery was permanently dead because the draft key was derived from `Date.now()` on every call, orphaning `localStorage` entries forever — fixed with a stable `admin-category-draft:new` key; (2) drag-and-drop reorder wrote the dropped-on row's `order` value directly onto the dragged item instead of computing a full resequence, so on fresh seed data (every category starting at `order: 0`) **every drag silently no-op'd** — fixed by reordering via target `id` and resequencing all affected siblings. Both fixes were not re-derived from scratch in this pass but the described defect mechanics are consistent with drag-and-drop-by-order-value being a known anti-pattern.
### Orders
- **Files**: `admin-orders.facade.ts` (213 lines).
- **Implemented ~75% relative to a real order system, ~95% relative to its explicitly-scoped mock ambition.** List (search/status filter/pagination/CSV export), detail (customer/payment/shipping, itemized total, status timeline, change-status, refund-request + cancel both `window.confirm`-gated, dual customer/internal notes, print-invoice via `window.print()`). **No real backend or data model for orders exists anywhere in the repository** — this is explicitly and repeatedly documented, not a hidden gap.
### Customers
- **Files**: `src/app/features/admin/customers/` (facade, models, pages) — routed at `/backoffice/customers` and `/backoffice/customers/:email`.
- Not covered in depth by `docs/ADMIN.md`'s sprint log (the doc's sprint numbering jumps from Products/Categories to Media/Orders/Transactions/Users/Monitoring/Analytics without a distinct "Customers" sprint entry) — **implementation depth not independently verified in this pass beyond confirming the routed pages exist and follow the same facade/gateway convention as every other admin domain.** Flagged as "not exhaustively verified."
### Transactions
- **Files**: `admin-transactions-local.gateway.ts` (91 lines), facade and list/detail pages.
- **Implemented ~85%** for its scope: search/status/type filters, CSV export, retry-failed-transaction flow, fraud-flag toggle, per-transaction audit-log dialog. Explicitly derived from the same 24 synthetic orders as the Orders domain (kept consistent by design, not by accident).
### Media (Media Library)
- **Files**: `src/app/features/backoffice/media/media-library-page.component.ts`, `src/app/core/media/mock-media-repository.service.ts` (277 lines), `src/app/shared/media/media-picker/`.
- **Implemented ~90% of its own scope.** Real, working: 10MB size + MIME allow-list validation with surfaced error messages, SVG `<script>`/`on*` attribute stripping before storage, raster image downscale-to-2000px + re-encode via `<canvas>` before storage, flat folder tagging (no true hierarchy), tag editing via `window.prompt`.
- **Storage is IndexedDB in the browser, not a real backend** — `MediaRepository` is hardwired to `MockMediaRepository` in `app.config.ts:29` with **no API implementation and no provider-switching at all** (unlike bootstrap/backoffice, this one isn't even token-gated). Uploaded media does not exist anywhere except the uploading admin's own browser.
### Moderation (Reviews & Reports)
- **Files**: `admin-moderation.facade.ts` (218 lines), `admin-moderation-local.gateway.ts` (189 lines), pages: `admin-reviews-list-page.component.ts`, `admin-review-detail-page.component.ts`, `admin-reports-list-page.component.ts`.
- This is the "Reviews" area named in the audit brief — the codebase calls it "Moderation," routed at `/backoffice/moderation` and `/backoffice/moderation/reports`. **Implemented ~80%** of its scope: 32 synthetic reviews + 10 reports, category filter + search. **No real backend** — reviews submitted by real customers via `ApiService.submitReview()` on the storefront do not appear to flow into this admin moderation queue in any code path found in this pass (not exhaustively verified — would require tracing whether `BackofficeDataService`/moderation gateway ever reads real submitted-review data; the seed data is explicitly described as "canned author/snippet arrays," which suggests it does not).
### Monitoring
- **Files**: `admin-monitoring-local.gateway.ts` (82 lines), `admin-monitoring-page.component.ts`.
- **Implemented ~70%** of its scope, and honest about it: one real section (Health, reusing the Dashboard's genuine `ProjectValidator` checks) alongside three fully fabricated sections (unified audit/security/API/error event feed — 40 synthetic entries; queue monitoring — 3 mock named queues; webhook delivery log — mock). No logging backend exists anywhere in the system, so there is nothing real to read from for the fabricated sections — a structural gap, not an oversight.
### Analytics
- **Files**: `admin-analytics.facade.ts` (338 lines), `admin-analytics-page.component.html` (246 lines).
- **Implemented ~65%.** Revenue/orders/AOV/sales-over-time/top-products are real aggregation arithmetic over the mock Orders/Products/Categories data (not fabricated numbers, but ultimately traceable to fabricated source data). Visitors/funnels/heatmaps correctly show `pending-backend` since no analytics/tracking pipeline exists anywhere. Chart is a plain `<div>`-bar chart via `[style.height.%]`, no charting library — adequate for one series, would need revisiting for more chart types.
### Users & Roles
- **Files**: `admin-users-local.gateway.ts`.
- **Implemented ~70%.** 4 built-in roles with a flat permission-string list (not a real permission catalog, no custom-role creation — intentionally scoped down). Invitations create a local record only; **no email actually sends**. Session/device manager is mocked (2 fabricated sessions per user on first view) since the real auth flow only ever tracks the current browser's session — there is no multi-device session backend.
### Reports / Settings / Diagnostics
- **Reports**: folded into Moderation (`/backoffice/moderation/reports`) — there is no separate "Reports" admin area distinct from the reports-queue-of-flagged-content concept; if "Reports" in the brief means business reporting, that function is covered (partially) by Analytics instead. Flagging this naming mismatch explicitly rather than guessing which the brief intended.
- **Settings**: there is no single "Admin Settings" page — tenant-level settings (branding, theme, domain, etc.) live in the **Marketplace Builder / Project Editor** (`/edit/*`), not in `/backoffice/*`. This is a genuine UX seam: an admin user looking for "Settings" inside the admin shell (`AdminLayoutComponent`) will not find it there; it's a separate top-level area (`/edit`) with its own navigation, reachable only via a Quick Action link or by typing the URL.
- **Diagnostics**: `src/app/features/diagnostics/` (route `/__diagnostics`) is dev-only and correctly excluded from production builds (`app.routes.ts:293-296`) and from crawling (`public/robots.txt` blocks it per Sprint 28). Not part of the admin shell/nav at all — a developer tool, not an admin-facing feature, correctly scoped.
### Admin-wide notes
- **Design consistency**: as of Sprint 28 (`docs/ADMIN.md`), all 8 built admin sections share `app-skeleton` (loading) and `app-empty-state` (empty) primitives — verified as a real, systematic pass rather than piecemeal, since the doc names each section's prior gap explicitly (which sections had a `loading` signal but no UI, vs. which had empty-state but no skeleton).
- **i18n**: the ~178 raw untranslated `adminXxx.*` keys documented as an open issue in `docs/KNOWN-ISSUES.md` are **fixed** — confirmed by grepping `src/app/i18n/{en,ru,hy,translations}.ts` for an `adminProducts:` object, present in all three locale files plus the `Translations` interface. This is a stale doc claim that no longer reproduces; `docs/KNOWN-ISSUES.md` should be updated (not done here — read-only audit).
- **Bundle**: the initial-bundle budget warning (~198kB over the 700kB `angular.json` budget, see Section 10) predates all admin work and is unaffected by it since every admin page is lazy-loaded.
---
## 4. Marketplace Builder
**Important correction to the audit brief's assumed file layout**: `src/app/features/builder/{feature-flag-editor,navigation-editor,page-editor,seo-editor,theme-editor}/` — the five sub-folders matching the brief's list — **contain only `.gitkeep` files each**. That is stale/unused scaffolding, not the real builder. The actual, fully-built Marketplace Builder lives at `src/app/features/project-editor/` (11 section components under `sections/`, one facade, a schema/validator layer under `schema/`) plus `src/app/features/content-management/` for Static Pages specifically. All findings below are against the real, routed implementation (`/edit/:section`).
### Mechanism notes (apply to every section)
- **Drag-and-drop**: `@angular/cdk/drag-drop` (`CdkDragDrop`, `moveItemInArray`), used for homepage section reordering and footer column/link reordering. Navigation link reordering and Static Pages list reordering are **up/down buttons, not drag-and-drop** — Static Pages' choice is an explicit, documented lower-complexity scope call; Navigation's is not explained anywhere, an inconsistency worth flagging (why does Homepage/Footer get CDK drag-drop but Navigation doesn't, when both are ordered lists of similar complexity).
- **Live preview is not an iframe/postMessage sandbox.** `ProjectEditorPreviewService.preview()` calls `PlatformRuntimeService.reloadFromBootstrap()` — the *same* mechanism Publish uses — then navigates the current tab to `/{lang}`. Preview and Publish are functionally the same client-side reload; there is no separate sandboxed preview surface. A merchant could reasonably believe "Preview" is non-destructive when it mutates live runtime state identically to Publish (it just doesn't flip `status` to `published` or write the "last published" marker).
- **No backend persistence for any of it.** `ApiBootstrapProvider` implements only `GET /bootstrap` — there is no PUT/POST anywhere in the provider layer. `save()` and `publish()` both write only to `localStorage` (`projectEditor.draftBootstrap.v1`, tenant-scoped) and then trigger the same in-app reload. Every "save" in the entire Builder only persists to the browser; clearing site data loses all edits.
### General
`general-section.component.ts` (74 lines). Name/domain (auto-derives `https://` prefix silently, with no visible feedback of the derived URL)/description — all wired. Language management was recently de-duplicated (commit `440d2ec`): previously a second, independent comma-separated text field existed here alongside the real Languages section; now it's a read-only chip summary linking to Languages — a genuine, verified UX fix eliminating a two-sources-of-truth bug.
### Branding
`branding-section.component.ts` (69 lines). Logo/compact-logo/favicon/social(OG)-image/gallery, all via `app-image-field` (thumbnail preview + replace/remove, opens `app-media-picker`). A real fixed bug: `branding.socialImageUrl` used to be saved but never actually read by `SeoService.resetToDefaults()` — now fixed. **Missing explanations**: no guidance on recommended logo dimensions/aspect ratio or favicon format constraints anywhere in the template.
### Theme
`theme-section.component.ts` (91 lines). Palette (8 colors via `app-color-picker`) is genuinely live, applied as real CSS custom properties. **Two confirmed-dead controls sit in the same section as the fully-working palette**, which is a real trust problem for a merchant since nothing in the UI distinguishes a live control from a decorative one:
1. **Theme Mode** (light/dark/system) saves and sets `data-theme-mode` on `<html>` but no stylesheet anywhere reads that attribute — picking Dark or System changes nothing visually.
2. **Site Layout** (`layout.type`) feeds the unwired `dynamic-renderer` pipeline described in Section 1 — has no effect on the rendered storefront.
### Header
`header-section.component.ts` (72 lines). 9 toggles + layout select + sticky, all live-wired except **`showProfile`**, a real toggle with no corresponding profile/account menu anywhere in `header.component.html` — flips a flag nothing reads (blocked on an auth-system dependency that doesn't exist for account menus today).
### Footer
`footer-section.component.ts` (198 lines) — recently rebuilt (commit `726df0c feat(builder): visual footer builder with drag-and-drop columns`). CDK drag-and-drop columns and links; per-link source toggle between a static-page reference (resolved by stable page id, survives route renames) or a custom URL; payment icons and social links via `app-key-value-editor`; multiple phones/emails. `FooterResolverService` was updated in the same pass to read `footer.columns` as primary source with fallback to legacy auto-grouping — this is genuinely wired end-to-end to the live storefront footer, not builder-only cosmetics. Two id-collision bugs (social-link ids derived from array length, payment-icon ids from `src`) that corrupted `@for (track item.id)` DOM identity on the live storefront footer were found and fixed in the same pass.
### Homepage
`homepage-section.component.ts` (167 lines) — recently rebuilt (commit `71d5f4d feat(builder): visual homepage blocks, merchant-language widget settings, real carousel arrows`). Merchant-facing block catalog (icon+name+description replacing raw `section.id` strings like `section-hero`); add-block picker across 8 block types; duplicate/remove per block; CDK drag-to-reorder. A companion `homepage-overview.component.ts` (67 lines) computes a completion-ring UI purely from real widget presence data (hero/categories/products/promotion/newsletter/any-sections) — not fabricated. **Dead code retained**: `updateSection(sectionId, 'type', ...)` still exists in the component with no UI calling it, because the field it writes feeds the unwired `dynamic-renderer` pipeline.
### Widgets
`widgets-section.component.ts` (215 lines). Typed editors for hero (title/subtitle/slide array) and toggle/reorder/duplicate/remove for all widgets; JSON-fallback textarea for anything without a dedicated editor. A real, non-obvious fix: the JSON fallback used to silently discard an in-progress invalid edit (parse error caught, then overwritten by the last-committed value on the next change-detection tick); now keeps the user's draft on screen with an inline error until valid. Hero widget's slide array drives a real rotator with dots, click-to-jump, and a 5-second autoplay interval — verified in `hero-widget.component.ts`, not a cosmetic stub.
### Static Pages
`static-pages-editor.component.ts` (**404 lines**, right at the project's 400-line convention ceiling — flagged as a split candidate, bulk-actions and validation-badge logic are natural extraction points), `content-page.service.ts` (285 lines). Full CRUD, multi-field search, status/locale filters, bulk actions (delete/enable/disable/publish/unpublish), duplicate, per-locale device preview, SEO fields with validation-driven badges. Two slug/route id-collision bugs (create-delete-create, duplicate-the-same-page-twice) were found and fixed with a shared `uniqueValue()` helper, confirmed used consistently in both `createPage()` and `duplicatePage()`.
- **`persist()` footgun**: every mutation must operate on the full unfiltered page list, never the search/filter-narrowed view (writing from a filtered view would silently delete whatever the filter was hiding) — correctly followed everywhere checked, but is an unenforced convention (no type system or lint rule prevents a future contributor from getting this wrong), a real maintainability risk.
- **Rich-text editor** (`MarketplaceHtmlEditorComponent`) is built on the deprecated `document.execCommand` API — works in all current browsers, no modern drop-in replacement exists; a real, acknowledged rewrite risk if browsers ever drop the API. Emits raw unsanitized HTML by design (sanitization is a render-time concern via `DomSanitizer`, not an authoring-time one) — recently redesigned toolbar (commit `9eacd00`) grouped by intent with PrimeIcons/tooltips/aria-labels.
- **Confusing**: the `enabled`/`status` gating story (a page can be individually unpublished independent of the whole-bootstrap publish state) is correctly documented in `docs/StaticPages.md` but not surfaced as an explicit inline warning anywhere in the editor UI itself — a merchant could publish the whole config and be confused why one page still isn't live.
### Navigation
`navigation-section.component.ts` (129 lines). Header nav add/remove/reorder(up-down)/per-locale label/URL/visibility; "Insert page link" creates a stable `type: 'staticPage'` nav item. **Confusing**: flat footer nav is editable here, but grouped (column-based) footer nav is read-only in this tab — must switch to the Footer tab to edit it, with no cross-link between the two in the template.
### Languages
`languages-section.component.ts` (69 lines). Add/remove/set-default correctly routes through `LocaleSyncService`, which seeds empty translation entries across static pages, header nav, footer nav (both shapes), and sidebar nav — verified thorough by reading the full sync implementation. Cannot remove the current default locale — enforced at the service layer, not just UI-disabled (real defense in depth). A silent-no-op bug (adding an already-supported locale cleared the input with no feedback) is fixed with an inline error. Recently de-duplicated against General (commit `440d2ec`) — single source of truth now.
### Features
`features-section.component.ts` (97 lines). Catalog navigation mode select, general flags, wishlist/compare/recently-viewed/search-suggestions-history/reviews/questions toggles. The double-flag wishlist/compare bug (Section 1/6) is fixed here — one toggle now correctly drives both `featureFlags.<key>` and `userExperience.<key>.enabled`.
### Preview
`preview-section.component.ts` (37 lines). Export/import JSON, a "changes since last publish" card (validation issues + before/after diff table). A real fixed bug: import used to bypass `updateBootstrap()` entirely, so an imported config never reached `localStorage` draft storage (lost on refresh before an explicit Save) and wasn't undo-able — now routed through the standard update pipeline.
### Cross-cutting Builder findings
- **`project-editor.facade.ts` is 686 lines** — by far the largest file in the entire builder, owning bootstrap state, a debounced (~300ms) undo/redo history capped at 50 entries, draft storage, validation aggregation, section-readiness computation, and navigation-link mutation. It works today (traced in full by the architecture pass) but is the single highest-blast-radius file in the Builder — a change here risks every section simultaneously.
- **Field-schema/validator layer** (`schema/`) is metadata-augmented, not schema-driven — section templates stay hand-authored, a schema registry sits alongside for labels/validators. `ProjectValidator` covers ~17 distinct checks (duplicate slugs/routes, invalid colors/CSS/URLs/emails, missing translations, malformed widget config, etc.), gates Publish only on `error`-severity issues, not `warning`s — a sound, deliberate design.
- **Undo/redo** is a real, pure reducer (`schema/history.util.ts`) wired with keyboard shortcuts (skipped while a text field has focus so native per-field undo still works) — genuinely implemented, not stubbed.
- **`items-carousel` (PrimeNG `p-carousel`, `src/app/components/items-carousel/`) is confirmed dead code** — not wired into any route or widget; commit `71d5f4d`'s own investigation (per the builder research pass) found the real carousel functionality landed in `ProductCarouselWidgetComponent` instead. This is a shared/top-level component directory (`src/app/components/`), so its unused status is a genuine cleanup candidate, not feature-folder clutter.
---
## 5. Backend readiness
### Provider-switching mechanism
`RuntimeProviderStrategyService` (`src/app/core/providers/runtime-provider-strategy.service.ts`) reads `environment.useMockData` (and, for bootstrap only, `useMockBootstrapOnLocal` + a localhost check) and returns `'mock' | 'api' | 'remote-config'`. This feeds `InjectionToken` factories that pick between a Mock/Api pair — but as established in Section 1, this mechanism is **only genuinely two-sided for Bootstrap and Backoffice reads**. For Products/Categories the "mock" branch is dead code (always resolves to the real API class); mocking there happens instead via `mockDataInterceptor` (923 lines) faking HTTP responses at the interceptor layer — a second, parallel mocking mechanism that a reader of `docs/ARCHITECTURE.md` alone would not know exists.
### Current state, by domain (see Section 3's table for the admin-domain detail)
| Domain | Status | Evidence |
|---|---|---|
| Bootstrap config | **Real HTTP + working mock**, switchable | `ApiBootstrapProvider``GET /bootstrap`; `MockBootstrapProvider``GET /assets/mock/bootstrap/bootstrap.json` (still an HTTP call, to a local JSON asset) |
| Backoffice products/categories (read) | **Real HTTP + working mock**, switchable | `ApiBackofficeDataProvider``GET /api/backoffice/{products,categories}`; `MockBackofficeDataProvider` → local JSON assets |
| Storefront products/categories | **Real HTTP only** (mock branch dead code; interceptor-level mocking exists separately) | `ApiProductDataProvider`, `ApiCategoryRepository`, both real `HttpClient` with retry+backoff |
| Cart/reviews/questions/payment/search | **Real HTTP**, no mock provider variant at all | `ApiService` (674 lines) — every method issues real `HttpClient` calls; extensive DTO normalization for backend-format drift (legacy vs "backOffice" shapes) |
| Wishlist/compare/recently-viewed/saved-searches | **Local-only, no API path exists, no switching** | `LocalUserExperienceRepository`, pure `localStorage` |
| Search history | **Local-only**; a `BackendSearchHistoryRepository` class exists but every method is a stubbed no-op | Not confirmed bound anywhere via DI |
| Media library | **Local-only (IndexedDB), no API class exists, hardwired in `app.config.ts`, not token-switched** | `MockMediaRepository` |
| Admin: Products/Categories | **Local/in-memory**, seeded from Backoffice data (itself switchable) but writes never persist past reload | `admin-{products,categories}-local.gateway.ts` |
| Admin: Orders/Transactions | **Fully synthetic**, no backend model exists anywhere | `admin-orders-local.gateway.ts` (24 seeded orders), `admin-transactions-local.gateway.ts` (derived) |
| Admin: Users/roles | **Fully fabricated**, hardcoded 4-user roster | `admin-users-local.gateway.ts` |
| Admin: Moderation/Monitoring | **Fully fabricated** (Moderation partially uses real product names) | `admin-moderation-local.gateway.ts`, `admin-monitoring-local.gateway.ts` |
| Admin: Analytics | **Real arithmetic over fabricated source data**; visitors/funnels/heatmaps explicitly `pending-backend` | No dedicated gateway file found |
| Admin: Dashboard metrics | **Derived from Backoffice data**, honest `pending-backend` state for Orders/Revenue cards | `admin-dashboard-metrics.local.gateway.ts` |
### What changes when backend integration happens
Per the pattern already used for bootstrap/backoffice (and independently documented, matching the code, in `docs/backend/BACKEND-INTEGRATION.md` §14): implement a new `*ApiGateway`/`*ApiRepository` class against the same interface each facade already depends on, then rebind the DI token (or, for the several admin domains that inject the concrete `*LocalGateway` class directly rather than through an `InjectionToken` — confirmed no `admin-{orders,transactions,users,moderation,monitoring}-gateway.token.ts` files exist — introduce a token first, or swap the binding in each facade's constructor/`inject()` call). No facade or component changes are required for domains that already sit behind an interface — this is the real, verified architectural payoff of the container/facade/gateway split, and it is consistently followed across all 8 admin domains plus the storefront.
### Estimated remaining work
Roughly **9 of the ~11 backoffice/admin domains are 100% frontend-mocked** with zero backend contract implemented (Orders, Transactions, Users/roles/invitations/sessions, Moderation, Monitoring, Analytics-beyond-arithmetic, Media, Wishlist/Compare-if-cross-device-sync-is-required). Each needs: (1) a real REST/GraphQL endpoint built server-side, (2) a new `*ApiGateway` class client-side, (3) a token rebind or facade constructor change. The storefront (product browsing, cart, auth, payment) is the only surface with a genuinely live backend contract today — and even there, whether the configured tenant base URL (`environment.ts`) is a *reachable* backend in any given deployment was not verified in this pass (no network calls were made). Not independently estimated in person-days/weeks — that would require backend-team input on endpoint complexity, which is out of scope for a frontend-only audit.
---
## 6. Technical debt
### Dead code (verified via route/import cross-check, not just filename inspection)
- **`src/app/pages/info/**` and `src/app/pages/legal/**`** (~40 files: about/contacts/delivery/faq/guarantee × {default, en, ru, hy} + company-details/payment-terms/privacy-policy/public-offer/return-policy × {default, en, ru, hy}) — **confirmed unrouted**, `app.routes.ts:289` (`cmsContentRoutes: Routes = []`) with an explicit `TODO(CMS)` comment naming every one of these pages as disabled. `public-offer-{en,ru,hy}.component.html` are 529 lines each; `privacy-policy-*` are 367 lines each. This is the largest concentrated dead-code block in the repository by line count.
- **`src/app/features/builder/{feature-flag-editor,navigation-editor,page-editor,seo-editor,theme-editor}/`** — every folder contains only a `.gitkeep`. Stale scaffolding for a builder structure that was superseded by `features/project-editor/`.
- **`src/app/integrations/{auth,authorization,payment}/`** — `.gitkeep` only, unused scaffolding; real code lives elsewhere (Section 1).
- **`src/app/dynamic-renderer/{page-renderer,section-renderer,widget-host}/`** — real service/model files exist, but zero components/templates; the pipeline is unconsumed by the actual storefront render path (Section 1). Not literally empty like the above, but functionally dead — a decision is needed to finish wiring it or delete it.
- **`src/app/pages/item-detail/item-detail.component.ts`** (368 lines) — likely superseded by `ProductDetailsContainerComponent`; `/item/:id` redirects to `/product/:id` (`app.routes.ts:38-41`), so this component has no route pointing to it. Likely-unused, not exhaustively verified (a full workspace-wide import search for `ItemDetailComponent` was not run).
- **`src/app/components/items-carousel/`** (PrimeNG `p-carousel` wrapper) — confirmed unused per the builder-pass investigation; superseded by `ProductCarouselWidgetComponent`.
- **`BackendSearchHistoryRepository`** (`features/search/services/search-history.repository.ts`) — every method is a no-op placeholder; not confirmed bound to any DI token, i.e. possibly entirely unreachable code today (not exhaustively verified — a full DI-graph trace was not performed).
### TODO/FIXME/HACK markers
`grep -rE "TODO|FIXME|HACK" --include="*.ts" src/app` returns essentially none outside the single `TODO(CMS)` comment in `app.routes.ts:286` — consistent with `docs/SPRINT-PLAN.md`'s Sprint 29 note that a dead-code/TODO grep across `features/admin/**` came back clean. This is a genuinely low-TODO codebase relative to its size, which is a positive signal (either debt is tracked elsewhere — `docs/KNOWN-ISSUES.md` — rather than left as inline comments, or it's genuinely been kept clean).
### `console.log`/`console.debug`
Zero occurrences found (`grep -rE "console\.(log|debug)" --include="*.ts" src/app` → 0). Clean.
### `@deprecated`
One file found using the marker (not individually inspected in this pass — flagged as a location to check, not a confirmed live deprecation).
### Type-safety smell (`: any` / `as any`)
58 occurrences across `src/app` (grep, not deduplicated by uniqueness) — a moderate count for a codebase this size (~36k lines of `.ts` under `src/app`, per `wc -l`). One confirmed instance directly relevant to architecture: `runtime-provider-strategy.service.ts` casts `(environment as any).useMockBootstrapOnLocal` rather than typing it properly on the `Environment` interface — a small but real type-safety gap in a file that controls production-vs-mock data routing. Not exhaustively catalogued — 58 is the raw count, not individually triaged for severity.
### Inconsistent patterns (architecture-level debt, not simple bugs)
- **Two facades in the same directory use two different reactive idioms** (`ProductFacade` constructor-DI + pass-through vs. `CategoryFacade` `inject()` + parallel signals/observables) — Section 1.
- **Two different drag-and-drop implementations** coexist: CDK drag-drop in the Project Editor (Homepage, Footer) vs. native HTML5 DnD in Admin Categories — no documented reason for the split.
- **Two different "mock data" mechanisms** coexist: DI-token provider-switching (Bootstrap, Backoffice) vs. HTTP-interceptor response-faking (`mockDataInterceptor`, Products/Categories storefront reads) — a reader of the architecture doc alone would not discover the second mechanism exists.
- **Inconsistent `canDeactivate` dirty-guard coverage**: Categories and the Project Editor have one; Products does not, despite having equivalent draft-tracking logic in its facade.
- **`RuntimeProviderMode.'remote-config'`** is declared in a type union and referenced in `switch` statements but never actually returned by any strategy method — dead enum value across at least 4 files.
### Duplicate/near-duplicate code
- The `src/app/pages/info/**` / `src/app/pages/legal/**` legal-page components are internally triplicated by design (one component per locale, e.g. `about-en`/`about-ru`/`about-hy`) rather than one component with locale-driven content — even before accounting for the fact the whole tree is unrouted, this per-locale-component pattern (rather than a single component reading translated content) is a duplication anti-pattern relative to how the rest of the app handles i18n (`TranslateService`/`TranslatePipe`).
- Sprint 24's Transactions gateway and Sprint 23's Orders gateway are intentionally coupled (transactions derive from orders) — this is documented, deliberate coupling, not accidental duplication.
### Legacy/deprecated coexistence
No `@NgModule`-based code exists anywhere alongside the standalone-component codebase (0 hits) — the codebase does not have the "old pattern next to new pattern" debt common in mid-migration Angular projects. This is a genuine strength.
---
## 7. UI/UX audit
Scope note: this section scores what Sections 24 already described in detail; it does not re-derive new findings, only assigns comparative judgments across pages/areas already audited. No redesign proposed, per the audit brief.
| Area | Design | Usability | Accessibility | Consistency | Visual hierarchy | Notes |
|---|---|---|---|---|---|---|
| Home | Good | Good | Not independently verified | Good | Good | Thin container delegating to widget system; known dead-space bug (Section 2) |
| Catalog/Search | Fair | Fair | Not independently verified | Good (shares filter/grid primitives) | Fair | 950-line container is a maintainability risk more than a UX one, but three distinct page contracts in one container increases regression risk for any of the three |
| Product detail | Good | Good | Not independently verified | Good | Good | Well-decomposed presentational children; legacy `ItemDetailComponent` unrouted (no user-facing impact) |
| Cart/Checkout | Fair | Fair | Not independently verified | Good | Fair | No distinct checkout step is a genuine UX gap for anything beyond a single-item impulse buy — no order review/confirmation screen before payment |
| Wishlist/Compare | Good | Good | Not independently verified | Good | Good | Solid feature, undermined by no cross-device sync (local-only) |
| Static Pages (rendered) | Good | Good | Not independently verified | Good | Good | Sanitization present at render |
| Authentication | Fair | Fair | Not independently verified | Good | Good | QR-only login excludes any customer without Telegram — a real access gap for some markets |
| 404 | Missing | Missing | N/A | N/A | N/A | Silent redirect-home, no feedback |
| Maintenance | Missing | Missing | N/A | N/A | N/A | Does not exist |
| Admin Dashboard | Good | Good | Good (Sprint 28 pass) | Good | Good | Honest `pending-backend` states are a real UX strength |
| Admin Products/Categories | Good | Good | Good (Sprint 28 pass) | Good | Good | Recent auto-slug/SEO-fill/badge-manager UX investment (recent commits) |
| Admin Orders/Transactions | Good | Good | Good (Sprint 28 pass) | Good | Good | Print-invoice via browser print is minimal but functional |
| Admin Users/Monitoring/Analytics | Fair | Fair | Good (Sprint 28 pass) | Good | Fair | Heavy reliance on fabricated data undermines perceived reliability for anyone who inspects the numbers closely |
| Admin Media | Good | Good | Not independently verified | Good | Good | Real validation/compression/sanitization, well-scoped |
| Builder — Theme | Fair | Poor | Not independently verified | Good | Good | Two dead controls sitting next to live ones without any visual distinction is a genuine usability failure (Section 4) |
| Builder — Header/Footer/Homepage/Widgets | Good | Good | Not independently verified | Good (recently redesigned) | Good | Most actively-improved area of the whole app per recent commit history |
| Builder — Static Pages | Good | Fair | Not independently verified | Good | Good | `enabled`/`status` dual-gating is conceptually sound but not surfaced clearly in-UI |
| Builder — Preview | Fair | Fair | Not independently verified | Good | Fair | "Preview" mutating live runtime state the same as "Publish" is a real conceptual mismatch with what the label implies |
**Accessibility, general**: `docs/ADMIN.md` Sprint 28 documents a real, specific pass (aria-labels added to every previously-unlabeled `<select>` across admin/*, confirmed `DialogComponent` already had focus-trap/Escape/`aria-modal`, no missing `<img>` alt text anywhere in `src/app`). This audit did not independently re-run a full accessibility sweep (no live browser/screen-reader session was performed) — the "Not independently verified" markers above reflect that this pass relied on reading templates for structural signals (aria attributes present in markup) rather than a live assistive-technology test.
---
## 8. Component audit
### Shared UI inventory (`src/app/shared/ui/**`, `src/app/ui-library/**`)
Confirmed present and reused across both Admin and Marketplace Builder (not the customer-facing marketplace, which mostly uses its own presentational components under `features/website/**`): `app-button`, `app-table`, `app-badge`, `app-empty-state`, `app-pagination`, `app-skeleton`, `app-dialog` (with real focus trap), `app-toggle`, `app-select`, `app-color-picker`, `app-section-card`, `app-locale-tabs`, `app-key-value-editor`, `app-image-field`, `app-code-editor`, `app-form-field`, `app-input`. This is a genuinely mature, well-adopted shared component library for the Admin/Builder surfaces — the Sprint 28/30 design-system consistency passes (Section 3/4) both explicitly targeted "reuse these primitives everywhere" and largely succeeded per the documented before/after state.
### Repeated-pattern extraction candidates (not yet shared)
- List-page toolbars (search box + status filter + pagination) are re-implemented per admin domain (Products/Categories/Orders/Transactions/Users/Monitoring) rather than composed from one shared `AdminListToolbar` — each domain's list page component/template was confirmed to exist independently; a shared toolbar component was not found. Not a bug, but a real extraction opportunity given 6+ near-identical implementations.
- CSV export (`Blob` download, client-side) is implemented independently in at least Orders, Transactions, and Analytics — a shared `exportCsv(rows, filename)` utility was not found in `src/app/shared/util/` or `src/app/shared/utils/` (both directories exist, suggesting an unresolved naming duplication of their own — see below).
- `window.confirm`/`window.prompt` are used directly for destructive-action confirmation and folder/tag naming in multiple admin areas rather than the existing `app-dialog` component — a missed opportunity to standardize on the shared, accessible dialog rather than native browser prompts (native `confirm`/`prompt` cannot be styled and have inconsistent cross-browser behavior).
### Directory-naming duplication (minor debt, worth flagging)
Both `src/app/shared/util/` and `src/app/shared/utils/` exist as separate top-level directories — almost certainly an accidental split (one created before the other, never consolidated) rather than an intentional distinction. Contents of each were not individually diffed in this pass to confirm zero overlap.
### Largest components (line count, `.ts`, excluding `*.spec.ts`)
| Lines | File |
|---|---|
| 950 | `src/app/features/website/catalog/containers/catalog-container.component.ts` |
| 923 | `src/app/interceptors/mock-data.interceptor.ts` |
| 686 | `src/app/features/project-editor/facade/project-editor.facade.ts` |
| 683 | `src/app/features/website/product/containers/product-details-container.component.ts` |
| 673 | `src/app/services/api.service.ts` |
| 664 | `src/app/pages/cart/cart.component.ts` |
| 592 | `src/app/features/search/facade/search.facade.ts` |
| 514 | `src/app/features/admin/categories/facade/admin-categories.facade.ts` |
| 404 | `src/app/features/content-management/components/static-pages-editor.component.ts` |
| 368 | `src/app/pages/item-detail/item-detail.component.ts` (likely unused, see Section 6) |
| 338 | `src/app/features/admin/analytics/facade/admin-analytics.facade.ts` |
| 316 | `src/app/features/admin/products/facade/admin-products.facade.ts` |
Largest templates (`.html`): `public-offer-{ru,hy,en}.component.html` (529 lines each, unrouted — Section 6), `privacy-policy-{ru,en,hy}.component.html` (367 lines each, unrouted), `cart.component.html` (359 lines), `item-detail.component.html` (356 lines, unrouted), `admin-product-form.component.html` (291 lines), `catalog-container.component.html` (290 lines).
**Split candidates**: `catalog-container.component.ts` (950 lines, three route contracts in one container — Section 2), `mock-data.interceptor.ts` (923 lines, almost entirely inline hardcoded mock JSON — a legitimate candidate to move into `assets/mock/*.json` files consumed by `fetch`/`HttpClient` the way `MockBootstrapProvider`/`MockBackofficeDataProvider` already do, for consistency and to shrink the TS bundle), `project-editor.facade.ts` (686 lines, too many concerns in one injectable — Section 4), `product-details-container.component.ts` (683 lines), `api.service.ts` (673 lines, but this is a single cohesive HTTP client surface, less clearly a split candidate than the others), `cart.component.ts` (664 lines, doing double duty as checkout — Section 2).
---
## 9. Translation audit
`src/app/i18n/{en,ru,hy}.ts` — exactly 3 locales, matching every other doc's claim (`en`, `ru`, `hy`; `ru` is the hardcoded routing default per Section 1). `translations.ts` defines the shared `Translations` interface; `translate.service.ts` (`TranslateService.t(key, params?)`) does a dot-path walk and **returns the raw key string unchanged if any segment is missing or the resolved value isn't a string** (`translate.service.ts:23-35`) — this is the exact fallback mechanism that produced the historical ~178-raw-key admin bug (now fixed, Section 3), and remains a standing risk: any future key added to one locale file and not the others will silently render as a raw dotted key in production for that locale, with no build-time or type-level check catching it. `params` string interpolation exists (line ~40 onward, not fully re-read in this pass).
Rough leaf-key counts (grep of `key: '...'`-shaped lines, an approximation not an exact parser-verified count): `en.ts` ~1,750, `ru.ts` ~1,759, `hy.ts` ~1,759. The 9-key difference between `en` and `ru`/`hy` was not resolved to specific missing keys in this pass — flagged as **not exhaustively verified**; a real audit would need a proper AST-based key-set diff across all three files (a grep-based line count is not reliable enough to name the exact missing keys, since some lines legitimately differ in shape, e.g. nested objects vs leaf strings).
**Sampling caveats, stated explicitly**: this audit did not run an exhaustive unused-key check (grepping every one of ~1,750 keys' usage across templates/TS would be prohibitively expensive for this pass and was not attempted), did not run an exhaustive hardcoded-string sweep (spot checks during Section 2/3 reading found translated strings consistently used via `TranslatePipe`/`TranslateService`, with no untranslated English string observed in the admin/builder templates read, but this was incidental to other reading, not a targeted sweep), and did not check for duplicate keys within a single file (would require a proper JS/TS object-literal parser, not grep, to do reliably given nested structure).
**One confirmed, structurally-guaranteed-safe pattern**: the legal/info pages under `src/app/pages/{info,legal}/**` (Section 6) use **per-locale components with hardcoded text** rather than translation keys at all — so they cannot suffer from missing-key drift, but only because they duplicate the same content three times per page instead. This is arguably worse debt than a translation gap, since fixing a typo in the legal text requires editing three separate files. Moot in practice since the whole tree is unrouted, but relevant if it's ever reconnected without also being migrated to the Static Pages system.
---
## 10. Performance
### Bundle budgets (`angular.json`)
```
"budgets": [
{ "type": "initial", "maximumWarning": "700kB", "maximumError": "1MB" },
{ "type": "anyComponentStyle", "maximumWarning": "40kB", "maximumError": "50kB" }
]
```
Per `docs/EDITOR.md`/`docs/ADMIN.md` Sprint 28 notes, the initial bundle currently sits ~198kB over the 700kB warning threshold (i.e. ~898kB), pre-dating all admin/builder feature work — **not independently re-verified in this pass** since no build was run (out of scope per the read-only audit constraints: "no build/install/format commands that write files"). This is a stale-but-plausible number from a prior session; treat it as an estimate, not a current measurement.
### Lazy loading
100% of routes use `loadComponent()` (Section 1) — no eager route components found anywhere in `app.routes.ts`. This is a genuine strength: the admin backoffice, the entire Builder, and every marketplace page are code-split by route.
### Change detection
`OnPush` in 188 of 189 `@Component` files (grep count) — effectively universal, a strong performance signal that should keep re-render scope tight throughout the app. The one component not using `OnPush` was not individually identified in this pass.
### Signals vs. Observable-heavy services
Mixed, as described in Section 1: `signal()` in 53 files, `computed()` in 47. Some services (e.g. `CategoryFacade`) maintain **both** an Observable and a Signal view of the same state, populated by hand inside a subscription rather than via `toSignal()` — this duplicates state and is a real, if minor, waste (two representations of the same data kept manually in sync) rather than a pure win from adopting signals.
### Images
Only 17 of 35 `<img>` occurrences in templates use `loading="lazy"` or `NgOptimizedImage`'s `ngSrc` — roughly half. Not broken down per-page/per-component in this pass (would require reading all 35 individually to name which specific templates lack it); flagged as a real, moderate-priority performance gap, especially for image-heavy surfaces like `product-gallery`, `category-grid`, and the Media Library grid (none of the three were individually confirmed to be in the lazy or non-lazy set in this pass).
### Duplicate/redundant requests
Not systematically checked in this pass beyond what fell out of reading `CategoryFacade` (which does maintain parallel Observable+Signal state, a source of potential double-computation but not necessarily double-HTTP-request) — a targeted trace of whether multiple components independently re-trigger the same gateway call without a shared cache was not performed. `cacheInterceptor` exists in the HTTP interceptor chain (`app.config.ts:26`, last in the chain) and was not opened/verified in this pass to confirm what it actually caches or for how long — **not verified**.
### Largest components (perf-risk read, cross-referencing Section 8)
`catalog-container.component.ts` (950 lines) is the highest-risk single component for both maintainability and runtime performance, given it serves three distinct route contracts and composes the most child components of any container in the app. `mock-data.interceptor.ts` (923 lines) ships as part of the JS bundle regardless of whether mock mode is active for a given request, since it's registered unconditionally in the interceptor chain (`app.config.ts:25-27`) — the interceptor itself presumably checks `environment.useMockData` internally before doing anything (not verified in this pass), but the ~900 lines of hardcoded mock category/product JSON still ship to every client's bundle whether or not mock mode is ever used at runtime, which is real unnecessary bundle weight.
---
## 11. Security
### Authentication & token storage
Two entirely separate auth systems, confirmed isolated (Section 3): customer (`AuthService`) and admin (`AdminAuthService`). Both use a **cookie** for the session id (admin: `adminSessionID`, `SameSite=Strict`, `Secure` when HTTPS, `Max-Age=3600`, set via raw `document.cookie` string construction in `admin-auth.service.ts:198-204` rather than a cookie-handling library — functionally fine but manually re-implements cookie serialization, a place a future edit could introduce a parsing bug). `localStorage` additionally holds `adminToken`/`adminRefreshToken` (`ADMIN_TOKEN_STORAGE_KEY`/`ADMIN_REFRESH_STORAGE_KEY`, `admin-auth.service.ts:23-24`) — per an inline code comment, this is "JWT pair storage, reserved for once the backend issues admin access/refresh tokens. Unused until then." — i.e., dead code paths (`getAdminToken`/`setAdminTokens`/`clearAdminTokens`) waiting on a backend feature that doesn't exist yet. **No JWT parsing/validation happens client-side today** since no JWT is actually issued yet — this is a forward-looking placeholder, not a live gap, but worth confirming these methods get real expiry-checking logic once a real token is issued rather than being wired in naively.
### Session handling
`AdminAuthService.scheduleSessionRefresh()` (`admin-auth.service.ts:165-174`) computes a refresh time from the session's `expires` timestamp minus a 60-second buffer, with a `setTimeout` — a reasonable, real implementation, not a stub. `logout()` calls the real session-invalidation API endpoint before clearing local state. `devBypassLogin()` (`admin-auth.service.ts:100-113`) is explicitly gated by `if (environment.production) return;` — a **runtime check, not just a build-time exclusion**, which is the correct defensive pattern for a dev-only auth bypass (a build misconfiguration that leaves `environment.production` false in a real deployment would still be a live vulnerability, but the code itself does the right thing given a correctly-set flag).
### Route guards
`adminAuthGuard` (`core/admin-auth/admin-auth.guard.ts`) is a straightforward `CanActivateFn` checking `isAuthenticated()` — correctly gates the entire `/backoffice/**` route tree from one place (`app.routes.ts:57`), rather than per-route guards that could be individually forgotten. `languageGuard` is unrelated to security (locale routing only).
### Admin isolation from customer routes
Confirmed structurally isolated: distinct guard, distinct service, distinct cookie name, distinct storage keys, distinct route tree (`/backoffice/**` vs `:lang` core routes) — genuinely well-separated client-side. **The unresolved, repeatedly-documented gap is server-side**: both admin and customer Telegram QR logins hit the identical session-creation endpoint (`TelegramSessionApiService`, `{authApiUrl}/users/sessions`) — the backend has no way to distinguish an admin scan from a customer scan. This means **today, any successfully-authenticated Telegram session could, in principle, be presented against admin API endpoints and the backend would have to accept it**, since nothing server-side currently encodes "this is an admin." This is a real, currently-open, security-relevant gap — not fixable from the frontend alone, and correctly flagged as such in `docs/backend/BACKEND-INTEGRATION.md`, `docs/ADMIN.md`, and `docs/EDITOR.md`. Confirmed independently in this pass by reading `AdminAuthService` in full: nothing in the client code sends an "admin" discriminator to the session-creation call.
### XSS surfaces (`[innerHTML]` / `bypassSecurityTrust*`)
All confirmed occurrences:
- `static-page-preview.component.ts:49``bypassSecurityTrustHtml(sanitized)`, bound at `static-page-preview.component.html:16`. Variable name `sanitized` implies a sanitization step happens before this call; the sanitization function itself was not traced to its source in this pass to confirm it actually strips dangerous content (e.g. `<script>`, event-handler attributes) rather than being a no-op rename — **flagged as needing direct verification, not confirmed safe or unsafe**.
- `static-page.component.ts:32,94,103` — same pattern (`sanitized` variable, `bypassSecurityTrustHtml`), used for the live customer-facing static-page render (`static-page.component.html:15`). Same caveat: the actual sanitization implementation was not opened in this pass.
- `item-detail.component.ts` (`getSafeHtml`, used at `item-detail.component.html:208`) — same pattern, and the component itself is likely-unused/unrouted (Section 6), so currently inert regardless.
- `cart.component.ts:371``bypassSecurityTrustResourceUrl(bankUrl)` for an embedded payment iframe `src` — this is the correct, narrower sanitizer for a resource URL (not raw HTML), appropriate for embedding a bank-hosted payment page.
- `code-editor.component.html:2``[innerHTML]="highlighted()"` inside the Builder's code-editor syntax highlighter (used by `MarketplaceHtmlEditorComponent`'s "Код" raw-HTML toggle) — the `highlighted()` computed presumably tokenizes and HTML-escapes the *admin's own* input for syntax-highlighting display, not third-party content; lower risk since only an already-authenticated admin's own typed content flows through it, but the escaping logic itself was not traced to confirm it actually escapes rather than passing through.
**Net assessment**: the pattern of routing all `bypassSecurityTrustHtml` calls through a variable literally named `sanitized`/`safeHtml` is a good sign of intent, but this audit did **not** trace any of those sanitization functions to their implementation to confirm they use a real HTML sanitizer (e.g. DOMPurify or equivalent) rather than a weaker transform — this is the single most important "could not verify" item in the whole audit given it gates customer-facing rendering of admin-authored (and, per the rich-text editor's "raw, unsanitized" authoring-time behavior noted in Section 4, potentially not pre-sanitized-at-source) HTML.
### HTML/rich-text editor sanitization
Per Section 4 and `docs/EDITOR.md`: `MarketplaceHtmlEditorComponent` **emits raw, unsanitized HTML by design** — the stated architecture is that sanitization happens at storefront-render time (`StaticPageComponent`/`StaticPagePreviewComponent`, both confirmed to run content through `DomSanitizer` per the `bypassSecurityTrustHtml` calls above) rather than at authoring time. Given the "could not verify the actual sanitizer implementation" caveat directly above, this is the platform's single highest-leverage security surface to independently verify before shipping any UGC or multi-admin-tenant scenario: if the render-time sanitization step is weak or bypassable, there is no other line of defense, since the authoring side is explicitly unsanitized.
### Upload security
`MockMediaRepository.validateFile()` (Section 3) rejects files over 10MB and outside a `jpeg/png/webp/gif/svg+xml/pdf` allow-list, with SVG-specific `<script>`/`on*=` stripping before storage — a real, meaningful client-side validation layer. Since storage is IndexedDB-only today (no backend), there is no server-side re-validation to assess — when a real upload backend is built, server-side validation of the same constraints will be required (client-side validation alone is never sufficient against a malicious client bypassing the UI).
### Cookies / CSRF
Only one `document.cookie` write site found (`admin-auth.service.ts`, the admin session cookie) plus presumably an equivalent one in `AuthService` for the customer session (not independently re-opened in this pass to confirm the same `SameSite=Strict`/`Secure` pattern is used — flagged as **not verified**, should be checked for parity with the admin implementation). No CSRF-token handling was found anywhere in `src/app` (no `X-CSRF-Token` header construction, no CSRF-related interceptor) — `SameSite=Strict` on the session cookie mitigates classic CSRF for cookie-based requests, but if any state-changing request relies on a bearer token in `localStorage` instead (as the reserved-but-unused `adminToken` will, once wired up), `SameSite` cookie protection won't apply to that path and CSRF would need a different mitigation at that point — a forward-looking note, not a current live gap since the token path is unused today.
---
## 12. Production checklist
| Feature | Status | Ready | Blocked | Needs Backend | Needs UX | Needs Tests |
|---|---|---|---|---|---|---|
| Home | Working | Yes | No | No | Minor (dead-space bug) | Yes |
| Catalog/Search | Working | Partial | No | No | Yes (container split) | Yes |
| Product detail | Working | Yes | No | No | No | Yes |
| Cart/Payment | Working | Partial | No | No | Yes (no checkout step) | Yes |
| Wishlist/Compare | Working | Partial | No | Yes (cross-device sync) | No | Yes |
| Static Pages (render) | Working | Yes | No | No | No | Yes |
| Legal/Info pages (legacy) | Dead code | No | Yes (unrouted, needs a decision) | No | N/A | N/A |
| Authentication | Working | Partial | No | Yes (non-Telegram fallback) | Yes | Yes |
| 404 page | Missing | No | Yes | No | Yes | Yes |
| Maintenance mode | Missing | No | Yes | No | Yes | Yes |
| Admin Dashboard | Working | Yes | No | Partial (Orders/Revenue) | No | Yes |
| Admin Products | Working | Yes | No | Yes (persistence) | Minor | Yes |
| Admin Categories | Working | Yes | No | Yes (persistence) | No | Yes |
| Admin Orders | Mocked | Partial | No | Yes (fully) | No | Yes |
| Admin Transactions | Mocked | Partial | No | Yes (fully) | No | Yes |
| Admin Customers | Unverified | Unknown | No | Unknown | Unknown | Yes |
| Admin Moderation | Mocked | Partial | No | Yes (fully) | No | Yes |
| Admin Monitoring | Mocked | Partial | No | Yes (fully) | No | Yes |
| Admin Analytics | Mocked | Partial | No | Yes (fully) | No | Yes |
| Admin Users/Roles | Mocked | Partial | No | Yes (fully) | No | Yes |
| Media Library | Local-only | Partial | No | Yes (storage) | No | Yes |
| Builder — General/Branding/Languages | Working | Yes | No | Yes (persistence) | Minor | Yes |
| Builder — Theme | Partial | No | No (control exists, effect doesn't) | No | Yes (dead controls) | Yes |
| Builder — Header/Footer/Homepage/Widgets | Working | Yes | No | Yes (persistence) | No | Yes |
| Builder — Static Pages | Working | Yes | No | Yes (persistence) | Minor | Yes |
| Builder — Navigation | Working | Yes | No | Yes (persistence) | Minor | Yes |
| Builder — Features | Working | Yes | No | Yes (persistence) | No | Yes |
| Builder — Preview/Publish | Working | Partial | No | Yes (no backend save) | Yes (label mismatch) | Yes |
| Dynamic renderer pipeline | Unwired | No | Yes (needs a build-or-delete decision) | No | N/A | N/A |
| Admin/customer server-side auth isolation | Missing | No | Yes | Yes (server-side) | No | Yes |
**Test coverage**: only 5 `.spec.ts` files exist in the entire `src/app` tree — essentially no automated test coverage for a codebase of this size (~36,000 lines of `.ts` under `src/app`). Every "Needs Tests: Yes" above reflects this same underlying gap rather than per-feature specifics.
---
## 13. Release readiness
- **Marketplace (customer-facing): ~65%.** Core browse/product/cart/payment flows are genuinely implemented and backend-wired (a real strength). But there is no checkout step, no customer order history, no password/email login fallback, no 404 page, no maintenance page, and the catalog container is a maintainability risk. The percentage reflects "would a customer complete a purchase today" (yes, via Telegram + cart-embedded payment) weighted against "does this feel like a complete e-commerce product" (no — several standard flows are simply absent, not broken).
- **Admin: ~70%.** Every listed admin area has a real, polished, accessible UI following a consistent design system — this is the most *finished-feeling* part of the app. But 9 of ~11 domains have zero backend behind them (Section 5), meaning almost everything an admin does today doesn't survive a page reload except Products/Categories (which persist to `localStorage`-backed local gateways, still not a real database). The percentage weighs UI completeness (high) against actual data durability (low).
- **Builder: ~75%.** The most actively-developed area per recent commit history (7 of the last 20 commits touch it), with real bug-hunt passes, real UX polish, and a mature validation/schema layer. Held back by: two dead controls in Theme, one unwired rendering pipeline two fields feed into, zero backend persistence for anything, and a Preview/Publish conceptual mismatch.
- **Backend Integration: ~20%.** Bootstrap and Backoffice-reads are the only domains with a working, switchable mock/API pair. Storefront reads work against real HTTP but have no working mock counterpart client-side (interceptor-level mocking is a workaround, not the documented architecture). Every admin write operation and the entire Builder save/publish flow persist only to `localStorage`. The percentage reflects how much of the *documented* provider-swap architecture is actually load-bearing today versus how much of the app's real data flow depends on it.
- **Overall Product: ~55%.** A genuinely well-architected frontend (standalone components throughout, near-universal `OnPush`, 100% lazy-loaded routing, a mature shared component library, near-zero TODO/console.log/NgModule debt, low but real type-safety debt) sitting on top of a backend integration that covers a minority of the product's actual feature surface, with a handful of specific, well-documented, currently-dead UI controls (Theme Mode, Site Layout) that could mislead a merchant into thinking they've configured something that has no effect. The 55% reflects the average of the above four weighted toward Backend Integration and Marketplace since those gate whether the product is actually usable end-to-end by a real customer and a real merchant simultaneously, not just individually impressive in isolation.
---
## 14. Top remaining tasks
Sorted P0 (blocks any real launch) → P3 (polish/nice-to-have). This audit found substantially more than 100 real, non-padded, evidence-based gaps; the list below is capped at the 100 most consequential ones per the brief, grouped for readability. Every item is grounded in a specific finding from Sections 111 above rather than invented.
### P0 — Blocks any real production launch
1. **Server-side admin/customer session isolation.** Why: any Telegram session can currently be presented against admin endpoints; backend has no concept of "admin." Difficulty: High (backend + minor frontend). Files: `src/app/core/admin-auth/admin-auth.service.ts`, backend session-issuance endpoint. Dependencies: backend team.
2. **Verify (or build) real HTML sanitization at every `bypassSecurityTrustHtml` call site.** Why: this audit could not confirm the "sanitized" variables are actually sanitized; if not, this is a live stored-XSS path for merchant-authored (unsanitized-at-source) rich text. Files: `static-page.component.ts`, `static-page-preview.component.ts`. Dependencies: none, purely frontend.
3. **Real backend for Orders + Transactions.** Why: currently 100% synthetic; no real e-commerce platform can ship without real order records. Files: `admin-orders-local.gateway.ts`, `admin-transactions-local.gateway.ts` (replace with `*ApiGateway`). Dependencies: backend order/payment-reconciliation domain.
4. **Real backend for Products/Categories writes.** Why: admin edits currently vanish on page reload (`localStorage` local gateway only, not a database). Files: `admin-products-local.gateway.ts`, `admin-categories-local.gateway.ts`. Dependencies: backend CRUD endpoints (`docs/backend/BACKEND-INTEGRATION.md` §6.7 area).
5. **Real backend for Builder save/publish.** Why: every Builder edit across all 11 sections only persists to a single browser's `localStorage` — clearing site data loses the entire tenant configuration. Files: `project-editor.facade.ts`, `ApiBootstrapProvider`. Dependencies: backend PUT/POST bootstrap endpoints.
6. **Build a real Checkout flow** (shipping/order-review/confirmation) distinct from Cart. Why: no e-commerce product should ship payment embedded directly in the cart with zero order-review step. Files: `src/app/features/website/checkout/` (currently empty), `cart.component.ts`. Dependencies: order backend (#3) for order-creation on submit.
7. **Real backend for Media Library storage.** Why: uploaded assets exist only in the uploading admin's own browser IndexedDB; no other user or device can see them. Files: `MediaRepository`/`MockMediaRepository`, `app.config.ts:29`. Dependencies: backend/CDN asset storage.
8. **Decide and act on the `dynamic-renderer/` pipeline** — finish wiring it (if planned) or delete it and the two dead Builder fields (Theme "Site Layout", Homepage section `type`) that feed it. Why: currently a merchant can configure settings that persist, validate, and appear functional but have zero effect. Files: `src/app/dynamic-renderer/**`, `theme-section.component.ts`, `homepage-section.component.ts`. Dependencies: none, purely a frontend/product decision.
9. **Fix or remove the Theme Mode (light/dark/system) control.** Why: same class of issue as #8 — a live, saveable, validated control with zero runtime effect. Files: `theme-section.component.ts`, needs a real dark-mode CSS strategy (`[data-theme-mode]`/`prefers-color-scheme` + `matchMedia` listener). Dependencies: design decision on whether dark mode is in scope at all.
10. **Automated test coverage.** Why: 5 `.spec.ts` files for a ~36,000-line frontend is not a safety net by any measure; every subsequent change (including all P0P2 items below) currently ships with no regression protection. Files: project-wide. Dependencies: none, but large effort.
### P1 — Blocks a confident, full-featured launch
11. Build a non-Telegram login/registration fallback for customers. `services/auth.service.ts`, `telegram-login.component.ts`.
12. Build a customer-facing order-history/account page. No existing files — net new.
13. Build a 404/not-found page and route it instead of the current silent redirect-home. `app.routes.ts:303`.
14. Build a maintenance-mode page/toggle. No existing files — net new.
15. Real backend for Admin Users/Roles/Invitations (emails currently don't send; roster is hardcoded). `admin-users-local.gateway.ts`.
16. Real backend for Admin Moderation (reviews/reports currently disconnected from real customer-submitted reviews). `admin-moderation-local.gateway.ts`.
17. Real backend for Admin Monitoring (no logging backend exists anywhere in the system). `admin-monitoring-local.gateway.ts`.
18. Real analytics/tracking pipeline for Visitors/Funnels/Heatmaps (currently `pending-backend` placeholders). `admin-analytics.facade.ts`.
19. Split `catalog-container.component.ts` (950 lines, three route contracts) into per-route containers sharing common logic via a service. `features/website/catalog/containers/catalog-container.component.ts`.
20. Delete or reconnect `src/app/pages/info/**` and `src/app/pages/legal/**` (~40 unrouted files, ~40% duplicated per-locale legal content) — either wire into `cmsContentRoutes` or remove entirely in favor of the Static Pages builder. `app.routes.ts:286-289`.
21. Add a `canDeactivate` dirty guard to the Admin Products editor, matching Categories/Project Editor. `app.routes.ts:88-105`, `admin-products.facade.ts`.
22. Confirm/fix `BackendSearchHistoryRepository`'s no-op stub — either wire it to a real endpoint or remove it if genuinely unreachable. `features/search/services/search-history.repository.ts`.
23. Give Wishlist/Compare/Recently-Viewed a real backend so state survives across devices for logged-in customers. `core/user-experience/repositories/local-user-experience.repository.ts`.
24. Reconcile the two mocking mechanisms (DI-token switching vs. `mockDataInterceptor`) into one documented pattern, or explicitly document why both exist. `runtime-provider-strategy.service.ts`, `interceptors/mock-data.interceptor.ts`.
25. Move `mock-data.interceptor.ts`'s ~900 lines of hardcoded JSON into `assets/mock/*.json` files consumed the way `MockBootstrapProvider` already does, to shrink the shipped JS bundle and match the existing convention. `interceptors/mock-data.interceptor.ts`.
26. Clarify or separate the "Preview" vs "Publish" mental model in the Builder — currently identical runtime effect. `preview-section.component.ts`, `project-editor-preview.service.ts`.
27. Add an explicit in-editor warning when a Static Page's own `status` will keep it hidden despite a whole-config Publish. `static-pages-editor.component.ts`.
28. Build the `showProfile` header toggle's actual profile/account menu, contingent on #11/#12 existing first. `header.component.html`, `header-section.component.ts`.
29. Address the ~198kB bundle-budget overage (`angular.json` 700kB warning threshold) via a main-bundle/core-module import audit. `angular.json`, app-wide.
30. Bring `<img>` lazy-loading from ~50% to consistent coverage across product/category/media-library image grids. `product-gallery.component.html`, `category-grid.component.html`, media library templates (specific files not individually identified in this pass).
### P2 — Meaningfully improves quality/consistency before or shortly after backend integration
31. Unify `ProductFacade`/`CategoryFacade` reactive idiom (pick signals-only or a consistent facade pattern project-wide). `facades/platform/product.facade.ts`, `facades/platform/category.facade.ts`.
32. Unify drag-and-drop implementation (CDK vs native HTML5) across Admin Categories and the Builder. `admin-categories-list.component.ts`, `homepage-section.component.ts`/`footer-section.component.ts`.
33. Extract a shared `AdminListToolbar` component (search + filter + pagination) used by 6+ near-identical admin list pages. `features/admin/{products,categories,orders,transactions,users,monitoring}/**`.
34. Extract a shared `exportCsv()` utility (currently reimplemented per-domain). `admin-orders`, `admin-transactions`, `admin-analytics`.
35. Consolidate `shared/util/` and `shared/utils/` into one directory. `src/app/shared/{util,utils}/`.
36. Replace `window.confirm`/`window.prompt` destructive-action/naming flows with the existing `app-dialog` component for consistency and accessibility. Multiple admin components (folder/tag naming in Media, confirm-gated actions in Orders/Categories).
37. Add navigation-link drag-and-drop (currently up/down-only, inconsistent with Homepage/Footer) if there's no deliberate reason for the difference — or document why not. `navigation-section.component.ts`.
38. Give the grouped (column-based) footer nav a cross-link or inline note from the flat-footer-nav tab in Navigation, since both are edited in different places. `navigation-section.component.ts`, `footer-section.component.ts`.
39. Delete confirmed-dead `items-carousel` component. `src/app/components/items-carousel/`.
40. Delete or repurpose the empty `src/app/features/builder/{feature-flag-editor,navigation-editor,page-editor,seo-editor,theme-editor}/` scaffolding.
41. Delete or repurpose the empty `src/app/integrations/{auth,authorization,payment}/` scaffolding.
42. Remove the dead `'remote-config'` `RuntimeProviderMode` enum value or implement it if it was meant to mean something (its name suggests a third data-source mode that was never built).
43. Fix the `(environment as any)` cast in `runtime-provider-strategy.service.ts` by adding `useMockBootstrapOnLocal` to the typed `Environment` interface.
44. Triage the remaining ~57 `: any`/`as any` occurrences project-wide (58 total, 1 addressed by #43) for genuine type-safety risk vs. acceptable escape hatches.
45. Audit and reconcile the `en`/`ru`/`hy` i18n key-count discrepancy (~9 keys) with a real AST-based diff tool rather than grep approximation.
46. Add per-locale missing-key detection to CI (a lint rule or small script comparing the 3 locale files' key sets) so the historical "raw key leak" bug class (Section 3/9) cannot silently recur.
47. Reduce `project-editor.facade.ts` (686 lines) scope by extracting undo/redo history management and draft-storage orchestration into smaller composed services.
48. Reduce `static-pages-editor.component.ts` (404 lines) by extracting bulk-actions and validation-badge-computation logic.
49. Add inline dimension/format guidance to Branding's logo/favicon/social-image fields.
50. Add min/max guardrails to Homepage section's `columns` numeric field (currently only `Number(value) || 1`).
51. Confirm parity between the customer (`AuthService`) and admin (`AdminAuthService`) cookie-setting implementations (`SameSite`, `Secure`) — only the admin one was directly read in this pass.
52. Verify what `cacheInterceptor` actually caches and for how long — not opened in this audit pass.
53. Verify `RuntimeDiagnosticsService`/`PlatformRuntimeStateService` (`core/runtime/`, 25-26 lines each) are actually consumed somewhere and not further dead scaffolding — not individually traced in this pass.
54. Confirm the exact scope/ownership of the "Admin Customers" area, which `docs/ADMIN.md`'s sprint log does not cover — this audit could not independently verify its implementation depth.
55. Decide whether "Reports" (per the brief) maps to Moderation's reports queue or should be a distinct business-reporting area, and build the latter if intended.
56. Add a dedicated "Settings" landing area inside the Admin shell that at least deep-links to the Builder, since today a user browsing `/backoffice/**` has no in-context way to discover `/edit/*` exists.
57. Add per-transaction/per-user audit-log consolidation (or an explicit "these are intentionally separate" note in-UI) since three separate audit-trail concepts exist (per-transaction, per-user, system-wide Monitoring) with no visible cross-reference.
### P3 — Polish, longer-horizon, or explicitly deferred by prior sprints
58. Real interactive image cropping in Media Library (currently compression/resize only, no crop UI — explicitly deferred).
59. Full permission-catalog and custom-role creation for Admin Users (currently 4 fixed built-in roles).
60. Full catalog search for the Related Products picker (currently limited to whatever page is loaded in the facade — explicitly documented trade-off).
61. True folder hierarchy for Media Library (currently a flat tag, not nested).
62. Dynamic, backend-generated sitemap per tenant (currently a static baseline for one locale — explicitly documented limitation).
63. Coupons/discounts/promotions engine (no client code beyond a flag exists — FUTURE per `docs/backend/BACKEND-INTEGRATION.md`).
64. Notification delivery system (FUTURE, no client code).
65. Inventory/warehouse management (FUTURE, no client code).
66. Delivery/logistics beyond cart-level display (FUTURE, no client code).
67. Invoice generation beyond browser print-to-PDF (currently `window.print()` + `@media print`, deliberately minimal).
68. Webhook registration UI for merchants (Monitoring only displays mock webhook delivery logs today, no registration flow).
69. A charting library for Analytics if more chart types are needed beyond the current single-series `<div>`-bar chart (deliberately deferred, not urgent at current scope).
70. Consider migrating `MarketplaceHtmlEditorComponent` off `document.execCommand` proactively rather than reactively if/when browser support risk materializes (currently low-urgency, monitor).
71. A per-field reset capability in the Builder (currently only section-level and whole-project reset exist).
72. Multi-device/multi-tab conflict handling for the `localStorage`-only draft model (two admins editing the same tenant in two tabs today would silently overwrite each other with no conflict warning) — becomes moot once real backend persistence (#5) lands, but worth a stop-gap warning until then.
73. A real design decision on whether the empty `dynamic-renderer/` pipeline (#8) is worth finishing versus deleting, documented in an ADR either way, so this doesn't remain ambiguous scaffolding indefinitely.
74. Standardize `alt`-text and `aria-label` coverage checks into an automated lint/CI rule now that a manual Sprint 28 pass achieved a clean baseline, to prevent regression.
75. Add `prefers-reduced-motion` coverage verification as an automated check (currently a manual global override exists in `src/styles.scss`, verified present but not tested for edge cases like third-party embedded content).
**Note on count**: the brief anticipated 100 tasks; this audit substantiated 75 genuinely evidence-grounded, non-padded items across P0P3. Padding the list to a round 100 with speculative or restated items would violate the brief's own instruction to prefer accuracy over a comprehensive-sounding count — 75 is the honest number of distinct, real findings this pass could ground in specific source evidence.
---
## 15. Final verdict
**If handed to another team tomorrow:** they would find a frontend that is unusually disciplined for its scope — 100% standalone components, near-universal `OnPush`, 100% lazy-loaded routing, essentially zero `console.log`/TODO/NgModule debt, a real and consistently-applied container/facade/gateway layering, and a shared component library that both the Admin and Builder surfaces genuinely reuse rather than reinvent per-feature. They would also quickly discover that almost none of the Admin backoffice and none of the Builder persist anything past a browser's `localStorage`/IndexedDB — the single fact that most changes what a new team should do first, since it means "finish the last 20%" reads very differently here than in most audits: the frontend is largely *done* for its current mocked scope, and the primary remaining work is backend integration, not frontend feature-building.
**Biggest strengths:**
- The architectural discipline (layering, standalone components, lazy loading, shared UI library) is real and load-bearing, not just documented aspiration — verified against actual code repeatedly in this pass, not just the architecture doc's claims.
- The Builder (`features/project-editor/`) is the most mature, actively-improved, and best-documented part of the codebase, with a genuine track record of finding and fixing real correctness bugs (id-collision bugs, silent-discard bugs, dead-toggle bugs) via a documented bug-hunt methodology rather than only shipping new features.
- Honesty patterns in the UI itself (`pending-backend` badges on Dashboard/Analytics cards rather than fabricated numbers) are a genuinely good practice that should be the template for every other mocked domain, not just the two that currently do it.
**Biggest risks:**
- **Zero automated test coverage** (5 spec files) means every one of the fixes and features described throughout this document — including the historical bug-hunt fixes already shipped — has no regression protection. The next refactor of `project-editor.facade.ts` or `admin-categories.facade.ts` could silently reintroduce any of the already-fixed bugs.
- **The admin/customer server-side auth isolation gap** (Section 11) is a real, currently-open security exposure, not a theoretical one, and is entirely outside frontend control to fix.
- **The unverified sanitization chain** for merchant-authored rich text (Section 11) is the audit's single largest "could not confirm" item with real stakes — if the render-time sanitizer is weak, there is no other line of defense given the authoring side is explicitly, deliberately unsanitized.
- **The scale of the mock-vs-real gap is easy to underestimate from the UI alone**, since the Admin surface is polished enough to look production-ready at a glance; a new team could easily under-budget the backend-integration effort if they judge readiness by UI quality rather than by tracing each gateway to its actual data source, as this audit did.
**What should never be changed** (without a very deliberate, reviewed decision): the container/facade/gateway/InjectionToken layering pattern itself — it is the reason backend integration is tractable at all (Section 5's "no facade changes required" finding is real and valuable); the standalone-components-only convention; the near-universal `OnPush` discipline; ADR-010's frozen auth/payment/authorization contracts (changing these outside a reviewed process is explicitly called out as high-risk in the architecture docs and this audit found no reason to disagree).
**What should be rewritten:** `catalog-container.component.ts` (950 lines serving three route contracts) is the clearest rewrite candidate — not because it's broken, but because its size and multi-contract scope make it the highest-risk file to safely modify without tests. `mock-data.interceptor.ts` should be converted to the asset-JSON pattern already used elsewhere for consistency and bundle size.
**What should be deleted:** the ~40 unrouted legal/info page components (`src/app/pages/{info,legal}/**`), the empty `features/builder/*` scaffolding, the empty `integrations/*` scaffolding, and the confirmed-dead `items-carousel` component — none of these carry any product risk to remove, and all four are currently misleading to a new contributor trying to understand "where does X live."
**What should be postponed until after backend integration:** essentially the entirety of Section 14's P2/P3 list (component extraction, drag-and-drop unification, toolbar consolidation, permission catalogs, etc.) — these are real but lower-leverage than making the 9 mocked admin domains, the Builder save/publish flow, and Checkout actually durable. Polishing a UI whose data doesn't persist is work that will likely need to be partially redone once real backend contracts (with real field shapes, real error states, real pagination semantics) replace the current local gateways.
---
*End of audit. No source files were modified to produce this document.*

View File

@@ -1,153 +0,0 @@
# Platform Standardization Report
Date: 2026-07-09
Scope: Sprint 11.5 - Platform Standardization
## Objective
Standardize the frontend platform before Admin Platform work without adding new marketplace features.
## Summary
The sprint delivered a platform-wide standardization pass across architecture boundaries, legacy cleanup, widget metadata support, translation coverage, theme token normalization, and architecture documentation updates.
## Files Changed
- `angular.json`
- `src/styles.scss`
- `src/assets/mock/bootstrap/widget-manifest.json`
- `src/app/theme/tokens/theme-variable-map.ts`
- `src/app/theme/mappers/theme-css-vars.mapper.ts`
- `src/app/shared/models/config/widget.model.ts`
- `src/app/widgets/contracts/widget-manifest.contract.ts`
- `src/app/widgets/resolvers/data-source-resolver.service.ts`
- `src/app/layouts/containers/dynamic-page-layout.component.ts`
- `src/app/components/product-card/product-card.component.ts`
- `src/app/components/product-card/product-card.component.html`
- `src/app/components/header/header.component.ts`
- `src/app/components/header/header.component.html`
- `src/app/components/header/header.component.scss`
- `src/app/components/language-selector/language-selector.component.scss`
- `src/app/components/delivery-selector/delivery-selector.component.scss`
- `src/app/components/items-carousel/items-carousel.component.scss`
- `src/app/pages/cart/cart.component.html`
- `src/app/pages/cart/cart.component.scss`
- `src/app/pages/home/home.component.ts`
- `src/app/pages/home/home.component.html`
- `src/app/pages/home/home.component.scss`
- `src/app/pages/item-detail/item-detail.component.scss`
- `src/app/pages/public/platform-home.component.ts`
- `src/app/pages/backoffice/backoffice-dashboard.component.ts`
- `src/app/pages/builder/builder-sandbox.component.ts`
- `src/app/widgets/ui/unknown-widget.component.ts`
- `src/app/widgets/ui/recently-viewed-widget.component.ts`
- `src/app/features/website/user-experience/components/recently-viewed-strip/recently-viewed-strip.component.ts`
- `src/app/features/website/user-experience/compare/components/compare-table.component.ts`
- `src/app/features/website/user-experience/compare/components/compare-table.component.html`
- `src/app/features/website/user-experience/wishlist/containers/wishlist-page.component.ts`
- `src/app/i18n/translations.ts`
- `src/app/i18n/en.ts`
- `src/app/i18n/ru.ts`
- `src/app/i18n/hy.ts`
- `docs/architecture/foundation/README.md`
- `docs/architecture/foundation/Configuration-Standards.md`
- `docs/architecture/foundation/Component-Standards.md`
- `docs/architecture/foundation/Service-Standards.md`
- `src/app/features/website/catalog/services/catalog-search-history.service.ts` (deleted)
## Technical Debt Removed
- Removed duplicate catalog search-history service implementation and consolidated on core search history service.
- Removed legacy variant naming from active application-layer styles/templates (`novo` / `dexar` remnants in `src/app/**`).
- Eliminated hardcoded UI strings identified in audited runtime pages/components by introducing translation keys.
- Standardized global token surface for spacing and transition variables.
- Added explicit widget metadata support surface for animation/style/permissions and normalized metadata propagation through dynamic rendering.
## Architecture Audit Result
Target flow: UI -> Facade -> Domain -> Infrastructure -> Backend
Result:
- Audited website feature containers/components for direct forbidden dependencies.
- No active component-level `HttpClient` usage found.
- No active component-level DTO imports found.
- No active component-level `environment` access found.
- Facade/container orchestrations remain in place for catalog and product UX flows.
## Legacy Cleanup Result
Searched and addressed:
- `isMarketplaceVariant`
- `marketplaceVariant`
- `projectName`
- legacy variant naming usage in app layer (`novo`, `lavero`, `dexar`)
Result:
- No remaining `isMarketplaceVariant`, `marketplaceVariant`, or `projectName` matches in `src/**`.
- No remaining `novo`/`lavero` matches in `src/app/**`.
## Bootstrap Audit Result
Config representability status:
- Core page/section/widget configuration remains bootstrap-driven.
- Widget metadata support expanded in contracts and dynamic renderer.
Documented gaps (no backend changes implemented):
- Full role/permission enforcement for widgets is limited by current auth session shape (no roles/permissions in session model).
- Catalog popular-search defaults are still facade-local constants and should be modeled in bootstrap catalog config.
- Optional persistent-storage key prefix override is not yet modeled in bootstrap schema.
## Widget Audit Result
Support status:
- `title`: supported
- `subtitle`: supported
- `visibility`: supported
- `layout`: supported
- `animation`: supported
- `style`: supported
- `permissions`: supported (auth-gate baseline)
## Translation Audit Result
- Remaining hardcoded UI strings found in audited pages/components were replaced with translation keys.
- Translation schema and locale resources were updated for new keys.
## Theme Audit Result
Standardized token set ensured:
- Spacing: `--space-xs`, `--space-sm`, `--space-md`, `--space-lg`, `--space-xl`
- Radius: `--radius-sm`, `--radius-md`, `--radius-lg`
- Shadows: `--shadow-sm`, `--shadow-md`, `--shadow-lg`
- Transitions: `--transition-fast`, `--transition-normal`, `--transition-slow`
Runtime mapping now propagates spacing/transition tokens through theme engine.
## Validation
Executed:
- `npm run build`
- `node tools/architecture/check-boundaries.mjs`
Expected acceptance target:
- Build with no warnings
- No legacy marketplace conditions in app layer
- No environment-driven business logic in feature/business layers
- No duplicate implementations in catalog search history path
## Remaining Recommendations
1. Extend auth session model/contracts to include role and permission claims, then enforce widget role/permission checks in dynamic layout.
2. Move `SearchFacade.popularSearches` to bootstrap `catalog` settings to remove remaining hardcoded discovery defaults.
3. Add bootstrap-configurable storage namespace/prefix for all local persistence services.
4. Continue translation audit on legacy pages outside audited standardization scope (especially static/legal and long-tail utility pages).
5. Consider normalizing project naming in build metadata (`angular.json` project id/output naming) in a dedicated infra-only cleanup sprint.

View File

@@ -1,86 +0,0 @@
# Release Candidate Audit (RC1)
**Date:** 2026-07-19 (audit) / 2026-07-19 (P0 fix pass, same day)
**Role played:** Staff Engineer + Senior UX Designer + QA Lead + Accessibility reviewer + Product Owner, auditing the whole platform (storefront + admin) for demo/RC readiness.
**Method:** Live click-through of the running dev build (`ng serve`) across storefront and admin routes, accessibility-tree inspection, source-code verification of every finding below (file/line cited where the root cause was located), plus review of this repo's own prior audit docs (`docs/KNOWN-ISSUES.md`, `docs/ADMIN-UX-AUDIT.md`, `docs/ADMIN.md`, `docs/SPRINT-PLAN.md`) to avoid re-reporting already-fixed items and to flag which older findings are now stale.
**Correction from the original audit pass:** the storefront's product/category data is **live backend data** (`proxy.conf.json` proxies `/api` to `https://novo.market`), not a local mock fixture as first assumed — this matters for P0-4 and P0-5 below (real third-party seller data, not something this repo's mock fixtures can simply be "reseeded" to fix).
**Fix pass status: all 8 P0 items resolved or closed as false-positive.** Every fix below is scoped to production polish only — no new features, no data-model changes beyond the one flagged and explicitly approved (P0-3), no architecture changes. Each commit was gated on `tsc --noEmit` and `ng build` passing, and live-verified in the running app. See §6 for the fix log.
**Environment caveat:** screenshot/visual-pixel capture was not obtainable in this session (browser tooling timeout); findings below are backed by live DOM/accessibility-tree inspection and source verification, not pixel screenshots. Color-only-signal and fine visual-hierarchy judgments are flagged as "needs visual follow-up" rather than asserted.
**Important — some prior findings are now stale.** `docs/ADMIN-UX-AUDIT.md` (dated 2026-07-17) reported "no admin shell" and "~178 raw i18n keys" as the two biggest P0s. Both are now substantially fixed: an admin nav/shell exists (`features/admin/shell/`), the admin dashboard renders fully in Russian with real copy, and recent commits (`fix(i18n): eliminate all raw translation key leaks across admin pages`, `c452d28 fix(admin): P0 user-reported bugs`) closed most of that gap. Do not re-triage those two items from the old doc as open — see §4 for what was re-verified live and what wasn't.
---
## P0 — Must fix before demo
| # | Status | Page | Problem | Fix |
|---|---|---|---|---|
| P0-1 | ✅ Fixed | Home, Catalog, Search, Wishlist, Compare, PDP "similar products" (every product card sitewide) | Every product card showed an unexplained raw badge reading exactly **"High"** — `product-card.component.html:9` rendered `item.remainings` directly, untranslated. | `catalog.stockHigh/Medium/Low/Out` keys added to all 3 locales; badge now translates via the value (case-insensitive), `out` styling fixed to match. Commit `1a5d435`. |
| P0-2 | ✅ Fixed | Home, Catalog, Search, Wishlist, Compare (every product card) | Favorite/compare/share/quick-view icon buttons' `aria-label`s rendered literal untranslated keys (`catalog.favorite`, `catalog.share`, `catalog.quickView` — none existed in any locale file, and even `catalog.compare` was unused/missing). | Added all 4 keys to `Translations` + `en/ru/hy.ts`. Commit `1a5d435`. |
| P0-3 | ✅ Fixed | Home hero section | Hero title/subtitle/CTA rendered hardcoded English on every locale — `HeroWidgetProps` had no per-locale text support, unlike nav labels (`NavigationLocalizedText`). | **User explicitly approved the real fix** (not a demo-data-only patch): `DataSourceResolverService.toHeroData()` now accepts a per-locale text map (`LocalizedTextContent`, same shape/fallback as footer group titles) alongside the existing plain-string form — existing tenants unaffected. Mock bootstrap hero props updated to a real ru/en/hy map. Verified live in all 3 locales. Commit `5b063d5`. |
| P0-4 | ✅ Fixed | Catalog & Search facet panel ("Category" filter) | Category filter showed placeholder labels like "Категория 2008" for every option — traced to `search.facade.ts` synthesizing `Category {id}` for every distinct `product.categoryID`. | **Investigated further and found this isn't a missing-lookup bug — it's a real data gap.** The storefront's product data is live backend data (`novo.market`, via `proxy.conf.json`), and its `categoryID` values don't correspond to any id in the demo tenant's own (much smaller) category tree — there is no real name to show. Wired `CategoryFacade` into `SearchFacade`; ids that do resolve now show their real title, ids that don't are dropped from the facet entirely instead of showing a fabricated label. The category filter group simply doesn't render when nothing resolves, which is honest given the data. Commit `3747749`. |
| P0-5 | ✅ Fixed | Search result cards / PDP description tab | Product descriptions rendered literal HTML-entity markup as visible text, e.g. `&lt;attention&gt;...&lt;/attention&gt;`, `&quot;AppStops&quot;` — confirmed this is live third-party seller content (via the `novo.market` proxy), not mock data. | Added `cleanDescription()` (`utils/item.utils.ts`) — decodes common HTML entities, then strips any resulting tag-like markup. Wired into `ProductCardComponent` (Home/Catalog/Search/Wishlist/Compare/PDP-similar) and `ProductDescriptionComponent` (PDP description tab). Output stays plain text via interpolation, never `innerHTML` — no new XSS surface. Verified live on the "iphone" search query that originally showed the bug. Commit `a39b342`. |
| P0-6 | ❌ Not a bug (false positive) | Cart | Original audit read "Цвет:" with nothing visibly after it and assumed the selected color value was lost. | **Re-investigated with the accessibility tree, not text extraction.** `cart.component.html:49-54` already renders a color swatch (`<span class="cart-colour-swatch" [style.background-color]="item.colour" [title]="item.colour">`) — a visual CSS box, not text, so it has no visible characters for a text-only reader to pick up. Confirmed live: the swatch's accessible name is `#00008B`, matching the PDP selection, exactly as designed. No code change made. This was an artifact of the original audit's text-only method, not a real defect — flagging the correction so it isn't "fixed" twice. |
| P0-7 | ✅ Fixed (descoped, not built) | Storefront Account | No account/profile area exists anywhere; `HeaderConfig.showProfile` is a Project Editor toggle with no corresponding UI. | Building a real account/order-history page is feature work, explicitly out of scope for this polish-only pass. Removed the dangling "Profile" toggle from the header-section editor and its field-schema entry instead, so the editor no longer implies a feature that doesn't exist. `HeaderConfig.showProfile` field/default unchanged (data model untouched) in case a future account feature wires it up. Commit `e2d97d1`. |
| P0-8 | ✅ Fixed (verification + 2 new leaks closed) | Admin — Orders, Transactions, Users, Monitoring, Analytics | Re-verify whether the ~178-key i18n gap and missing-admin-shell items from `docs/ADMIN-UX-AUDIT.md` (2026-07-17) are actually still open, given later commits claim to have fixed them. | **Confirmed stale**: admin shell and dashboard localization are genuinely fixed. Live-walked the 5 remaining CRUD areas and found 2 real remaining leaks: `adminOrders.status.all` (primary status filter's "All" option) and `adminUsers.suspend`/`adminUsers.reactivate` (row action button). Transactions/Monitoring/Analytics showed no raw dot-key leaks. Added the missing keys to all 3 locales; verified live. Commit `1c39263`. |
---
## P1 — Should fix
| # | Page | Problem | Why it matters | Est. effort | Solution |
|---|---|---|---|---|---|
| P1-1 | Catalog & Search "Popular searches" chips | Chips read **"Smartphones, Sneakers, Headphones, Laptops"** in English while every surrounding label on the same page (`ru` locale) is Russian. | Same locale-inconsistency problem as the hero (P0-3) but lower-visibility (secondary UI, not first-screen). | S | Route these through the translate pipe / locale-aware config the same way the rest of the search UI already is. |
| P1-2 | Catalog & Search "Size" filter | First option in the Size filter chip list is literally **"default"** — a technical placeholder value, not a size. | A customer sees a nonsense filter option before the real sizes (S/M/L/XL). Low effort, easy trust hit. | S | Filter out or relabel placeholder enum values before rendering facet options. |
| P1-3 | Every product card with no reviews (majority of the catalog) | Rating always renders **"0.0 (0)"** instead of hiding the rating block or showing a neutral "No reviews yet" state, per `product-card.component.html:45-50` (`showRating` doesn't gate on `item.rating > 0` or review count). | A brand-new store with zero reviews looks like it's selling 0-star-rated products across the board — actively worse than showing nothing. | S | Hide the rating row (or swap to a neutral "No reviews yet" chip) when review count is 0, regardless of the numeric rating value. |
| P1-4 | PDP color/variant swatches | The color swatch's accessible name/tooltip is the raw hex code (`#00008B`) instead of the human color name ("Deep Blue," which is already present in the product title). | Screen-reader and low-vision users get a hex code instead of a color name; sighted users hovering get the same unhelpful string. | S | Prefer a `colorName` field for the swatch's `aria-label`/title, falling back to hex only if no name exists. |
| P1-5 | Cart line item price | Price renders as raw `166126.9 RUB` (no thousands separator, single decimal) directly under the product row, while the same page's "Итого"/"К оплате" totals correctly show `166,126.90 RUB` a few lines down. | Two different number formats for the same currency on one screen reads as unpolished/inconsistent, and the more prominent (unformatted) one is right next to the product a customer is about to buy. | S | Apply the same `number:'1.2-2'` (or currency) pipe used elsewhere in cart/checkout to the line-item price. |
| P1-6 | Admin Dashboard "System status" | Products/Categories show a red **"Проблема" (Problem)** status purely because the tenant currently has 0 products/categories seeded — this conflates "empty/not started yet" with "broken." Same class of issue `docs/ADMIN-UX-AUDIT.md` R6 already flagged for the old English-language dashboard; re-confirmed live against the now-localized version — the *design*, not just the translation, still needs a distinct "not started" state. | A merchant opening a brand-new store sees red "Problem" badges before they've done anything wrong, which is discouraging and inaccurate framing for an onboarding moment. | SM | Give the health-check list a third state ("not started" / neutral) distinct from "problem," reserved for genuinely-empty-by-design conditions (0 products on a fresh tenant is not a config problem). |
| P1-7 | PDP tabs | Live text extraction of the product detail tab strip showed `Описание / Характеристики / Отзывы / Вопросы и ответы / Доставка / Гарантия / Описание` — "Описание" (Description) appears to occur twice in sequence. Flagged as **needs a visual follow-up** (couldn't confirm via screenshot this session) to rule out this being the tab label immediately followed by the active panel's own heading rather than an actual duplicate tab. | If it's a genuine duplicate tab, it's a straightforward but visible bug on the highest-traffic storefront page (PDP). If it's just adjacent label/heading text, no action needed. | XS to verify | Visually confirm with a screenshot pass once browser tooling is available; fix tab config if duplicated. |
| P1-8 | Storefront-wide | `docs/KNOWN-ISSUES.md` items still open and unresolved as of this audit: Theme Mode selector has no runtime effect (light/dark/system all look identical); the `dynamic-renderer/` pipeline is fully built but never wired to any template (dead code, and two Project Editor fields — Site Layout, homepage section `type` — silently do nothing); homepage hero-to-categories dead space gap. All three re-confirmed still open by source inspection. | Each is a real "this control does nothing" trap for a merchant using the editor, discovered by the platform's own prior audits — repeating them here so they aren't lost before RC sign-off. | See original entries in `docs/KNOWN-ISSUES.md` for effort/detail | Either finish or explicitly descope (and hide the corresponding editor controls for) Theme Mode and the unwired renderer pipeline before calling the editor "done." |
| P1-9 | Admin — Orders/Transactions/Users/Monitoring dangerous actions | `docs/ADMIN-UX-AUDIT.md` R7 flagged Suspend user / Retry payment / Clear fraud flag / Export financial data as same-visual-weight inline actions with no confirmation. Not re-verified live this session (lower-traffic screens, time-boxed pass) — carrying forward as still-likely-open pending a final confirm pass. | Financial/security-adjacent actions without a confirm step are a real risk even in a demo environment (a stray click "retries" a payment or exports customer PII). | SM per action | Add an explicit confirm step stating the concrete consequence before firing any of these actions. |
---
## P2 — Nice improvements
| # | Page | Problem | Why it matters | Est. effort | Solution |
|---|---|---|---|---|---|
| P2-1 | Catalog & Search "Rating" filter | Only shows "5 звёзд" and "1 звёзд" chips (5-star and 1-star), skipping 2/3/4 — unclear if intentional (min/max only) or a truncated facet list. | Minor UX ambiguity; a shopper wanting "4 stars and up" has no obvious option. | XS to verify, S to fix | Confirm intent; if meant to be a full 15 range, render all 5 chips or switch to a min-rating slider. |
| P2-2 | Home hero-to-categories | `docs/KNOWN-ISSUES.md` item 1 (dead space gap between hero and categories) traced to mock config padding values, not code — re-confirm against real tenant data before RC since this doc's own note says it's unconfirmed outside the mock fixture. | Cosmetic but visible on the highest-traffic page. | XSS | Re-test with a non-mock bootstrap once available; adjust section/widget padding tokens if it reproduces. |
| P2-3 | Bundle size | `angular.json` production budget: initial bundle was already **1.29MB, over the pre-existing 1MB error threshold**, before any P0 fix — confirmed via `git stash` on a clean tree, i.e. `ng build` failed on `main`/`B2B` already. Raised `maximumError` to 1.5MB (commit `8d99510`, user-approved) purely to unblock the build-gate requirement for this fix pass; the `700kB` warning threshold is untouched, so the size regression stays visible in every build. This does **not** fix the underlying size problem, only stops it from hard-failing `ng build`. | Doesn't block a demo; affects first-load performance in production. Was already broken before this session — not introduced by any P0 fix. | M (main-bundle/core-module import audit) | Track as its own post-RC performance sprint, not an RC blocker. Consider whether `maximumError` should come back down once the real fix lands. |
| P2-4 | Storefront-wide | Live browser screenshot/visual capture was unavailable in this session (tooling timeout) — several judgment calls above (P1-7 tab duplication, color-contrast/status-icon clarity on the admin dashboard, general spacing/typography rhythm across pages) could only be text/DOM-verified, not visually confirmed. | A pixel-level pass (Lighthouse/axe or manual screenshot review) is still owed before RC sign-off, consistent with the same caveat already logged in `docs/ADMIN.md` Sprint 28 ("no live Lighthouse/browser run available in this environment"). | S (once tooling allows) | Re-run this audit's visual-dependent items with a working screenshot tool, or manually in a real browser, before final RC sign-off. |
| P2-5 | Search "Найдено товаров: 200" vs "200 товаров" | The results-count string appears to render twice in slightly different phrasing right next to each other ("Найдено товаров: 200" then "200 товаров" a line below, same pattern seen on Catalog too). Needs visual confirmation — may be a result-count component plus a separate view-mode/count label that just read as adjacent text in this text-only pass. | If genuinely duplicated, it's redundant/confusing; low severity either way. | XS to verify | Visual/DOM follow-up once screenshots are available. |
---
## 4. What was re-verified vs. carried forward from prior docs
To avoid duplicating this repo's own extensive audit history, the following prior documents were treated as background, not re-derived from scratch:
- **`docs/KNOWN-ISSUES.md`** — all "Open" items re-confirmed still open by source inspection this session (see P1-8). "Fixed" items were not re-tested (no reason to doubt them; they're specific and were verified live at the time).
- **`docs/ADMIN-UX-AUDIT.md`** (2026-07-17) — its two headline P0s (no admin shell, ~178 raw i18n keys) are **stale**; both look substantially fixed based on live re-verification of Dashboard/Categories/Products this session and recent commit history (`fix(i18n): eliminate all raw translation key leaks across admin pages`, `refactor(admin): unify platform UX and consistency`, `c452d28`). Its lower-priority items (R7 confirm-steps on dangerous actions, R9 Monitoring audience mismatch, R10R15 polish items) were **not** re-verified live this session and are carried forward as likely-still-relevant (P1-9, and implicitly the rest) rather than re-litigated — a follow-up pass specifically re-walking Orders/Transactions/Users/Monitoring/Analytics is the single highest-value next step to close this audit out (P0-8).
- **`docs/SPRINT-PLAN.md`** — confirms Sprint 29's translation-validation work and Sprint 30's final-verify pass both ran (`tsc`, `ng build`, `arch:check`) and reported clean; this audit did not re-run those build-level checks (out of scope for a UX/product audit) and takes them as current.
## 5. Areas requested but not separately itemized above
- **Builder / Project Editor**: `docs/EDITOR.md`'s own "Bug-hunt audit pass" and "Known gaps" sections already cover this in more depth than a fresh pass would in the time available (9 verified-and-fixed defects, 3 documented open gaps — Theme Mode, dynamic-renderer, showProfile — carried into P1-8 above). No new Builder-specific findings beyond what's already tracked there.
- **Static Pages, Legal, Info pages**: not separately walked live this session; `docs/StaticPages.md` describes the module as feature-complete with draft/publish, SEO, and device preview. Recommend a follow-up pass if any tenant-authored legal/static content will be shown in the demo, since content quality (not code) is the main open risk there, matching the pattern found in P0-4/P0-5.
- **Reviews, Media Library, Diagnostics (`/__diagnostics`)**: Media Library is called out in `docs/ADMIN-UX-AUDIT.md` §1.12 as the best-executed screen in the admin — no reason to doubt that holds. Diagnostics is dev-only and excluded from production, so it's not a demo-readiness concern by design.
## 6. P0 fix pass log (2026-07-19)
Executed immediately after this audit, same day, per explicit instruction to fix P0 only. Constraints: architecture unchanged, no invented backend APIs, no data-model changes unless absolutely necessary, no feature additions, production polish only. Every commit below passed `tsc --noEmit` and `ng build` before being made, and every fix was live-verified in the running dev app (not just typechecked).
1. `8d99510`**Build-gate prerequisite.** `ng build` failed on an unmodified tree (pre-existing 1.29MB bundle vs 1MB error budget). Raised `angular.json`'s `maximumError` only, after asking the user how to handle it. Not a P0 item itself — logged in P2-3.
2. `1a5d435` — P0-1, P0-2 (stock badge translation, favorite/share/quickView aria-label keys).
3. `5b063d5` — P0-3 (hero locale-map). **Asked the user** whether to do a demo-data-only patch or the real (data-shape-widening) fix, since it crossed the "avoid data-model changes" constraint; user chose the real fix.
4. `3747749` — P0-4 (category filter: resolve real names via `CategoryFacade`, drop unresolvable options instead of fabricating a label).
5. `a39b342` — P0-5 (decode/strip HTML-entity markup from product descriptions, `product-card` + PDP description tab).
6. `e2d97d1` — P0-7 (removed dangling header "Profile" toggle from the editor; did not build the account feature it implied, per "no feature additions").
7. `1c39263` — P0-8 (verification pass over Orders/Transactions/Users/Monitoring/Analytics; found and fixed 2 more raw i18n key leaks).
8. P0-6 — investigated, determined to be a false positive from the original audit's text-only method. No fix needed; corrected in this doc.
**Stopped and asked the user twice**, per "stop only if a business decision is required": once for the build-budget gate (pre-existing failure unrelated to any P0 fix), once for P0-3 (whether hero localization warranted the data-model widening the constraints otherwise ask to avoid). Both were explicitly resolved by the user before proceeding.

View File

@@ -1,47 +0,0 @@
# Release Candidate Report
Live browser walkthrough of every route across Storefront, Builder, Backoffice — "pretend this ships tomorrow." No redesign, no route/API changes. Three sequential commits on branch `B2B` (not pushed):
| Commit | Scope |
|---|---|
| `0a1acbd` | Storefront |
| `2cbb62a` | Builder |
| `907ac2c` | Backoffice |
`npm run build` verified green after every commit. `npx tsc --noEmit` clean throughout. Dev server run against the local mock bootstrap + dev-proxy `/api/*` (real seeded data, expected setup — not a bug).
## P0s found and fixed
Two genuine ship-blockers, both app-wide in effect despite being caught during a single-surface walkthrough:
1. **Query-param routing broken on every route (`src/app/guards/language.guard.ts`).** The legacy-URL redirect built the target with `router.createUrlTree([...])` using a single path-segment string with the query string baked in, so it got percent-encoded into the path instead of parsed as query params — `/edit/branding?devBypassAdmin=true` became `/ru/edit/branding%3FdevBypassAdmin%3Dtrue`, a dead route. This guard runs on every top-level route, so **any bookmarked or shared deep link carrying query params was silently broken app-wide** — not a Builder-only bug, just discovered there. Fixed with `router.parseUrl()`.
2. **Categories CRUD completely broken end-to-end (`admin-categories-gateway.token.ts`).** `RuntimeProviderStrategyService.getBackofficeProviderMode()` has no `isLocalhost()` fallback (unlike `getBootstrapProviderMode()`), so the categories gateway always resolved to the real HTTP `AdminCategoriesApiGateway` instead of the purpose-built local mock — in an environment with no real backend (see `docs/PERFORMANCE_REPORT.md`/`docs/ACCESSIBILITY_REPORT.md` context: backend integration is still PLANNED). Combined with `saveDraft()` having no error handler, every create/publish click **silently failed** — nothing saved, the dirty flag never cleared, and the unsaved-changes guard then blocked navigation with zero feedback to the admin user. Fixed by wiring the token to the already-defined `getCategoryProviderMode()` + adding the same `isLocalhost()` mock fallback. Live-verified: created/edited/reordered categories, all persist correctly now. Production/non-localhost behavior unchanged.
## P1s found and fixed
**Storefront:**
- Cart item description rendered a bare `...` when an item had no description.
- Compare table showed raw internal stock enum values (`high`/`low`) instead of localized labels.
- Zero-result search stacked confusing "browse categories" empty-category messaging on top of the search's own "nothing found" message.
- Footer "About" link 404'd (`/about` vs actual route `/about-us`) — fixed in mock bootstrap.
- Missing fallback placeholder image for items with no photo — added `public/assets/images/placeholder.svg`.
**Builder:**
- `resetDraft()` discarded the draft but never resynced `lastSavedBootstrap`, so the save bar immediately showed "unsaved changes" right after a full reset.
**Backoffice:**
- Categories list mislabeled its Edit button "Edit product" (wrong i18n key — `adminProducts.edit` instead of the already-correct `adminCategories.edit`). Not part of the tracked ~178-key i18n gap, since the right key already existed.
## Flagged, not fixed (need a decision, not a code fix)
- **Storefront**: Footer "Contacts" link has no static-page content in mock data at all (unlike About, which was just a route-name mismatch) — needs a content decision. `/images/*.webp` 404s on product cards are numeric-ID paths meant to resolve against a real backend/CDN not present in local dev — expected dev-only gap, not a bug. Russian pluralization ("1 товаров" vs "1 товар") — cosmetic, out of scope.
- **Builder**: the static page editor's actual body-content WYSIWYG editor lives inside a collapsed `<details>` under "Advanced", mislabeled "Source HTML (advanced)" though it's the only way to edit page content — works correctly once found, relocating/relabeling it is a navigation/design decision for product sign-off.
## Verification
Live browser walkthrough performed in all three passes (not static-only) — every route above was actually navigated, forms actually submitted, CRUD actually exercised (categories create/edit/delete/reorder, cart add/remove, compare/wishlist add), both desktop and mobile viewports checked, console + network inspected after each navigation. `tsc --noEmit` and `npm run build` green after each commit.
## Cross-reference
This pass sits on top of four prior audits this cycle: `docs/UI-COMPOSITION-REVIEW.md` (composition), `docs/STORE_FRONT_UX_REVIEW.md` / `docs/STORE_REVIEW.md` (storefront polish), `docs/PERFORMANCE_REPORT.md` (bundle/reactivity), `docs/ACCESSIBILITY_REPORT.md` (WCAG 2.1 AA). Known, already-tracked gaps not re-reported here: ~178 missing `adminXxx.*` i18n keys, Theme Mode has no runtime effect, `dynamic-renderer/` unwired, `primeng` still in `package.json` pending an unrelated lockfile blocker — all in `docs/KNOWN-ISSUES.md`.

View File

@@ -1,102 +0,0 @@
# Sprint Plan (working doc, delete after release)
Repo already past old "Sprint 19" (dashboard). Renumbering user's Sprint7+ roadmap to continue actual sequence: **Sprint 20 = old "Sprint 7" (Categories)** ... **Sprint 30 = old "Sprint 17" (Final Release)**.
Pattern to follow (mirrors `admin/products`): `models/`, `services/<x>-gateway.interface.ts` + `<x>-local.gateway.ts` (IndexedDB via existing local-storage service), `facade/`, `pages/`, `components/`. Local-only until backend endpoint exists — log gap in `docs/BACKEND.md`.
Autonomy: no stop-and-ask except missing API contract / business decision / secrets / external config / legal / multi-valid-design-fork. Commit after each sprint, conventional commits, no push. Quality gate before marking sprint done: build, typecheck, lint (if configured), responsive check, a11y pass, docs updated, no dead code, arch:check green.
Notify user: **from Sprint 20 (Categories) once product↔category link + admin categories CRUD exist, catalog can start receiving real items** (currently admin-products already has categoryId field but no category source of truth — that's the point where "adding items to catalog" becomes real, not mocked dropdown).
---
## Sprint 20 — Category Management ✅ done
- [x] AdminCategory model (id, parentId, title, translations, slug, icon, image, seo, visible, order, draft/published status, soft-delete)
- [x] AdminCategoriesGateway interface + local (in-memory, seeded from BackofficeDataService) gateway, mirrors products gateway
- [x] AdminCategoriesFacade (signals, CRUD, tree ops, slug validation, draft recovery)
- [x] Categories list page: indented tree, native HTML5 DnD reorder, visibility toggle, item counter, empty state, include-deleted filter + restore
- [x] Category editor: name/slug (+ uniqueness validation), translations editor, icon field, image via MediaPickerComponent, SEO fields, breadcrumb preview
- [x] Delete validation (blocked if has children or itemsCount>0) + soft-delete/restore
- [x] Draft/publish workflow + localStorage draft recovery (mirrors Project Editor autosave) + unsaved-changes CanDeactivate guard (mirrors projectEditorDirtyGuard)
- [x] Wired into admin routing (`app.routes.ts`, replaced coming-soon placeholder)
- [x] Updated `docs/ADMIN.md` (new Sprint 20 section), `docs/BACKEND.md` (categories gap rewritten with real field list)
- [~] Responsive/a11y: reuses existing FormField/Input/Table/Button/EmptyState a11y wiring; live-browser click-through blocked by safety classifier on the guarded admin route (see below) — verified via tsc/build/arch:check only
- Commit: `feat(admin): complete category management`
- Note: `admin/products`' category dropdown still uses its own `AdminProductsGateway.loadCategories()`, not this new gateway — unification deferred to Sprint 21 (documented in BACKEND.md).
## Sprint 21 — Product Management completion ✅ done
- [x] Audit + add missing: `archived` (soft, list filter + archive/restore action), `barcode`, `variants` (lightweight name|price|quantity), `relatedProductIds` (checkbox picker)
- [x] Wired products to real `AdminCategoriesGateway` (`AdminProductsLocalGateway` now seeds from `AdminCategoriesLocalGateway`, replacing its own `BackofficeDataService` category seed)
- [x] Gallery via `MediaPickerComponent` (add/remove thumbnails); translation editor already existed (Sprint pre-19); preview (computed discounted price); infinite-scroll toggle on list (`facade.loadMore()` appends vs pagination)
- [x] Updated `docs/ADMIN.md` (new Sprint 21 section), `docs/BACKEND.md` (item 6 rewritten with real field list)
- Commit: `feat(admin): complete product management`
- Known trade-off: related-products picker sources from whatever page is currently loaded in `AdminProductsFacade.products()`, not a full catalog search — documented in ADMIN.md, fine at current mock scale.
## Sprint 22 — Media System hardening ✅ done
- [x] Folder support (flat, filter-based), tags (edit + filter), search (already existed)
- [x] Resize/compression on upload (canvas downscale + re-encode, raster only) + SVG script-sanitization + file-type/size validation with real surfaced errors
- [x] Confirmed reuse: category images (20), product gallery (21), branding logo/compact-logo/favicon (22, newly wired). Static pages: no image fields (not a gap). Hero: no dedicated field exists.
- [x] Storage abstraction documented (`MediaRepository` abstract class was already the abstraction)
- Commit: `feat(media): reusable media management`
- Scoped down from ticket: no interactive crop UI (compression/resize only); folders are a flat tag, not a real folder entity/hierarchy.
## Sprint 23 — Orders (mock/local, flag backend gap) ✅ done
- [x] AdminOrder model + local gateway (24 seeded synthetic orders, no real data source existed)
- [x] List: search, status filter, pagination, CSV export
- [x] Detail: customer/payment/shipping, itemized total, status timeline, change-status, refund request + cancel (confirm-gated), customer notes + internal notes, print invoice (window.print + @media print)
- [x] `docs/ADMIN.md` (new Sprint 23 section), `docs/BACKEND.md` item 7 rewritten
- Commit: `feat(admin): order management`
## Sprint 24 — Transactions (mock/local) ✅ done
- [x] Payments/refunds/QR list, status, history (derived from Sprint 23's seeded orders), export, filters, search, retry failed, fraud flags, per-transaction audit log dialog
- [x] `docs/ADMIN.md` (new Sprint 24 section), `docs/BACKEND.md` item 13 added
- Commit: `feat(admin): transaction management`
## Sprint 25 — Users & Roles ✅ done
- [x] Users/roles/permissions models (local, 4 built-in roles), marketplace vs office admin distinction
- [x] Invitations, passwordless login confirmed already real (Telegram QR, AdminAuthService — linked not reimplemented), session/device manager (mocked), per-user audit trail
- [x] Wired into Dashboard Quick Actions + new `/:lang/backoffice/users` route
- [x] `docs/ADMIN.md` (new Sprint 25 section), `docs/BACKEND.md` item 14 added
- Commit: `feat(admin): users and permissions`
## Sprint 26 — Monitoring ✅ done
- [x] Audit/security/login/failed-login logs (unified event feed, category filter), API/error/warning feed, queue/webhook mock views, health page (reuses real Sprint 19 healthChecks)
- [x] `docs/ADMIN.md` (new Sprint 26 section), `docs/BACKEND.md` item 15 added
- Commit: `feat(admin): monitoring center`
## Sprint 27 — Analytics ✅ done
- [x] Sales/revenue/orders/products/categories (real aggregation over mock order data), visitors/funnels/heatmaps (pending-backend badges, no fabricated data), sales-over-time bar chart, 7/30/90-day ranges, CSV export
- [x] `docs/ADMIN.md` (new Sprint 27 section), `docs/BACKEND.md` item 16 added
- Commit: `feat(admin): analytics dashboard`
## Sprint 28 — Marketplace Polish ✅ done
- [x] Skeleton/empty-state consistency: `admin/products`, `admin/users`, `admin/monitoring`, `admin/analytics` had a `loading` signal but no loading/empty UI - wired `app-skeleton`/`app-empty-state`; `admin/categories`/`admin/orders`/`admin/transactions`/media library got the missing loading skeleton (already had empty-state); `admin/dashboard`'s card component migrated its pre-primitive shimmer `<div>` to `app-skeleton`
- [x] A11y: aria-label on every previously-unlabeled `<select>` across admin/*; manual audit confirmed `DialogComponent` focus trap/Escape/aria-modal already correct and no missing `<img>` alt text anywhere in `src/app`
- [x] Animations: global `prefers-reduced-motion` override added in `src/styles.scss` (neutralizes all animation/transition durations + smooth-scroll)
- [x] SEO: `SeoService.resetToDefaults()` now reads real `bootstrap.seo.default`/`branding` instead of hardcoded placeholder text + a broken `/og-image.jpg` reference; re-applies automatically on bootstrap (re)load
- [x] Sitemap/robots: new `public/sitemap.xml` (static baseline, documented per-tenant-dynamic limitation) + `public/robots.txt` `Sitemap:` directive and admin/editor `Disallow` rules
- [x] Responsive: confirmed `shared/ui/table`'s existing `overflow-x: auto` wrapper covers all admin tables; admin list pages already had per-feature breakpoints, extended consistently
- [x] `docs/ADMIN.md` new Sprint 28 section (combined result of both commits below)
- [x] Found and logged (not fixed - Sprint 29 scope): ~178 missing `adminXxx.*` i18n keys across the whole admin backoffice, see `docs/KNOWN-ISSUES.md`
- Commits: `refactor: marketplace release polish` (admin a11y/skeleton pass), plus this session's follow-up completing SEO/sitemap/animations/dashboard-card/KNOWN-ISSUES
- Known trade-off: no live Lighthouse/browser run available in this environment (same guarded-admin-route constraint noted in prior sprints) - SEO/a11y/animation fixes above are the manual-audit equivalent of what it would have flagged.
## Sprint 29 — Release Candidate
- [x] Dead code check: grepped `console.log|console.debug|console.warn|debugger|TODO|FIXME` across `features/admin/**` — none found. `--noUnusedLocals --noUnusedParameters` tsc pass over `features/admin/**` — clean.
- [x] Build/type/arch validation: `tsc --noEmit`, `ng build`, `arch:check:boundaries`, `arch:check:cycles` all green with the current working tree (including this session's other in-flight edits).
- [x] `CHANGELOG.md` and `RELEASE-NOTES.md` added at repo root, summarizing Sprints 20-28.
- [ ] Translation validation / lint: superseded by the ~178 missing `adminXxx.*` i18n keys already logged in `docs/KNOWN-ISSUES.md` and being addressed there — not duplicating that work here. No lint script exists in `package.json` (no eslint config) — skipped as not applicable.
- Commit: `chore: release candidate`
## Sprint 30 — Final Release
- [x] Full verify pass: `tsc --noEmit`, `ng build`, `arch:check:boundaries`, `arch:check:cycles` all green against the final committed tree (10 sprints + concurrent SEO work, 13 commits since `dbd905b`)
- [x] Working tree clean, all sprint work committed (`git log` confirmed)
- [ ] `git push` — awaiting explicit user go-ahead (confirm-required, per plan and safety rules)
---
### Stop conditions (ask user)
- Real backend API contract needed (orders/transactions/users are local-mock by design per above — flagged, not blocking)
- Ambiguous business rule (e.g. category deletion policy specifics) not inferable from existing product-deletion pattern
- Credentials/external service config
- Legal/product-only decision
- Two equally valid architectures for same feature

View File

@@ -1,97 +0,0 @@
# Storefront UI Polish — RC1 Review
Scope: production-ready UI/UX polish pass across the storefront, one page at a time.
Architecture, routing, facades, providers, APIs, and data models were left unchanged
per instructions. No `docs/DESIGN.md` exists in this repo, so `impeccable` design-hook
findings referencing it (font-size/radius/color "outside DESIGN.md") were treated as
inapplicable noise throughout — there's no design system file to violate.
## Pages reviewed
| Page | Commit | Status |
|---|---|---|
| Home | `fix(storefront): polish home` | done |
| Header | `fix(storefront): polish header` | done |
| Footer | `fix(storefront): polish footer` | done |
| Search | `fix(storefront): polish search` | done |
| Catalog | `fix(storefront): polish catalog` | done |
| Product Details | `fix(storefront): polish product-details` | done |
| Compare | `fix(storefront): polish compare` | done |
| Wishlist | — | reviewed, no changes needed |
| Cart | `fix(storefront): polish cart` | done |
| Checkout | — | no separate implementation (see below) |
| Static Pages | `fix(storefront): polish static-pages` | done |
## Improvements made
**Correctness bugs fixed:**
- **Catalog**: `app-catalog-empty-state` self-closed one line early in the
empty-category state, leaving `(secondaryAction)="goToParentCategory()" />` as an
orphaned line outside any tag. Angular rendered it as literal text on the page and
the handler was never wired. Fixed and verified the component's `secondaryAction`
output exists.
- **Cart**: the payment success checkmark (`✓`) and timeout clock (`⏱`) icons had been
corrupted to literal `?` glyphs (confirmed via `git log -p` — both were replaced by
`?` in a prior change). A customer who just paid saw a confusing `?` instead of a
success indicator. Restored both icons and marked them `aria-hidden` since the
adjacent heading already conveys the status in text.
**Accessibility:**
- Added `role="status"`/`aria-live="polite"` to loading states and `role="alert"` to
error states on Home, Search, Product Details, and the CMS static-page loader —
previously plain text with no announcement to screen readers.
- Added `:focus-visible` states where entirely missing: Footer nav links, Search retry
button, Catalog empty-state actions/chips, Product Details buying-flow controls
(add-to-cart/buy-now/wishlist/compare/share buttons, variant colour/size pickers,
star rating selector), and the static-page 404 back-home link.
- Fixed a real keyboard-accessibility bug in the Header: mobile nav items (catalog,
static pages) were `<a>` with no `href`, driven only by `(click)` — not reachable
via Enter/Space and not exposed correctly to assistive tech. Converted to
`<button type="button">`, matching the existing desktop nav-btn pattern.
- Search input had a placeholder but no accessible label — added `aria-label`.
- Cart icon-only buttons (quantity increase/decrease, mobile delete) had no
accessible name at all; the desktop remove button relied on a `title` attribute
only, which screen readers don't reliably announce. Added `aria-label` to all four,
plus new `cart.increaseQuantity`/`decreaseQuantity` i18n keys (en/ru/hy).
- Compare's remove-from-list button rendered a bare `×` glyph with no label — added
`ux.removeFromCompare` across all three locales.
**Dead code / duplication removed:**
- Home: ~900 lines of CSS from two superseded redesigns (`.alt-*`, `.platform-*`
home layout) with zero template references — verified via grep before deletion.
- Header: ~495 lines of dead `.header`/`.alt-header` CSS from earlier redesigns,
same verification approach. Only the current `.platform-*` classes are live.
**Loading/empty states:**
- Home's hardcoded "Loading homepage..." text replaced with a skeleton layout
(`app-skeleton`) mimicking the hero + category grid; empty-config state now uses
the shared `EmptyStateComponent` instead of raw text.
## Remaining issues (not fixed in this pass)
- **Product engagement components** (review-form, question-form, question-card,
review-card, review-list, question-list, rating-summary, stars, product-tabs,
product-gallery, product-description, product-specifications, product-warranty,
related-products, delivery-information) still have **zero `:focus-visible` coverage**.
This pass fixed the highest-traffic buying-flow controls (actions, variants, star
selector) given scope/time; the remaining engagement-tab components should get the
same treatment in a follow-up pass.
- Several icon SVGs in the Header (search icon, cart icon, mobile menu icons, chevrons)
use hardcoded hex fills (`#576463`, `#1e3c38`, `#497671`, `#697777`, `#677B78`)
instead of theme CSS custom properties (ADR-008 convention). They'll look correct in
the current tenant theme but won't adapt if a tenant switches theme/dark mode.
Recasting them to `currentColor` + a themed wrapper color was judged too broad a
diff for a single-page polish pass — flagged here for a dedicated icon-theming pass.
- No `docs/DESIGN.md` exists. If the team wants literal design-system enforcement
(type ramp, radius scale, color palette) going forward, that file needs to be
authored — the `impeccable` hook is currently flagging pre-existing values against
a document that was never created.
## Optional future improvements (post backend-integration)
- Cart quantity controls, search-box, and header search box are all separate hand-rolled
implementations; once the design system stabilizes, consider consolidating around a
shared input/stepper component to reduce drift.
- Home's skeleton loading state is hand-built to approximate the hero+grid layout;
once real component boundaries for the dynamic homepage are finalized, this could
move to per-widget skeleton placeholders resolved by the widget host itself.

View File

@@ -1,69 +0,0 @@
# Storefront Premium UX Review — RC-Premium-01
> **Archived, correction (2026-07-25):** the FAQ/legal-page polish below was applied to `pages/info/**`/`pages/legal/**`, which turned out to be unrouted dead code (the CMS static-page renderer serves these routes in production, not these components) — see `docs/KNOWN-ISSUES.md` item 13. Kept here for historical accuracy.
Follow-up to `docs/UI-COMPOSITION-REVIEW.md` (RC-Visual-02). That sprint fixed *composition* bugs — undefined theme variables, hand-rolled skeletons/empty-states replaced with shared components, missing `scope="col"`. This sprint (RC-Premium-01) is a narrower, purely *visual/interaction* polish pass on top of that clean baseline: hierarchy, spacing, states, motion, accessibility signaling. No redesign, no business-logic changes, no route changes, no new components — reuses `src/app/shared/ui/*` and the existing token set in `src/styles.scss` exclusively.
Executed as four sequential commits on branch `B2B` (not pushed):
| Commit | Scope |
|---|---|
| `9ea8c98` | Home, Catalog, Search |
| `ea1a5d9` | Product Details, Compare, Wishlist |
| `9956020` | Cart, Checkout |
| `d5603c2` | Static / Legal / Info pages |
`npm run build` verified green after every commit. Baseline bundle-budget warning (pre-existing, unrelated) persists.
## Pages audited
Home, Catalog (grid, filters, sorting, layout switcher), Search (bar, results, trending, empty state), Product Details (gallery, info, actions, description, specs, warranty, delivery, related products, variant selector, reviews/questions/ratings), Compare, Wishlist, Cart (line items, quantity, summary, payment modal, QR/bank-payment flow), Checkout (no separate route — the payment section within Cart), Static Pages (FAQ, About, Contacts, Delivery, Guarantee, legal pages, CMS-driven static-page renderer).
**Not touched, with reason:**
- `src/app/pages/item-detail/*` — confirmed dead/unrouted code.
- Wishlist page and `product-details-container` — already fully composed with shared components from RC-Visual-02, nothing to add.
- Contacts page — no form exists to polish.
- About/Delivery/Guarantee/Company-details/Payment-terms/Privacy-policy/Public-offer/Return-policy — already token-clean, no accordion/form elements in scope.
## Recurring theme: color-only signaling
The single most common finding across every area: state was communicated by color alone (selected swatch = border color change only, active tab = background color only, active sort/layout = highlight color only). Fixed everywhere it appeared by pairing color with a second signal — `aria-pressed`, `aria-current`, `aria-selected`, `aria-live`, or a visible checkmark/icon — so the same state is legible to screen readers and to users with color-vision deficiency, not just sighted mouse users.
## Fixes applied by area
### Home, Catalog, Search (`9ea8c98`)
- Hardcoded hex across `product-card`, `category-grid`, catalog filters/results normalized to design tokens; stock/discount indicators moved to semantic `--success-color`/`--warning-color`/`--error-color`.
- Color/size/rating filter chips, layout-switcher buttons, mobile sort/grid sheet options get `aria-pressed`/`aria-current`; selected color swatch gets a checkmark + ring instead of border-color-only.
- Add-to-cart hover, card hover shadow, favorite/compare toggle buttons (`aria-pressed`) polished with existing `--transition-*`/`--shadow-lg` tokens.
- Home loading/empty spacing converted to `--space-*` tokens.
### Product, Compare, Wishlist (`ea1a5d9`)
- Hardcoded hex to tokens across gallery, actions, description, related-products, variant-selector, and engagement components (tabs, reviews, questions, star-selector).
- `aria-pressed` on wishlist/compare toggles and variant buttons; checkmark on selected color swatch; `aria-current`+`aria-label` on active gallery thumbnail; `role="tab"`/`aria-selected` on product tabs; per-star `aria-label` on star-selector (new `starsLabel` i18n key added to en/hy/ru); `aria-expanded` on ask-question disclosure.
- Hover/focus-visible/active/disabled states added across action buttons, gallery thumbnails/toolbar, pager/load-more, form inputs.
- Review-list and question-list empty text replaced with shared `app-empty-state`.
- Compare table: `scope="col"`/`scope="row"`, sticky header + sticky attribute column, polished remove-chip hover/focus.
### Cart, Checkout (`9956020`)
- Hardcoded hex normalized to tokens across line items, quantity controls, summary, login gate, terms checkbox, payment modal, QR/payment-active screen, bank-payment iframe overlay; deduplicated a repeated `.close-modal-btn` rule.
- `focus-visible` rings added to every interactive control in the flow (clear-cart, remove-item, quantity stepper, checkout/QR/card buttons, close-modal, retry-payment, copy/open-link, telegram-login).
- Delivery-required warning now pairs an icon + `role="alert"` with the text; quantity value gets `aria-live="polite"` + `aria-label` so screen readers announce changes.
- `delivery-selector`: tokens normalized, hover/focus-visible added to the `<select>`, dead `:host-context(.cart-container.alt)` rule removed (orphaned by RC-Visual-02's `.alt` theme deletion).
### Static Pages (`d5603c2`)
- FAQ entries converted from always-expanded static blocks to native `<details>/<summary>` disclosures, reusing the existing global chevron pattern in `styles.scss` rather than a new accordion component.
- `shared-legal.scss` (shared by every info/legal page): `transition: all 0.3s ease` sites normalized to `--transition-normal`; prose capped at `max-width: 70ch` for readable line length.
- CMS-driven static-page renderer (`src/app/pages/static-page/`): spacing moved to `--space-*` tokens, prose capped at 70ch, and real styling added for headings/lists/links/images/blockquote/table — previously only `h2`/`h3` margins were styled for arbitrary `[innerHTML]` CMS content.
## Remaining recommendations (not applied — flagged for follow-up, not this pass's scope)
- **Payment modal / bank-payment iframe** could move to shared `app-dialog`, but it carries custom multi-step state and its own focus-trap already — a composition change, not visual polish.
- **`clearCart()`'s native `confirm()`** could become a custom confirm-remove dialog, but no existing storefront pattern exists to follow yet — first instance would be an architecture decision.
- **Cart's `.email-form` block** (post-payment email/phone capture) is dead CSS behind commented-out markup — candidate for deletion in a cleanup pass.
- **`pages/category` and `pages/search`** still use hand-rolled skeleton markup instead of `app-skeleton` — replacing that is a composition fix (RC-Visual-02 territory), not covered here.
- **`stars.component` rating glyph color** and a few legacy hex literals in `pages/category`/`pages/search` have no exact token match — left as-is to avoid an unintended visual shift; worth a deliberate token-extension decision later.
- **Responsive verification**: all four passes worked from source/SCSS with build checks only; no browser was available in this session. Recommend a visual QA pass at mobile/tablet widths, particularly the FAQ accordion, compare table's sticky column, and cart's payment modal.
## Not audited
- Same exclusions as RC-Visual-02: **Settings** (no route exists), **Diagnostics** (dev-only, excluded from production).

View File

@@ -1,46 +0,0 @@
# Storefront Review — RC STORE-01
> **Archived, correction (2026-07-25):** the category/search skeleton fix below was applied to `pages/category/*`/`pages/search/*`, which turned out to be unrouted dead code — see `docs/KNOWN-ISSUES.md` item 13. The fix itself is harmless but has zero production effect. Kept here for historical accuracy.
Mission: polish the customer-facing storefront only (Home, Catalog, Search, Product, Compare, Wishlist, Cart, Checkout, Static Pages). No redesign, no architecture change, no Builder/Backoffice changes. Reused existing `src/app/shared/ui/*` components exclusively.
This is the third storefront-focused pass, on top of two prior sprints already documented:
| Doc | Scope |
|---|---|
| `docs/UI-COMPOSITION-REVIEW.md` (RC-Visual-02) | Composition bugs: undefined theme vars, hand-rolled skeletons/empty-states → shared components, missing `scope="col"`, dead CSS. |
| `docs/STORE_FRONT_UX_REVIEW.md` (RC-Premium-01) | Visual/interaction polish: typography, spacing, hover/focus/active states, color-only-signal fixes (aria-pressed/current/live), transitions, badges/price/discount presentation. |
| **This doc (RC STORE-01)** | Closed the concrete gaps those two passes deliberately deferred, re-verified nothing regressed. |
## What this pass found
Re-checked `docs/KNOWN-ISSUES.md` items 6-10 (all flagged during RC-Premium-01, 2026-07-23) against the current tree before doing any new inspection — per this mission's "don't rediscover" instruction, existing docs were the source of truth, not a fresh repo-wide audit.
Of the 5 flagged items, 2 were safe, scoped, non-architectural fixes matching this mission's focus list (Skeletons, and general cleanup); 3 require an architecture or design-system decision and were correctly left alone both times.
## Fixed this pass
1. **Category/Search skeleton → shared `app-skeleton`.**
`src/app/pages/category/*` and `src/app/pages/search/*` still had their own `.skeleton-card`/`.skeleton-image`/`.skeleton-line` markup with a hardcoded-hex shimmer animation, duplicating exactly what `app-skeleton` (already used by `catalog-container` and `product-details-container`) provides. Swapped both pages to `app-skeleton` (`shape="rect"` for image/button, `shape="text"` for lines), deleted the now-dead per-page shimmer CSS (~50 lines/page, hardcoded hex removed). Structural wrapper classes (`.item-card`, `.item-details`) kept for grid layout — only the shimmer implementation moved to the shared primitive.
Commit: `2e85495`.
2. **Cart: removed dead `.email-form` markup + CSS.**
Post-payment email/phone-capture form was commented-out HTML (`cart.component.html`) with a live ~90-line `.email-form`/`.email-input`/`.submit-email-btn` CSS block still shipping in the bundle for markup that never renders. Removed both. No `.ts` logic touched — the component's email/phone handler methods still exist and are unaffected; if they're confirmed unused elsewhere, that's a separate dead-code decision, not in scope here.
Commit: `ce96ef1`.
## Deliberately not touched (architecture/design decisions, not polish)
- **Payment modal / bank-payment iframe on Cart** — still a custom component, not `app-dialog`. It owns a real multi-step state machine and its own focus-trap already; migrating it is a composition change. Flagged three times now (RC-Visual-02, RC-Premium-01, this pass) — recommend a dedicated ticket rather than deferring again silently.
- **Cart's `clearCart()` native `confirm()`** — no existing storefront confirm-dialog pattern to follow; introducing the first one is an architecture decision (which shared dialog variant, where it lives), not a polish-pass call.
- **`stars.component` rating glyph color + legacy hex in category/search** — no exact token match in the current palette. Needs a deliberate token-extension decision (add a token vs. accept the literal) before touching, to avoid guessing at a visual outcome nobody signed off on.
## Verification
- `tsc --noEmit`: clean.
- `npm run build`: green, only the pre-existing bundle-budget warning (768 kB vs 700 kB, unrelated to this pass).
- Manual: category/search skeleton markup swap is a like-for-like structural replacement (same wrapper classes, same grid), verified by reading rendered template output; no visual-regression risk beyond the shimmer implementation itself, which is the same shimmer `app-skeleton` already renders elsewhere in the same app.
- No live browser session available this session; recommend a quick visual check of category/search infinite-scroll loading state and the cart post-payment success screen before shipping.
## Storefront status after this pass
Home, Catalog, Search, Product, Compare, Wishlist, Cart, Checkout (payment section within Cart — no separate checkout route), Static Pages: all three passes (composition, visual polish, this cleanup) complete. Remaining open items are the 3 above, all correctly gated behind a decision outside a pure polish/cleanup pass's scope — not silently dropped, tracked in `docs/KNOWN-ISSUES.md` (items 6-8) and `docs/FRONTEND-ROADMAP.md`.

View File

@@ -1,133 +0,0 @@
# Sprint 10 - Advanced Search & Catalog Report
## 1. Architecture
- Existing platform architecture was preserved.
- No authentication flow changes.
- No payment flow changes.
- No bootstrap loading flow changes.
- Widget engine and section engine were not modified.
- ProductFacade was only extended (no contract removals or breaking changes).
### Data Flow
- Catalog UI calls ProductFacade only.
- ProductFacade extensions for sprint 10:
- search(criteria)
- filter(criteria)
- sort(criteria)
- loadCatalog(criteria)
- No HttpClient usage in feature layer.
## 2. New Reusable Components
### Search Module
- CatalogSearchBoxComponent
- keyword search
- instant suggestions (frontend-prepared, backend-ready)
- recent searches
- search history
- loading and no-results hint
### Filters Module
- CatalogFiltersPanelComponent
- dynamic filter rendering from filter definitions
- supports multi-select, range, toggle filter types
### Sorting Module
- CatalogSortingControlComponent
- configurable sort options
### Layout Module
- CatalogLayoutSwitcherComponent
- grid
- large grid
- compact grid
- list
### Search Results Module
- CatalogSearchResultsComponent
- loading skeletons
- summary
- pagination
- empty state
### Product Card Enhancements
- Added placeholders (no business logic):
- favorite
- compare
- quick view
- Added stock badge and configurable discount/rating/availability visibility.
## 3. Domain Models
Added reusable catalog/search models:
- SearchCriteria
- FilterDefinition
- FilterOption
- SortDefinition
- CatalogView
- SearchResult
File:
- src/app/core/products/models/catalog-experience.model.ts
## 4. Bootstrap Additions
Added catalog feature configuration (config only, no catalog data):
- layout
- navigationMode
- defaultSort
- availableSorts
- enabledFilters
- showBreadcrumbs
- showCategoryBanner
- showSubcategoryChips
- showRatings
- showDiscounts
- showAvailability
- suggestionsEnabled
- searchHistoryEnabled
Files:
- src/app/shared/models/config/catalog-config.model.ts
- src/app/shared/models/config/bootstrap-config.model.ts
- src/assets/mock/bootstrap/bootstrap.json
## 5. Backend Expectations
Documented current and future-ready expectations:
- Current:
- GET /searchitems
- GET /category/{id}
- Future-ready:
- GET /search/suggestions?q={term}
- GET /catalog/filters?category={id}&q={term}
## 6. Validation Results
### Build
- npm run build: PASS
### Architecture
- npm run arch:check:boundaries: PASS
- npm run arch:check:cycles: PASS
### Layering checks
- HttpClient in feature layer: none found
- DTO leaks in feature layer: none found (search matched addToCart text only, not DTO usage)
### Compatibility
- Widget compatibility preserved
- Section engine compatibility preserved
## 7. Documentation Updated
- docs/platform/00-bootstrap-example.md
- docs/platform/02-bootstrap-json-spec.md
- docs/platform/06-api-contracts.md
- docs/platform/08-catalog-domain.md
- docs/platform/13-backend-requirements.md
- docs/backend-platform/business-apis.md
## 8. Future Extension Points
- Backend-driven suggestions endpoint integration.
- Backend-driven dynamic filter metadata (option counts/ranges/facets).
- Navigation mode implementations:
- left category navigation
- mega category layout
- top category carousel
- URL-state sync for all search/filter/sort/layout inputs.
- Dedicated saved searches / personalized search suggestions.

View File

@@ -1,173 +0,0 @@
# Sprint 11 - User Experience Module Report
## Scope
Implemented reusable customer experience capabilities for marketplace storefront UX while preserving established architecture constraints.
Implemented features:
- Wishlist (guest local storage + architecture-ready repository contract)
- Compare (configurable max count, reusable comparison table, compare page)
- Recently Viewed (auto tracking + reusable strip + widget compatibility)
- Continue Browsing (persist/restore filters, sort, page, scroll)
- Saved Searches (architecture + UI integration)
- Product Sharing (Web Share + clipboard fallback)
- Product Card action preparation without duplication
- Floating notifications and heart animation
## Architectural Constraints Compliance
Confirmed constraints:
- No modifications to authentication, payment, bootstrap loading flow, widget engine core, section engine core.
- No direct HttpClient usage in UI feature components.
- Product data path remains facade-driven.
- Existing ProductFacade contracts preserved (only UX facade added separately).
- Bootstrap contains feature configuration only; no business/user data payload.
## Implemented Modules and Components
### Core Domain and Repository Contracts
Added:
- src/app/core/user-experience/models/user-experience.model.ts
- src/app/core/user-experience/repositories/user-experience.repository.ts
- src/app/core/user-experience/repositories/local-user-experience.repository.ts
- src/app/core/user-experience/user-experience-repository.token.ts
- src/app/facades/platform/user-experience.facade.ts
Purpose:
- Create domain entities: FavoriteItem, ComparedProduct, RecentlyViewedItem, SavedSearch, ContinueBrowsingState.
- Provide repository abstraction for future authenticated backend synchronization.
- Implement guest mode via localStorage repository.
### Bootstrap Config Extensions
Added:
- src/app/shared/models/config/user-experience-config.model.ts
Updated:
- src/app/shared/models/config/bootstrap-config.model.ts
- src/app/shared/models/config/index.ts
- src/assets/mock/bootstrap/bootstrap.json
New bootstrap block:
- userExperience.wishlist
- userExperience.compare
- userExperience.recentlyViewed
- userExperience.share
- userExperience.continueBrowsing
- userExperience.savedSearches
### Wishlist
Added:
- src/app/features/website/user-experience/wishlist/containers/wishlist-page.component.ts
- src/app/features/website/user-experience/wishlist/containers/wishlist-page.component.html
- src/app/features/website/user-experience/wishlist/containers/wishlist-page.component.scss
Integrated:
- Header badge + counter in header component.
- Route: /wishlist.
### Compare
Added:
- src/app/features/website/user-experience/compare/components/compare-table.component.ts
- src/app/features/website/user-experience/compare/components/compare-table.component.html
- src/app/features/website/user-experience/compare/components/compare-table.component.scss
- src/app/features/website/user-experience/compare/containers/compare-page.component.ts
- src/app/features/website/user-experience/compare/containers/compare-page.component.html
- src/app/features/website/user-experience/compare/containers/compare-page.component.scss
Integrated:
- Route: /compare.
- Configurable max items from bootstrap userExperience.compare.maxItems.
- Hide-identical and highlight-differences controls.
- Responsive table wrapper.
### Recently Viewed
Added:
- src/app/features/website/user-experience/components/recently-viewed-strip/recently-viewed-strip.component.ts
- src/app/features/website/user-experience/components/recently-viewed-strip/recently-viewed-strip.component.html
- src/app/features/website/user-experience/components/recently-viewed-strip/recently-viewed-strip.component.scss
- src/app/widgets/ui/recently-viewed-widget.component.ts
Integrated:
- Auto-tracking in product details container after product load.
- Widget compatibility via widget registry bootstrap + manifest.
Updated widget integration:
- src/app/widgets/ui/index.ts
- src/app/widgets/registry/widget-registry.bootstrap.service.ts
- src/app/widgets/resolvers/data-source-resolver.service.ts
- src/assets/mock/bootstrap/widget-manifest.json
### Continue Browsing + Saved Searches
Integrated in catalog container:
- Persist selected filters, sort, page, layout, and scroll position.
- Restore state automatically on entry when route has no explicit category/query.
- Save current search and reuse/delete saved searches.
Updated:
- src/app/features/website/catalog/containers/catalog-container.component.ts
- src/app/features/website/catalog/containers/catalog-container.component.html
- src/app/features/website/catalog/containers/catalog-container.component.scss
### Product Sharing and Product Card Actions
Added:
- src/app/features/website/user-experience/services/product-share.service.ts
Updated Product Card to reusable actions (without duplication):
- src/app/components/product-card/product-card.component.ts
- src/app/components/product-card/product-card.component.html
- src/app/components/product-card/product-card.component.scss
Updated catalog grid/result propagation:
- src/app/features/website/catalog/components/product-grid/product-grid.component.ts
- src/app/features/website/catalog/components/product-grid/product-grid.component.html
- src/app/features/website/catalog/components/search-results/search-results.component.ts
- src/app/features/website/catalog/components/search-results/search-results.component.html
### UI/Animation/Notification Enhancements
Added:
- src/app/features/website/user-experience/services/user-notification.service.ts
- src/app/features/website/user-experience/components/floating-notifications/floating-notifications.component.ts
- src/app/features/website/user-experience/components/floating-notifications/floating-notifications.component.html
- src/app/features/website/user-experience/components/floating-notifications/floating-notifications.component.scss
Integrated globally in app shell:
- src/app/app.ts
- src/app/app.html
Heart animation:
- Added active pulse animation in product card favorite action.
### Routes and Header
Updated:
- src/app/app.routes.ts (wishlist/compare routes)
- src/app/components/header/header.component.ts
- src/app/components/header/header.component.html
- src/app/components/header/header.component.scss
### Localization
Updated translation contract and locale dictionaries:
- src/app/i18n/translations.ts
- src/app/i18n/en.ts
- src/app/i18n/ru.ts
- src/app/i18n/hy.ts
## Documentation Updates
Updated to include Sprint 11 bootstrap and backend contract expectations:
- docs/platform/02-bootstrap-json-spec.md
- docs/platform/06-api-contracts.md
- docs/platform/13-backend-requirements.md
- docs/backend-platform/business-apis.md
## Validation
Executed:
- npm run build
- npm run arch:check:boundaries
- npm run arch:check:cycles
Results:
- Build: PASS (existing bundle budget warning remains)
- Architecture boundaries: PASS
- Circular dependencies: PASS
## Notes
- Guest mode is fully functional using local storage.
- Authenticated synchronization is prepared by repository contracts and can be implemented via alternate repository provider without changing feature UI components.
- Bootstrap remains configuration-only and contains no user/business lists for wishlist/compare/recently viewed/saved searches.

View File

@@ -1,112 +0,0 @@
# Sprint 9 - Product Engagement Report
## 1. Architecture Decisions
- Product engagement was implemented as a dedicated module under product feature: `src/app/features/website/product/engagement/components`.
- ProductFacade remained the single feature entry point for engagement flows:
- `loadRating(productID)`
- `loadReviews(productID, query)`
- `loadQuestions(productID, query)`
- `submitReview(productID, input)`
- `submitQuestion(productID, input)`
- No feature component uses `HttpClient` directly.
- Data flow remains layered:
- Feature Container/Components -> `ProductFacade` -> `ProductDataService` -> `ProductDataProvider` (`ApiProductDataProvider`) -> `ApiService`.
- Existing auth/payment/bootstrap initialization flows were not changed.
- Widget/section/bootstrap runtime architecture was preserved.
## 2. Reusable Components Added
### Product Engagement Atoms
- `StarsComponent`
- `StarSelectorComponent`
- `ProductTabsComponent`
- `RatingSummaryComponent`
### Reviews
- `ReviewFormComponent`
- `ReviewCardComponent`
- `ReviewListComponent` (includes pagination support)
### Questions
- `QuestionFormComponent`
- `QuestionCardComponent`
- `QuestionListComponent` (includes pagination + Ask Question toggle)
### Product Page Sections
- Existing reusable sections kept:
- Gallery, Product Info, Variant Selector, Delivery, Description, Related Products
- New/updated reusable sections:
- `ProductSpecificationsComponent`
- `ProductWarrantyComponent`
- Reviews section via `ReviewListComponent`
- Questions section via `QuestionListComponent`
## 3. Domain Models Added
File: `src/app/core/products/models/product-engagement.model.ts`
- `RatingSummary`
- `Review`
- `Question`
- `Answer`
- Supporting contracts:
- `RatingDistributionEntry`
- `EngagementListQuery`
- `EngagementListResult<T>`
- `SubmitReviewInput`
- `SubmitQuestionInput`
## 4. Bootstrap Additions (Feature Config Only)
Added new optional config model:
- `src/app/shared/models/config/product-page-config.model.ts`
Added to bootstrap contract:
- `BootstrapConfig.productPage?: ProductPageConfig`
Added to mock bootstrap example:
- `src/assets/mock/bootstrap/bootstrap.json` now contains `productPage` with:
- `rating.enabled`
- `reviews.enabled/pageSize/showSummary`
- `questions.enabled/pageSize`
- `tabs.enabled/items`
- `relatedProducts.enabled`
No review/question domain data was placed in bootstrap.
## 5. Backend Endpoints Expected
Prepared in docs/contracts as target backend API:
- `GET /products/{id}/rating`
- `GET /products/{id}/reviews`
- `GET /products/{id}/questions`
- `POST /products/{id}/reviews`
- `POST /products/{id}/questions`
Current runtime uses existing available mock/API item data mapping where applicable, while facade methods and contracts are already prepared for dedicated engagement endpoints.
## 6. Validation Results
### Build
- `npm run build` -> PASS
### Architecture Rules
- No `HttpClient` usage found in feature layer (`src/app/features/**`).
- No `environment.*` usage found in feature layer (`src/app/features/**`).
- No DTO imports found in feature layer (`src/app/features/**`, checked for `\bdto\b|\.dto`).
### Architecture Governance Scripts
- `npm run arch:check:boundaries` -> PASS
- `npm run arch:check:cycles` -> PASS (`No circular dependency found`)
## 7. Documentation Updated
Platform docs:
- `docs/platform/00-bootstrap-example.md`
- `docs/platform/02-bootstrap-json-spec.md`
- `docs/platform/06-api-contracts.md`
- `docs/platform/10-product-domain.md`
- `docs/platform/13-backend-requirements.md`
Backend docs:
- `docs/backend-platform/business-apis.md`
## 8. Future Extension Points
- Photo upload support in review form (placeholder is already present in UI).
- Backend-side moderation and verification flags for reviews/questions.
- Dedicated seller identity and accepted-answer workflow integration.
- Server-driven sorting/filtering for reviews and questions.
- Optional widgetized engagement blocks for dynamic page composition.

View File

@@ -1,79 +0,0 @@
# UI Composition Review — RC-Visual-02 Page Composition Audit
Audit of every live page against the finished design system. No redesign, no business-logic changes, no new features — layout/spacing/hierarchy/alignment/state-composition fixes only, reusing existing shared components (`src/app/shared/ui/*`).
Executed as four sequential commits on branch `B2B` (not pushed):
| Commit | Scope |
|---|---|
| `2e31e80` | Storefront |
| `4ebc15f` | Builder / Project Editor |
| `712a7b4` | Backoffice: Dashboard, Products, Categories, Orders |
| `63c9cee` | Backoffice: Transactions, Customers, Moderation (Reviews/Reports), Users, Monitoring, Analytics |
`npm run build` verified green after every commit. Baseline warning (pre-existing, unrelated to this work) remains: initial bundle 768.57 kB vs 700 kB budget.
## Pages audited
**Storefront** — Home, Catalog, Search, Product Details, Cart, Compare, Favorites/Wishlist, Static Pages (public renderer).
**Builder** — General, Branding, Theme, Homepage, Widgets, Navigation, Languages, Features, Preview, Static Pages/Content tab, Media Library, Save bar.
**Backoffice** — Dashboard, Products, Categories, Orders, Transactions, Customers (list + detail), Reviews/Moderation (list + reports queue + detail), Users, Monitoring, Analytics.
**Not built / out of scope** (confirmed via `app.routes.ts`, not assumed from stale docs):
- **Settings** — no `path: 'settings'` exists anywhere under the backoffice route block. Genuinely missing, not a placeholder page to polish.
- **Diagnostics** — exists as `__diagnostics`, excluded from production builds (`environment.production` guard), not a backoffice/admin page. Skipped.
- Note: `docs/ADMIN.md` (Sprint 19) is stale — it labels Transactions/Customers/Orders/Media as "coming soon" placeholders. All are actually live-routed to real, built components as of the current branch. Audited as real pages, not noted as placeholders.
- `src/app/features/backoffice/{categories,customers,inventory,orders,products,settings}` is dead/unrouted duplicate code, superseded by `src/app/features/admin/*`. Not deleted (deletion is a separate call), flagged for follow-up cleanup.
## Recurring problems found (same category, repeated across all three passes)
1. **Undefined CSS theme variables**`var(--x, #hexFallback)` referencing variable names that don't exist anywhere in `src/styles.scss` or theme files (`--danger-color`, `--brand-primary`, `--color-primary`, `--border-subtle`, `--surface-muted`, `--text-muted`, `--text-tertiary`). These silently resolved to their hardcoded hex fallback and never responded to tenant theming — the actual RC-relevant bug, not cosmetic. Fixed by remapping to real tokens (`--error-color`, `--primary-color`, `--border-color`, `--bg-tertiary`, `--text-secondary`, `--text-light`).
2. **Hand-rolled skeletons/spinners** instead of `app-skeleton`.
3. **Hand-rolled empty states** (`h1`/`h2`/`p`/`a`) instead of `app-empty-state`.
4. **Legacy global `.btn`/`.btn-primary`/`.btn-ghost`** (from `src/styles.scss`) running as a second parallel button system alongside `app-button`.
5. **Bare `<th>` with no `scope="col"`**, breaking screen-reader table navigation.
6. **Dead CSS** — most notably an entire unused `.alt` cart theme (~530 lines, never applied by the template) in `cart.component.scss`, which was the direct cause of that file's pre-existing 40kB budget overrun.
## Fixes applied by area
### Storefront (`2e31e80`)
- Catalog, Product Details, Static Pages: replaced hand-rolled loading/error/empty markup with `app-skeleton` / `app-empty-state` / `app-button`; moved hardcoded hex to theme vars.
- Cart: removed dead `.alt` theme CSS (2242 → 1157 lines); cart chunk 89.49 kB → 59.39 kB; budget warning eliminated; empty-cart state componentized.
- Compare, Wishlist: legacy `.btn` classes → `app-button`; hand-rolled empty states → `app-empty-state`.
- Product Details loading: added a gallery+info skeleton layout with `sr-only` status text (previously a bare CSS spinner with no accessible label).
### Builder (`4ebc15f`)
- Homepage/Widgets sections: added `app-empty-state` branches for the zero-content case (previously rendered nothing at all, no explanation).
- Navigation: added `aria-label` to icon-only move-up/move-down buttons.
- Static Pages tab: was using a dead CSS class (`.editor-section-card`, removed from the shared stylesheet after all other sections migrated to `app-section-card`) — silently missing all card chrome. Migrated to `app-section-card`.
- Save bar: every color was wired to nonexistent CSS variables (`--surface`, `--border`, `--warning`, `--danger`, `--muted-foreground`, `--info-bg`, `--info`) — fully disconnected from the theme. Remapped to real tokens.
- "Reset section" button: the one action button in the editor shell bypassing `app-button` — converted to `<app-button variant="danger" size="sm">`, dead custom CSS removed.
- `section.shared.scss` + 3 section files: bare hex / undefined `--danger-color` → real theme vars.
### Backoffice — Dashboard/Products/Categories/Orders (`712a7b4`)
- Confirmed `features/admin/*` is the live-routed implementation; `features/backoffice/*` (except `media` and `shared`) is dead duplicate code.
- 13 files: undefined theme vars → real tokens; `scope="col"` added to product/category/order tables.
- SEO-preview widget colors (Google SERP mimicry) and pill-radius fallback deliberately left as-is — intentional, not theme drift.
### Backoffice — Transactions/Customers/Moderation/Users/Monitoring/Analytics (`63c9cee`)
- Same undefined-var remap pattern across 9 more files, most notably `admin-analytics-page.component.scss` (`--color-primary` used repeatedly for tabs, chart bars, focus rings — never defined).
- `scope="col"` added across ~35 more `<th>` elements spanning transactions, customers, reviews, reports, users (2 tables), monitoring (2 tables), analytics (4 mini-tables).
- Confirmed no Settings route exists.
## Remaining recommendations (not applied — flagged for follow-up, not composition bugs)
- **Detail-page pattern inconsistency**: Order/Customer/Review detail pages all share the same pattern — hand-rolled `.card` divs and a plain `<p>{{ 'common.loading' | translate }}</p>` instead of `app-card`/`app-skeleton`. Left unchanged everywhere for consistency (fixing one would make it the odd one out among its siblings); recommend a single follow-up pass across all three detail pages together.
- **Bare `<select>` filters** (category/status/stock/sort/role/scope pickers) across Products, Orders, Users are not migrated to `app-select`. This was already a reviewed, deliberate decision per `docs/ADMIN.md`'s Sprint 28 notes (aria-labels present) — left as-is.
- **Catalog toolbar buttons** (`.catalog-reset-btn`, `.catalog-save-search-btn`, sort/grid sheet buttons) still hand-styled rather than `app-button` — carry active/selected-state styling not trivially portable; flagged as a larger follow-up.
- **Analytics `lowStockProducts` table** has no loading-skeleton branch while `topProducts` (same facade signal) does — minor inconsistency, not fixed to keep the diff surgical.
- **`SearchEmptyResultsComponent`** (`src/app/features/search/components/empty-results/`) is dead code, defined but never referenced — flagged for cleanup, not deleted (deletion is a separate decision).
- **`features/backoffice/*` dead duplicate code** (categories, customers, inventory, orders, products, settings subfolders) — superseded by `features/admin/*`, candidate for removal in a dedicated cleanup pass.
- **Responsive breakpoints**: all three passes worked from SCSS only, no browser available. Recommend a visual pass at mobile/tablet widths, particularly catalog's filter drawer/mobile toolbar and cart's swipe-to-delete interaction.
- **Payment modal spinners** (`.spinner-large`/`.spinner-small` in cart) intentionally left as CSS spinners rather than `app-skeleton` — appropriate for an in-progress action state, not a content-loading placeholder.
## Not audited
- **Settings** — no page exists to audit.
- **Diagnostics** — dev-only, excluded from production.

View File

@@ -1,192 +0,0 @@
# UI Design System Finalization — RC Final Polish
Scope: token infrastructure + mechanical, value-preserving normalization of
typography/radius/spacing across storefront, builder, and backoffice.
Architecture, routing, facades, providers, and business logic were left
untouched, per instructions. This pass builds on the icon standardization
work (see `docs/UI-ICON-AUDIT.md`) and the earlier storefront/admin polish
passes (`docs/STORE_FRONT_REVIEW.md`, `docs/ADMIN_REVIEW.md`) — it does not
redo that work.
No `docs/DESIGN.md` exists in this repo (confirmed in `docs/STORE_FRONT_REVIEW.md`);
`impeccable` hook findings referencing it were treated as inapplicable noise
throughout, same precedent as the prior pass.
## Before
- `src/styles.scss` had a color/shadow/radius/spacing token set
(`--primary-color`, `--radius-sm..xl`, `--space-xs..xl`, `--shadow-sm..lg`)
but **no typography scale at all** — no font-size, font-weight, or
line-height tokens.
- Font sizes were hand-written per component. A repo-wide scan found **24
distinct font-size values in simultaneous use** for what was clearly the
same handful of intended sizes: `0.7/0.72/0.75/0.78/0.8/0.8125/0.85/0.875/
0.9/0.9375/0.95/1/1.05/1.1/1.125/1.15/1.2/1.25/1.3/1.35/1.4/1.5/1.75/2rem`
(plus a handful of raw px values), spread across 104 of 136 component
SCSS files.
- Font-weight was always a raw literal (400/500/600/700), never a token.
- `border-radius` had the same drift: 72 of 136 files used raw px values
instead of the existing `--radius-*` tokens; two "shape" values in
constant use — `4px` (small chips/badges) and `999px` (pills/toggles) —
had no token at all, so every pill/chip component reinvented `999px` by
hand (41 occurrences) or `4px` (9 occurrences).
- `src/app/shared/ui/*` — the actual shared component library used across
admin/builder/backoffice (button, input, select, badge, card, table,
dialog, etc.) — was already internally consistent (button and input both
used a matching 32/40/48px sm/md/lg scale with 0.8125/0.9375/1rem font
sizes) but expressed every value as a hand-written literal rather than a
token, and one file (`section-card.component.scss`) had drifted from its
sibling `card.component.scss` (16px/18px/14px raw vs. `--radius-lg`/
`--space-*`).
- `code-editor.component.scss`'s focus state used a hardcoded `#497671`
(the dexar tenant's primary color) instead of `var(--primary-color)`
it would not have adapted if a tenant switched to the lavero or novo
theme.
## After
### Tokens introduced (`src/styles.scss`)
```
--font-size-xs: 0.75rem (12px)
--font-size-sm: 0.8125rem (13px)
--font-size-base: 0.875rem (14px)
--font-size-md: 0.9375rem (15px)
--font-size-lg: 1rem (16px)
--font-size-xl: 1.125rem (18px)
--font-size-2xl: 1.25rem (20px)
--font-size-3xl: 1.5rem (24px)
--font-size-4xl: 2rem (32px)
--font-weight-normal: 400
--font-weight-medium: 500
--font-weight-semibold: 600
--font-weight-bold: 700
--line-height-tight: 1.2
--line-height-normal: 1.5
--line-height-relaxed: 1.6
--space-2xl: 48px (new, extends the existing xs/sm/md/lg/xl scale)
--space-3xl: 64px (new)
```
### Tokens added to all three theme files (`src/styles/themes/*.theme.scss`)
```
--radius-xs: 4px (dexar/lavero/novo — chip/badge corner radius)
--radius-full: 999px (dexar/lavero/novo — pill/toggle shape)
```
The scale steps were chosen to match sizes already in wide use (button/input
sm|md|lg already used 0.8125/0.9375/1rem) so most components could adopt the
tokens by direct substitution rather than a visual redesign.
### Files touched, by phase
**Global base (`src/styles.scss`)**`body`, `h1``h6`, `p`, `small`, the
`.btn`/`.mt-*`/`.mb-*`/`.p-*` utility classes, and the shared
`.item-badge`/`.item-tag`/`.item-simple-desc` classes now reference the new
tokens instead of literals.
**Shared component library (18 files, hand-reviewed, not mechanical)**
`src/app/shared/ui/{button,input,select,badge,card,section-card,table,dialog,
empty-state,form-field,pagination,toggle,image-field,key-value-editor,
locale-tabs,color-picker,code-editor,skeleton}/*.component.scss`. Fixes made
along the way:
- `code-editor.component.scss`: hardcoded `#497671` focus color →
`var(--primary-color)` — now adapts correctly across tenant themes.
- `section-card.component.scss`: raw `16px`/`18px`/`14px` radius/padding →
`var(--radius-lg)`/`var(--space-*)`, matching the sibling `card` component.
- `toggle`/`badge`/`item-tag`: raw `999px` → the new `--radius-full` token.
**App-wide mechanical rollout (89 files touched across storefront, builder,
and backoffice)** — three scripted, value-preserving sweeps:
1. `font-size: <literal>;``var(--font-size-STEP, <same-or-nearest-step>);`
for every literal that matched (or was within ~0.03rem/1px of) one of the
9 scale steps. Values within that tolerance were snapped to the nearest
step to consolidate near-duplicates (e.g. `0.85rem` and `0.8rem` both →
`--font-size-sm`/`0.8125rem`).
2. `border-radius: <literal>;` → token, but **only** for the 5 values that
exactly match a token everywhere (`4px`, `8px`, `12px`, `13px`, `999px`).
`20px`/`16px`/`10px`/`6px`/`3px`/`2px`/`14px`/`24px`/`19px` were
deliberately left alone (see Remaining issues).
3. `font-weight: <literal>;` → token (400/500/600/700), value-preserving.
Each sweep was applied with `sed`, spot-checked against `git diff` for
correctness, then verified with a full `ng build --configuration=production`
(exit 0) before committing. Five commits total, one per phase/sweep, each
independently buildable and revertable.
## Remaining issues (deferred, not fixed in this pass)
- **Spacing (margin/padding) was not mechanically swept.** The existing
`--space-xs..3xl` scale covers gaps and simple paddings, but
margin/padding shorthand comes in 1/2/3/4-value forms that aren't safe to
regex-replace without risking silently changing box models on components
that use asymmetric shorthand. This needs a manual, component-by-component
pass — recommended as the next follow-up, using the same `--space-*`
scale already established.
- **`border-radius` values with no exact token match** (`20px` × 9,
`16px` × 7, `10px` × 38, `6px` × 9, `3px` × 4, `2px` × 3, `14px`, `24px`,
`19px`) were intentionally left as literals. Several of these (10px, 6px)
are common enough that a `--radius-2xs`/intermediate step might be
warranted, but adding one now without a design call would be inventing a
new shape rather than normalizing existing ones — flagged for the design
owner rather than decided unilaterally.
- **Phases 59 (cards, tables, status system, animations, accessibility)**
were not addressed as dedicated passes in this session. The shared/ui
`card`, `table`, `dialog`, `badge`, and `skeleton` components are already
reasonably unified (see "After" above) since they're single shared
components consumed everywhere, but a full per-surface audit (storefront
product cards, admin dashboard cards, builder panel cards; every table's
sticky-header/hover/selection/density behavior; a single status-badge
language for success/warning/error/draft/published/archived/pending) was
out of reach in this pass and should be scoped as its own follow-up.
- **Status system**: no unified success/warning/error/draft/published/etc.
visual language audit was performed. The `app-badge` component has
`--neutral/primary/success/warning/danger/info` variants already, but
whether every surface (storefront order status, admin order/review
status, builder publish state) actually uses `app-badge` rather than a
hand-rolled span was not verified in this pass.
- **Animations**: not audited in this pass beyond what already existed
(the codebase already has a global `prefers-reduced-motion` neutralizer
in `src/styles.scss` and per-component reduced-motion overrides in most
shared/ui components — this predates this session).
- **Accessibility**: not audited in this pass; the prior `STORE_FRONT_REVIEW.md`
and `UI-ICON-AUDIT.md` passes already covered a a11y pass on the storefront
and icon-only buttons respectively. No new a11y work was done here.
- **`cart.component.scss`** now sits ~771 bytes over its `anyComponentStyle`
budget warning (40kB) due to the added `var(...)` string length from the
font-weight sweep. This is a `maximumWarning`, not `maximumError`
(`angular.json` budgets: initial 700kB warn / 1.5MB error, component
style 40kB warn / 50kB error), so the build still exits 0 — but it's
worth either trimming the file or bumping its allotment slightly in a
follow-up.
- **Tenant brand colors** (`src/styles/themes/{dexar,lavero,novo}.theme.scss`)
and **badge accent colors** (`.item-badge.badge-new/sale/exclusive/...` and
code-editor syntax-highlight colors `.cm-*`) were intentionally left
untouched — these are per-tenant palettes / syntax-token colors, not
design-system drift.
## Recommendations
1. **Next pass: spacing.** Do a manual, per-surface sweep of
margin/padding using the existing `--space-xs..3xl` scale, prioritizing
the same high-traffic files already touched here (shared/ui, then
catalog/product-details/cart on storefront, then admin dashboard/list
pages).
2. **Decide on the missing radius steps** (`10px`, `6px`) with the design
owner — either add `--radius-2xs`/an intermediate token, or confirm they
should collapse into `--radius-xs`/`--radius-sm` and re-run the same
mechanical sweep pattern used in this session (`sed` + `ng build` +
commit per phase).
3. **Status system audit**: grep for hand-rolled status spans/badges
outside `app-badge` across admin (orders, reviews, moderation) and
storefront (order tracking) and consolidate onto the existing
`app-badge` variants.
4. **Cards/tables surface audit**: now that `card`/`section-card`/`table`
are internally token-consistent, verify every page-level "card" and
"table" actually composes these shared components rather than
hand-rolling similar-looking markup — this is the highest-leverage next
step for phases 56 since the primitives are already unified.

View File

@@ -1,110 +0,0 @@
# RC UI Sprint — Icon & Visual Language Audit
Full-application audit and consolidation onto **one icon library** (Lucide, via
`@lucide/angular`) across storefront, Marketplace Builder, and backoffice. Before this
pass the app mixed three icon systems in the same screens: **PrimeIcons** (`pi pi-*`
CSS classes), **dozens of hand-rolled inline `<svg>` blocks** (often the same icon
redrawn from scratch at different sizes/colors), and **bare text glyphs** (`♥`, `⇄`,
`«`, `»`) standing in for icons entirely outside any icon system.
## Foundation
- Added `@lucide/angular` (the maintained package; the unscoped `lucide-angular` is
deprecated upstream — installed the correct one directly).
- One shared entry point for every icon: `app-icon` (`src/app/shared/ui/icon/`),
backed by a canonical `name → Lucide icon` registry (`icon-registry.ts`). Default
size 20px, default stroke-width 2 — the same visual weight everywhere unless a
call site explicitly overrides it for a specific context (e.g. a 48px empty-state
illustration vs. a 14px inline chevron).
- `app-icon` is decorative (`aria-hidden`) by default. An `ariaLabel` input exists for
the rare case an icon carries meaning with no adjacent text; in practice, the label
almost always belongs on the surrounding interactive element instead.
## Old icon → New icon → Reason
| Area | Old | New | Reason |
|---|---|---|---|
| Admin dashboard | `pi-file-edit`, `pi-heart`, `pi-clock`, `pi-question-circle`, `pi-check-circle`, `pi-save`, `pi-upload`, `pi-book`, `pi-bolt`, `pi-flag`, `pi-megaphone`, `pi-circle` (data-driven via `icon: 'pi-*'` fields) | `edit`, `heart`, `clock`, `help`, `checkCircle`, `save`, `upload`, `book`, `zap`, `flag`, `megaphone`, `circle` | PrimeIcons → Lucide; icon fields retyped `AppIconName` so an unmapped name is a compile error, not a silently blank icon |
| Backoffice shell (sidebar nav + topbar) | `pi-home`, `pi-box`, `pi-tags`, `pi-shopping-cart`, `pi-user`, `pi-credit-card`, `pi-star`, `pi-chart-bar`, `pi-sitemap`, `pi-users`, `pi-cog`, `pi-desktop`, `pi-chart-line`, `pi-sign-out`, `pi-bars`, `pi-search`, `pi-upload`, `pi-building`, `pi-bell` | `home`, `package`, `tags`, `cart`, `user`, `creditCard`, `star`, `chartBar`, `network`, `users`, `settings`, `monitor`, `chartLine`, `logOut`, `menu`, `search`, `upload`, `building`, `bell` | PrimeIcons → Lucide |
| Marketplace Builder (overview, nav, sections, HTML editor toolbar) | `pi-arrow-left/right-circle/up/down`, `pi-check-circle`, `pi-circle`, `pi-circle-fill`, `pi-question-circle`, `pi-desktop`, `pi-shop`, `pi-image`, `pi-th-large`, `pi-box`, `pi-images`, `pi-history`, `pi-megaphone`, `pi-verified`, `pi-code`, `pi-globe`, `pi-sliders-h`, `pi-compass`, `pi-copy`, `pi-trash`, `pi-list`, `pi-link`, `pi-table`, `pi-minus`, `pi-video`, **`pi-bars` reused for drag handles** | `arrowLeft/Right/Up/Down`, `checkCircle`, `circle`, `help`, `monitor`, `store`, `image`, `layoutGrid`, `package`, `images`, `history`, `megaphone`, `verified`, `code`, `globe`, `slidersHorizontal`, `compass`, `copy`, `trash`, `list`, `link`, `table`, `minus`, `video`, **new dedicated `grip` icon** | PrimeIcons → Lucide. `pi-bars` had been reused for both the hamburger menu **and** every drag-to-reorder handle — two unrelated meanings sharing one icon, exactly the "duplicated icon, different meaning" pattern the audit brief calls out. Added `GripVertical` as a distinct drag-handle icon. |
| Header (storefront) | Two hand-drawn magnifying-glass SVGs (`#576463` desktop / `#1e3c38` mobile — same icon, two different hardcoded colors), `♥`/`⇄` **text glyphs** for wishlist/compare, hand-drawn cart SVG, hand-drawn home/catalog mobile-menu icons, 3× duplicated inline chevron SVG | `search` (color via `currentColor`), `heart`, `scale`, `cart`, `home`, `layoutGrid`, `chevronRight` | Consolidates one icon drawn twice with two different colors into one; replaces bare text characters (`♥`/`⇄`) that weren't part of any icon system at all |
| Search page | Same magnifying-glass path hand-duplicated **4 times** (input icon, empty-query, no-results, no-query states) at 3 sizes/3 colors | `search` ×4, color preserved per state via a CSS `color` property on the wrapper | Textbook duplication — one icon, four copy-pasted SVGs |
| Cart | Trash/X/plus/minus inline SVGs, standalone `EmptyCartIconComponent` (an 80px duplicate of the same cart glyph, only used once), chat-bubble "login gate" icon, duplicated close-X icon (2 modals), duplicated refresh icon (QR expired/error) | `trash`, `x`, `plus`, `minus`, `cart` (inline, component deleted), `lock`, `x`, `refresh` | Removed a whole component that existed only to duplicate an icon already available; unified the "login required" icon with telegram-login's identical icon (same concept, was drawn twice) |
| Language/region selectors | 3× duplicated chevron SVG (language dropdown, currency dropdown, region dropdown — identical path, copy-pasted), map-pin SVG, crosshair "locate" SVG, globe SVG | `chevronDown` ×3, `mapPin`, `locate`, `globe` | Same chevron redrawn three times in two components |
| Items carousel, category/subcategories, item-detail | Hand-drawn star (hardcoded `#497671` fill), hand-drawn cart icon (×3 separate redraws across 3 files), no-image/package/grid empty-state illustrations, check/X status icons, thumbs-up/down vote icons, dynamic fill/stroke rating stars | `star` (with new `color` input + `.dx-star--filled` CSS class for the solid/outline toggle), `cart`, `image`/`package`/`layoutGrid`, `check`/`x`, `thumbsUp`/`thumbsDown` | The cart icon alone had been hand-drawn from scratch in 5 different files across this audit (header, items-carousel, subcategories, item-detail, cart) — now one icon, one registry entry |
| Shared `app-select` | Browser-native `<select>` dropdown indicator (renders differently per browser, no relation to the icon system) | `chevronDown` (native indicator hidden via `appearance: none`) | "Selects: replace browser default indicators" — this component is used by nearly every admin form, so the fix applies everywhere at once |
| Shared `app-pagination` | `&laquo;` / `&raquo;` HTML entities | `chevronLeft` / `chevronRight` | "Pagination: use proper chevrons" |
| Every native `<details>`/`<summary>` expander (7 call sites: admin product form ×4, page editor, builder widget panel) | Browser-default disclosure triangle (differs Chrome/Firefox/Safari) | Lucide `ChevronDown` path drawn via one global CSS rule (`details > summary::after`), rotated 180° on `[open]` | Covers all 7 expanders with a single CSS change rather than touching each template — no shared class existed to hang a component-based fix on |
## Consistency improvements
- **One icon family everywhere.** Zero PrimeIcons (`pi-*`) class usages remain
anywhere in the app (verified by full-repo sweep after each batch). PrimeIcons
(`primeicons` package) is still installed as a PrimeNG peer dependency but no
longer used directly for any icon in app code.
- **One default size/stroke-width.** `app-icon` defaults to 20px / stroke-width 2;
every call site that deviates does so for a legible, deliberate reason (48px empty-
state illustration, 14px inline chevron), not arbitrary per-instance sizing.
- **Color via `currentColor`, not hardcoded hex.** Every hand-rolled SVG that baked a
specific hex into its `fill`/`stroke` attribute now inherits color from its
surrounding CSS `color`, so hover/active/disabled states that already change text
color also correctly recolor the icon — previously several icons ignored those
states entirely because their color was hardcoded in the SVG markup.
- **Duplicated icons resolved to one instance:**
- Magnifying glass: was hand-drawn independently in header (×2), search page (×4).
Now one `search` icon everywhere.
- Shopping cart: was hand-drawn independently in header, items-carousel,
subcategories, item-detail, and cart (via the now-deleted
`EmptyCartIconComponent`). Now one `cart` icon everywhere.
- Chevron/dropdown arrow: was hand-drawn independently in language-selector (×2),
region-selector, and the builder mobile nav (×3). Now one `chevronDown`/
`chevronRight` everywhere.
- "Login required" icon (chat-bubble shape): was drawn identically in both
telegram-login and cart's login gate. Now one `lock` icon in both, matching the
actual semantic ("authentication required") better than a chat bubble did.
- **Bare text glyphs replaced with real icons.** `♥` and `⇄` in the header were plain
Unicode characters, not part of any icon system, inconsistent stroke weight and
optical size versus every other icon on the same toolbar. Now `heart` and `scale`.
- **Corrected a genuine meaning collision**, not just a style one: PrimeIcons'
`pi-bars` (hamburger lines) was reused in the Marketplace Builder for both the
mobile menu toggle *and* every drag-to-reorder handle. A user scanning the builder
UI would see the same glyph mean "open navigation" in the header and "drag this
row" in a list — added a dedicated `grip` icon (`GripVertical`) so the two concepts
are now visually distinct.
- **Icon-only buttons audited for accessible names.** Swept every button whose only
content is an icon; found three relying on `title` alone (not reliably announced by
screen readers) or nothing at all (region-selector detect-location, carousel
add-to-cart, subcategories add-to-cart) and added `aria-label` to all three.
- **Decorative icons intentionally kept as custom SVG (not migrated):**
- The **Telegram brand logo** (cart, telegram-login) — a brand mark, not a generic
icon; replacing it with a generic Lucide icon would misrepresent the brand.
- **`layout-switcher`'s grid-pattern preview icons** — these show the actual layout
being selected (2-column, compact grid, list, etc.) as a literal visual preview,
not a stand-in for a word. Lucide has no equivalent "this exact grid pattern"
icon set; redrawing them as generic layout icons would lose the preview function.
## Remaining issues / backlog
- **No sortable table columns exist anywhere in the app** to add sort-direction
icons to (every admin list table has static, non-interactive `<th>` labels). Adding
actual column-sort interactivity would be new functionality, out of scope for an
icon/visual-language pass — flagged here rather than invented.
- **Bundle size**: `app-icon` renders each Lucide icon as its own standalone Angular
component (the current `@lucide/angular` API — no tree-shakeable "icon font" or
sprite sheet). The initial bundle grew from the prior build's already-over-budget
~585 KB over the 700 KB target to ~760 KB over, mostly from icon components now
bundled eagerly in the admin shell/dashboard/builder (loaded on every admin route).
Worth a follow-up pass to lazy-load icon-heavy admin sections if bundle size becomes
a concrete problem.
- **No `docs/DESIGN.md` exists** in this repo (confirmed across all three RC sprints
this session). The `impeccable` design-quality hook flagged pre-existing font-size/
radius/color values against a document that doesn't exist — none of those findings
were introduced by this pass; they're pre-existing values in files this pass
touched for unrelated reasons (colors, radii, spacing untouched).
- **PrimeIcons (`primeicons` npm package) is still installed** — it's a transitive
dependency PrimeNG components may rely on internally (calendar, carousel nav
arrows rendered by `p-carousel`, etc.), so it wasn't removed from `package.json`.
No app code imports `pi-*` classes directly anymore, but a future pass could audit
whether PrimeNG's own internal icon usage (e.g. `p-carousel`'s built-in prev/next
arrows) should also be re-skinned to match, or left as PrimeNG's own visual
language since those are framework-owned, not hand-authored.