From e927a53029981965dc5860fde70bac1cc1de8a64 Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Fri, 3 Jul 2026 01:41:37 +0400 Subject: [PATCH] phase-10: add backoffice sandbox with mock business domain management --- src/app/app.routes.ts | 4 + .../facades/backoffice/backoffice.facade.ts | 40 +++++++++ .../backoffice-dashboard.component.ts | 86 +++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 src/app/facades/backoffice/backoffice.facade.ts create mode 100644 src/app/pages/backoffice/backoffice-dashboard.component.ts diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 2fde283..4842afe 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -35,6 +35,10 @@ const coreRoutes: Routes = [ { path: 'builder-sandbox', loadComponent: () => import('./pages/builder/builder-sandbox.component').then(m => m.BuilderSandboxComponent) + }, + { + path: 'backoffice-sandbox', + loadComponent: () => import('./pages/backoffice/backoffice-dashboard.component').then(m => m.BackofficeDashboardComponent) } ]; diff --git a/src/app/facades/backoffice/backoffice.facade.ts b/src/app/facades/backoffice/backoffice.facade.ts new file mode 100644 index 0000000..44fa4b9 --- /dev/null +++ b/src/app/facades/backoffice/backoffice.facade.ts @@ -0,0 +1,40 @@ +import { Injectable, signal } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { ProductCardConfig, CategoryCardConfig } from '../../shared/models/ui'; + +@Injectable({ providedIn: 'root' }) +export class BackofficeFacade { + readonly products = signal([]); + readonly categories = signal([]); + readonly loading = signal(false); + + constructor(private readonly http: HttpClient) {} + + load(): void { + this.loading.set(true); + + this.http.get('/assets/mock/bootstrap/products.json').subscribe({ + next: (products) => { + this.products.set(products ?? []); + this.loadCategories(); + }, + error: () => { + this.products.set([]); + this.loadCategories(); + } + }); + } + + private loadCategories(): void { + this.http.get('/assets/mock/bootstrap/categories.json').subscribe({ + next: (categories) => { + this.categories.set(categories ?? []); + this.loading.set(false); + }, + error: () => { + this.categories.set([]); + this.loading.set(false); + } + }); + } +} diff --git a/src/app/pages/backoffice/backoffice-dashboard.component.ts b/src/app/pages/backoffice/backoffice-dashboard.component.ts new file mode 100644 index 0000000..80a6dd2 --- /dev/null +++ b/src/app/pages/backoffice/backoffice-dashboard.component.ts @@ -0,0 +1,86 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { BackofficeFacade } from '../../facades/backoffice/backoffice.facade'; + +@Component({ + selector: 'app-backoffice-dashboard', + standalone: true, + imports: [CommonModule], + template: ` +
+

Backoffice Sandbox

+

Business data management domain (mocked for platform foundation).

+ + @if (loading()) { +
Loading backoffice data...
+ } @else { +
+

Products

+ + + + + + + + + + + @for (product of products(); track product.id) { + + + + + + + } + +
SKUTitlePriceStatus
{{ product.sku }}{{ product.title }}{{ product.price.amount }} {{ product.price.currency }}{{ product.stockStatus ?? 'n/a' }}
+
+ +
+

Categories

+
    + @for (category of categories(); track category.id) { +
  • {{ category.title }} ({{ category.itemsCount ?? 0 }})
  • + } +
+
+ +
+

Other Domains

+
+ Orders + Customers + Inventory + Media + Settings +
+
+ } +
+ `, + styles: [ + ` + .backoffice-dashboard { padding: 1.5rem; display: grid; gap: 1rem; } + .card { border: 1px solid var(--border-color, #d3dad9); border-radius: 12px; padding: 1rem; background: #fff; } + table { width: 100%; border-collapse: collapse; } + th, td { text-align: left; padding: 0.5rem; border-bottom: 1px solid #ececec; } + ul { margin: 0; padding-left: 1rem; } + .chips { display: flex; gap: 0.5rem; flex-wrap: wrap; } + .chips span { padding: 0.25rem 0.5rem; border-radius: 999px; background: var(--bg-secondary, #f5f5f5); } + ` + ], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class BackofficeDashboardComponent { + private readonly facade = inject(BackofficeFacade); + + readonly products = this.facade.products; + readonly categories = this.facade.categories; + readonly loading = this.facade.loading; + + constructor() { + this.facade.load(); + } +}