Files
marketplaces/docs/ADMIN.md
sdarbinyan 325dc17911 feat(admin): sprint 19 admin dashboard, routing, i18n
- Add admin dashboard feature (models/gateway/facade/components/page)
- Wire admin/products routes and backoffice coming-soon placeholders
- Add lastPublishedAt to ProjectEditorFacade/state
- Add dashboard i18n keys (en/ru/hy) and docs/ADMIN.md

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 12:21:33 +04:00

7.9 KiB

Marketplace Admin Dashboard - Sprint 19

Scope

Sprint 19 adds the production Admin Dashboard and makes it the default landing page for the admin area. It also wires the previously-unrouted admin/products feature and adds route placeholders for backoffice sections that don't have a feature built yet.

Routing

All admin routes live under /:lang/backoffice/** (app.routes.ts), guarded by the existing adminAuthGuard (core/admin-auth/admin-auth.guard.ts):

/:lang/backoffice            -> redirects to dashboard
/:lang/backoffice/dashboard  -> AdminDashboardPageComponent
/:lang/backoffice/products            -> AdminProductsListPageComponent
/:lang/backoffice/products/create     -> AdminProductEditorPageComponent
/:lang/backoffice/products/:id/edit   -> AdminProductEditorPageComponent
/:lang/backoffice/products/:id/duplicate -> AdminProductEditorPageComponent
/:lang/backoffice/categories    -> BackofficeComingSoonPageComponent
/:lang/backoffice/static-pages  -> BackofficeComingSoonPageComponent
/:lang/backoffice/transactions  -> BackofficeComingSoonPageComponent
/:lang/backoffice/orders        -> BackofficeComingSoonPageComponent
/:lang/backoffice/media         -> BackofficeComingSoonPageComponent

admin/products (features/admin/products/) was already fully implemented in an earlier sprint but was never wired into app.routes.ts and its internal navigation hardcoded the ru locale segment. Both are fixed in this sprint: routes are wired, and admin-products-list-page.component.ts / admin-product-editor-page.component.ts now build the locale segment from LanguageService.currentLanguage().

Dashboard as default admin page: on successful admin Telegram QR login, TelegramLoginComponent (mode="admin") navigates to /:lang/backoffice/dashboard (components/telegram-login/telegram-login.component.ts). The backoffice route's empty path also redirects to dashboard, so any bare /:lang/backoffice link lands there too.

Architecture

src/app/features/admin/dashboard/
  models/    admin-dashboard.model.ts
  services/  admin-dashboard-metrics.gateway.interface.ts
             admin-dashboard-metrics.local.gateway.ts
             admin-dashboard-metrics-gateway.token.ts
             admin-dashboard-history.service.ts
  facade/    admin-dashboard.facade.ts
  components/ admin-dashboard-card.component.*
              admin-dashboard-quick-actions.component.*
              admin-dashboard-activity.component.*
              admin-dashboard-health.component.*
  pages/     admin-dashboard-page.component.*

src/app/features/backoffice/shared/
  backoffice-coming-soon-page.component.*

Follows the existing container/facade/service split (ADR-006, ADR-007): AdminDashboardPageComponent is the container, AdminDashboardFacade owns orchestration, presentational card/quick-actions/activity/health components take only @Input()s and have no HttpClient/localStorage/route access.

Data sources (future-ready)

Cards never read ConfigService, localStorage, or an HTTP client directly - everything routes through AdminDashboardFacade, which composes:

  • ProjectEditorFacade (already existed) - bootstrap, status, lastSavedAt, lastPublishedAt (new, see below), validationIssues, homepageWidgets. Backs Marketplace Status, Project Name, Current Theme, Languages, Last Publish, Last Draft Save, Bootstrap Version, Active Layout, Enabled Widgets, and the System Health checks.
  • ADMIN_DASHBOARD_METRICS_GATEWAY (new InjectionToken, same swap pattern as BACKOFFICE_DATA_PROVIDER) - defaults to AdminDashboardMetricsLocalGateway, which composes BackofficeDataService.loadCategories()/loadProducts() (already used by AdminProductsLocalGateway) into counts. Backs Categories Count and Products Count. Swapping to a real dashboard-metrics endpoint later means implementing AdminDashboardMetricsGateway and rebinding the token - the facade and cards don't change.
  • AdminDashboardHistoryService (new) - localStorage-backed activity log, scoped per tenant, same pattern as ProjectEditorDraftStorageService. The facade appends an entry whenever lastSavedAt/lastPublishedAt change (detected via an effect(), primed on first read so the initial bootstrap load doesn't get logged as an activity event). Backs Recent Activity.

Orders / Revenue

No backend or local data model exists for orders or revenue anywhere in the codebase (features/backoffice/orders is an empty placeholder folder). These two cards render an honest pending-backend card state ("Awaiting backend integration") rather than fabricated numbers - not a "no data" empty state, since the gap is structural, not a temporarily-empty dataset.

Card states

AdminDashboardCardComponent (components/admin-dashboard-card.component.ts) renders one of: loading (skeleton), empty, error, pending-backend, or the ready value + optional subtitle. The container computes each card's status per data source (bootstrap not yet loaded -> loading; metrics gateway error -> error; no supported locales -> empty; Orders/Revenue -> always pending-backend).

System Health

ProjectValidator (features/project-editor/services/project-validator.service.ts) already covered 5 of the 6 required checks. This sprint added two more:

  • translationIssues() - flags a supported non-default locale missing a header nav label translation or a static-page translations entry.
  • layoutIssues() - flags bootstrap.layout.type or any section's layout.strategy that isn't one of the known enum values (PlatformLayoutType / SectionLayoutStrategy). Runtime validation matters here because bootstrap JSON isn't type-checked at load time.

Dashboard mapping (AdminDashboardFacade.healthChecks):

Dashboard label Validator code
Bootstrap valid structural: bootstrap !== null && schemaVersion set
Configuration valid no validation issues at all
Missing translations missing-translations (new)
Invalid colors invalid-colors (existing)
Invalid widget references missing-widget (existing - a homepage widget with no type)
Invalid layouts invalid-layouts (new)

Quick Actions

Static list in AdminDashboardFacade (route arrays relative to the lang root); the page component prefixes the current locale (LanguageService.currentLanguage()) before binding routerLink. Categories, Static Pages, Transactions, Orders, and Media Library currently land on BackofficeComingSoonPageComponent since those features aren't built yet - this is a routing placeholder, not a dashboard card placeholder.

lastPublishedAt (ProjectEditorFacade change)

Before this sprint, publish() only updated lastSavedAt, so "last draft save" and "last publish" were indistinguishable after a publish. Added lastPublishedAt: number | null to ProjectEditorState / ProjectEditorFacade, set only inside publish(). lastSavedAt behavior is unchanged (still updated by both save() and publish()).

Known gaps / backend needs

  • Dashboard metrics endpoint. Categories/Products counts are computed client-side from BackofficeDataService (itself mock/API-switchable via BACKOFFICE_DATA_PROVIDER). A dedicated /builder/dashboard/summary-style endpoint would let AdminDashboardMetricsGateway return richer data (real-time counts, trend deltas) without touching the facade or cards.
  • Orders/Revenue have no backend at all (see above) - needs an order domain and revenue aggregation before these cards can show real data.
  • Recent Activity is local-only, scoped to the browser/tenant via localStorage (adminDashboard.activityHistory.v1), same limitation as the existing draft-save local storage. It will not show another editor's activity until a real audit-log endpoint exists.
  • Admin authorization is still not enforced server-side (see Project-Editor.md - "Admin Authentication" section); this sprint does not change that. Nothing new here beyond routing/dashboard.