arch(sprint1): decouple backoffice facade from http provider

This commit is contained in:
sdarbinyan
2026-07-03 02:01:01 +04:00
parent 95dbea7768
commit ac48799d9a
8 changed files with 75 additions and 54 deletions

View File

@@ -0,0 +1,8 @@
import { InjectionToken, inject } from '@angular/core';
import { BackofficeDataProvider } from './providers/backoffice-data-provider.interface';
import { MockBackofficeDataProvider } from './providers/mock-backoffice-data.provider';
export const BACKOFFICE_DATA_PROVIDER = new InjectionToken<BackofficeDataProvider>('BACKOFFICE_DATA_PROVIDER', {
providedIn: 'root',
factory: () => inject(MockBackofficeDataProvider)
});

View File

@@ -0,0 +1,17 @@
import { Injectable, inject } from '@angular/core';
import { Observable } from 'rxjs';
import { ProductCardConfig, CategoryCardConfig } from '../../shared/models/ui';
import { BACKOFFICE_DATA_PROVIDER } from './backoffice-data-provider.token';
@Injectable({ providedIn: 'root' })
export class BackofficeDataService {
private readonly provider = inject(BACKOFFICE_DATA_PROVIDER);
loadProducts(): Observable<ProductCardConfig[]> {
return this.provider.loadProducts();
}
loadCategories(): Observable<CategoryCardConfig[]> {
return this.provider.loadCategories();
}
}

View File

@@ -0,0 +1,18 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ProductCardConfig, CategoryCardConfig } from '../../../shared/models/ui';
import { BackofficeDataProvider } from './backoffice-data-provider.interface';
@Injectable({ providedIn: 'root' })
export class ApiBackofficeDataProvider implements BackofficeDataProvider {
constructor(private readonly http: HttpClient) {}
loadProducts(): Observable<ProductCardConfig[]> {
return this.http.get<ProductCardConfig[]>('/api/backoffice/products');
}
loadCategories(): Observable<CategoryCardConfig[]> {
return this.http.get<CategoryCardConfig[]>('/api/backoffice/categories');
}
}

View File

@@ -0,0 +1,7 @@
import { Observable } from 'rxjs';
import { ProductCardConfig, CategoryCardConfig } from '../../../shared/models/ui';
export interface BackofficeDataProvider {
loadProducts(): Observable<ProductCardConfig[]>;
loadCategories(): Observable<CategoryCardConfig[]>;
}

View File

@@ -0,0 +1,21 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ProductCardConfig, CategoryCardConfig } from '../../../shared/models/ui';
import { BackofficeDataProvider } from './backoffice-data-provider.interface';
@Injectable({ providedIn: 'root' })
export class MockBackofficeDataProvider implements BackofficeDataProvider {
private readonly productsUrl = '/assets/mock/backoffice/products/list.json';
private readonly categoriesUrl = '/assets/mock/backoffice/categories/list.json';
constructor(private readonly http: HttpClient) {}
loadProducts(): Observable<ProductCardConfig[]> {
return this.http.get<ProductCardConfig[]>(this.productsUrl);
}
loadCategories(): Observable<CategoryCardConfig[]> {
return this.http.get<CategoryCardConfig[]>(this.categoriesUrl);
}
}

View File

@@ -1,6 +1,6 @@
import { Injectable, signal } from '@angular/core'; import { Injectable, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ProductCardConfig, CategoryCardConfig } from '../../shared/models/ui'; import { ProductCardConfig, CategoryCardConfig } from '../../shared/models/ui';
import { BackofficeDataService } from '../../core/backoffice/backoffice-data.service';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class BackofficeFacade { export class BackofficeFacade {
@@ -8,12 +8,12 @@ export class BackofficeFacade {
readonly categories = signal<CategoryCardConfig[]>([]); readonly categories = signal<CategoryCardConfig[]>([]);
readonly loading = signal(false); readonly loading = signal(false);
constructor(private readonly http: HttpClient) {} constructor(private readonly backofficeDataService: BackofficeDataService) {}
load(): void { load(): void {
this.loading.set(true); this.loading.set(true);
this.http.get<ProductCardConfig[]>('/assets/mock/bootstrap/products.json').subscribe({ this.backofficeDataService.loadProducts().subscribe({
next: (products) => { next: (products) => {
this.products.set(products ?? []); this.products.set(products ?? []);
this.loadCategories(); this.loadCategories();
@@ -26,7 +26,7 @@ export class BackofficeFacade {
} }
private loadCategories(): void { private loadCategories(): void {
this.http.get<CategoryCardConfig[]>('/assets/mock/bootstrap/categories.json').subscribe({ this.backofficeDataService.loadCategories().subscribe({
next: (categories) => { next: (categories) => {
this.categories.set(categories ?? []); this.categories.set(categories ?? []);
this.loading.set(false); this.loading.set(false);

View File

@@ -1,16 +0,0 @@
[
{
"id": "cat-001",
"title": "Electronics",
"description": "Phones, laptops and accessories",
"imageUrl": "https://images.unsplash.com/photo-1498049794561-7780e7231661?w=400&h=300&fit=crop",
"itemsCount": 120
},
{
"id": "cat-002",
"title": "Clothing",
"description": "Fashion for everyone",
"imageUrl": "https://images.unsplash.com/photo-1441986300917-64674bd600d8?w=400&h=300&fit=crop",
"itemsCount": 80
}
]

View File

@@ -1,34 +0,0 @@
[
{
"id": "prod-001",
"sku": "SKU-001",
"title": "Demo Product 1",
"subtitle": "Platform product mock",
"imageUrl": "https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=600&h=400&fit=crop",
"badges": ["new"],
"tags": ["featured"],
"rating": 4.8,
"reviewsCount": 15,
"price": {
"amount": 149990,
"currency": "RUB",
"originalAmount": 169990
},
"stockStatus": "in_stock"
},
{
"id": "prod-002",
"sku": "SKU-002",
"title": "Demo Product 2",
"imageUrl": "https://images.unsplash.com/photo-1496181133206-80ce9b88a853?w=600&h=400&fit=crop",
"badges": ["sale"],
"tags": ["bestseller"],
"rating": 4.6,
"reviewsCount": 8,
"price": {
"amount": 89990,
"currency": "RUB"
},
"stockStatus": "low_stock"
}
]