From fd5a436220f0621480650c49fe012e4b427e3cfb Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Mon, 20 Jul 2026 01:02:36 +0400 Subject: [PATCH] api doc --- docs/AUDIT/PROJECT-STATE.md | 706 ++++++++++++++++++ docs/backend/REMAINING-BACKEND-WORK.md | 75 ++ .../admin-auth-headers.interceptor.ts | 10 +- .../facade/admin-analytics.facade.ts | 4 +- .../facade/admin-categories.facade.ts | 4 +- .../services/admin-categories-api.gateway.ts | 66 ++ .../admin-categories-gateway.token.ts | 14 + src/app/pages/cart/cart.component.ts | 33 +- src/app/services/api.service.ts | 24 + src/environments/environment.production.ts | 2 +- src/environments/environment.ts | 2 +- 11 files changed, 930 insertions(+), 10 deletions(-) create mode 100644 docs/AUDIT/PROJECT-STATE.md create mode 100644 docs/backend/REMAINING-BACKEND-WORK.md create mode 100644 src/app/features/admin/categories/services/admin-categories-api.gateway.ts create mode 100644 src/app/features/admin/categories/services/admin-categories-gateway.token.ts diff --git a/docs/AUDIT/PROJECT-STATE.md b/docs/AUDIT/PROJECT-STATE.md new file mode 100644 index 0000000..5a9b700 --- /dev/null +++ b/docs/AUDIT/PROJECT-STATE.md @@ -0,0 +1,706 @@ +# 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//*.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 `` 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.` and `userExperience..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 +- `` lazy-loading: only 17 of 35 template `` 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 `` 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 `/{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 `