refactor(core): prepare frontend for backend integration

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

- Fix AdminLayoutComponent.readRouteData crash: leaf route snapshot/data is now optional with dashboard-title fallback, so admin deep links never crash the shell when route metadata is missing (root cause of every blocked browser test since Sprint 12)
- Never-settling promises fixed: all gateway ensureData() bridges (products, categories, moderation) and the catalog category resolver now resolve with an empty list on transport failure instead of hanging forever, so API outages surface as empty states with guidance rather than permanent skeletons plus global console errors
- Request de-duplication: BackofficeDataService caches products/categories with shareReplay - one in-flight request per endpoint shared by all consuming gateways (was 5+ duplicate requests per admin page load); failures clear the cache so the next call retries
- Gateway contract audit: all 8 admin gateways (products, categories, orders, customers/moderation, transactions, users, dashboard metrics, monitoring) now implement an explicit *Gateway interface - added the missing AdminMonitoringGateway; media already swaps via the abstract MediaRepository DI class
- Mock mode untouched: provider selection still flows through RuntimeProviderStrategyService/BACKOFFICE_DATA_PROVIDER
This commit is contained in:
sdarbinyan
2026-07-19 07:53:47 +04:00
parent e2ec8dc632
commit 16dec127c9
8 changed files with 73 additions and 10 deletions

View File

@@ -1,17 +1,46 @@
import { Injectable, inject } from '@angular/core';
import { Observable } from 'rxjs';
import { Observable, throwError } from 'rxjs';
import { catchError, shareReplay } from 'rxjs/operators';
import { ProductCardConfig, CategoryCardConfig } from '../../shared/models/ui';
import { BACKOFFICE_DATA_PROVIDER } from './backoffice-data-provider.token';
/**
* Single access point for backoffice catalog data. Requests are cached with
* shareReplay so the many gateways that read products/categories (products,
* categories, moderation, dashboard metrics) share one in-flight request per
* endpoint instead of each firing their own. A failed request clears the cache
* so the next call retries instead of replaying the error forever.
*/
@Injectable({ providedIn: 'root' })
export class BackofficeDataService {
private readonly provider = inject(BACKOFFICE_DATA_PROVIDER);
private products$: Observable<ProductCardConfig[]> | null = null;
private categories$: Observable<CategoryCardConfig[]> | null = null;
loadProducts(): Observable<ProductCardConfig[]> {
return this.provider.loadProducts();
if (!this.products$) {
this.products$ = this.provider.loadProducts().pipe(
catchError(error => {
this.products$ = null;
return throwError(() => error);
}),
shareReplay({ bufferSize: 1, refCount: false }),
);
}
return this.products$;
}
loadCategories(): Observable<CategoryCardConfig[]> {
return this.provider.loadCategories();
if (!this.categories$) {
this.categories$ = this.provider.loadCategories().pipe(
catchError(error => {
this.categories$ = null;
return throwError(() => error);
}),
shareReplay({ bufferSize: 1, refCount: false }),
);
}
return this.categories$;
}
}