docs: v3.1 gap analysis + delivery plan; add DI seams to 9 admin gateways
Adds InjectionToken + factory for Orders, Products, Users, Transactions, Monitoring, Moderation (mirrors existing Categories/Dashboard pattern) and repoints their facades plus the derived Analytics/Customers facades and admin-order-watcher off the mock LocalGateway class directly. No behavior change today - still resolves to the mock - but a real backend can now be bound per domain with zero facade edits. Docs: full gap analysis of Product Plan v3.1 against current repo state, and a phased delivery plan (10 phases, 34 sprints, 5 tracks) breaking every identified gap into scoped, sequenced work. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,10 +13,10 @@ import {
|
||||
AdminRecentActivityEntry,
|
||||
AdminRecommendationCard,
|
||||
} from '../models/admin-analytics.model';
|
||||
import { AdminOrdersLocalGateway } from '../../orders/services/admin-orders-local.gateway';
|
||||
import { AdminProductsLocalGateway } from '../../products/services/admin-products-local.gateway';
|
||||
import { ADMIN_ORDERS_GATEWAY } from '../../orders/services/admin-orders-gateway.token';
|
||||
import { ADMIN_PRODUCTS_GATEWAY } from '../../products/services/admin-products-gateway.token';
|
||||
import { ADMIN_CATEGORIES_GATEWAY } from '../../categories/services/admin-categories-gateway.token';
|
||||
import { AdminModerationLocalGateway } from '../../moderation/services/admin-moderation-local.gateway';
|
||||
import { ADMIN_MODERATION_GATEWAY } from '../../moderation/services/admin-moderation-gateway.token';
|
||||
import { AdminDashboardFacade } from '../../dashboard/facade/admin-dashboard.facade';
|
||||
import { AdminOrder } from '../../orders/models/admin-order.model';
|
||||
import { AdminProduct } from '../../products/models/admin-product.model';
|
||||
@@ -31,10 +31,10 @@ import { AdminReview } from '../../moderation/models/admin-review.model';
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AdminAnalyticsFacade {
|
||||
private readonly ordersGateway = inject(AdminOrdersLocalGateway);
|
||||
private readonly productsGateway = inject(AdminProductsLocalGateway);
|
||||
private readonly ordersGateway = inject(ADMIN_ORDERS_GATEWAY);
|
||||
private readonly productsGateway = inject(ADMIN_PRODUCTS_GATEWAY);
|
||||
private readonly categoriesGateway = inject(ADMIN_CATEGORIES_GATEWAY);
|
||||
private readonly moderationGateway = inject(AdminModerationLocalGateway);
|
||||
private readonly moderationGateway = inject(ADMIN_MODERATION_GATEWAY);
|
||||
private readonly dashboardFacade = inject(AdminDashboardFacade);
|
||||
|
||||
readonly dateRange = signal<AdminAnalyticsDateRange>(30);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Injectable, inject, signal } from '@angular/core';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { AdminOrdersLocalGateway } from '../../orders/services/admin-orders-local.gateway';
|
||||
import { ADMIN_ORDERS_GATEWAY } from '../../orders/services/admin-orders-gateway.token';
|
||||
import { AdminOrder } from '../../orders/models/admin-order.model';
|
||||
import { AdminCustomer } from '../models/admin-customer.model';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AdminCustomersFacade {
|
||||
private readonly ordersGateway = inject(AdminOrdersLocalGateway);
|
||||
private readonly ordersGateway = inject(ADMIN_ORDERS_GATEWAY);
|
||||
|
||||
readonly customers = signal<AdminCustomer[]>([]);
|
||||
readonly loading = signal(false);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Injectable, computed, inject, signal } from '@angular/core';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { AdminReview, AdminReviewListFilters, AdminReviewStatus } from '../models/admin-review.model';
|
||||
import { AdminReport, AdminReportStatus } from '../models/admin-report.model';
|
||||
import { AdminModerationLocalGateway } from '../services/admin-moderation-local.gateway';
|
||||
import { ADMIN_MODERATION_GATEWAY } from '../services/admin-moderation-gateway.token';
|
||||
import { LocalStorageService } from '../../../../core/storage/local-storage.service';
|
||||
|
||||
export type AdminModerationViewMode = 'table' | 'cards';
|
||||
@@ -38,7 +38,7 @@ const COLUMNS_KEY = 'admin-moderation:visible-columns';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AdminModerationFacade {
|
||||
private readonly gateway = inject(AdminModerationLocalGateway);
|
||||
private readonly gateway = inject(ADMIN_MODERATION_GATEWAY);
|
||||
private readonly localStorage = inject(LocalStorageService);
|
||||
|
||||
readonly filters = signal<AdminReviewListFilters>({ search: '', status: 'all', rating: 'all', page: 1, pageSize: 10 });
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { InjectionToken, inject } from '@angular/core';
|
||||
import { AdminModerationGateway } from './admin-moderation-gateway.interface';
|
||||
import { AdminModerationLocalGateway } from './admin-moderation-local.gateway';
|
||||
|
||||
/** Swap point for a real Moderation backend - see BACKEND-API-REFERENCE.md §8 (no seam existed before this token). */
|
||||
export const ADMIN_MODERATION_GATEWAY = new InjectionToken<AdminModerationGateway>('ADMIN_MODERATION_GATEWAY', {
|
||||
providedIn: 'root',
|
||||
factory: () => inject(AdminModerationLocalGateway),
|
||||
});
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Injectable, inject, signal } from '@angular/core';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { AdminMonitoringEvent, AdminMonitoringEventFilters, AdminQueue, AdminWebhookDelivery } from '../models/admin-monitoring.model';
|
||||
import { AdminMonitoringLocalGateway } from '../services/admin-monitoring-local.gateway';
|
||||
import { ADMIN_MONITORING_GATEWAY } from '../services/admin-monitoring-gateway.token';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AdminMonitoringFacade {
|
||||
private readonly gateway = inject(AdminMonitoringLocalGateway);
|
||||
private readonly gateway = inject(ADMIN_MONITORING_GATEWAY);
|
||||
|
||||
readonly filters = signal<AdminMonitoringEventFilters>({ category: 'all', search: '' });
|
||||
readonly events = signal<AdminMonitoringEvent[]>([]);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { InjectionToken, inject } from '@angular/core';
|
||||
import { AdminMonitoringGateway } from './admin-monitoring-gateway.interface';
|
||||
import { AdminMonitoringLocalGateway } from './admin-monitoring-local.gateway';
|
||||
|
||||
/** Swap point for a real Monitoring backend - see BACKEND-API-REFERENCE.md §8 (no seam existed before this token). */
|
||||
export const ADMIN_MONITORING_GATEWAY = new InjectionToken<AdminMonitoringGateway>('ADMIN_MONITORING_GATEWAY', {
|
||||
providedIn: 'root',
|
||||
factory: () => inject(AdminMonitoringLocalGateway),
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable, computed, inject, signal } from '@angular/core';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { AdminOrder, AdminOrderListFilters, AdminOrderStatus, TERMINAL_ORDER_STATUSES } from '../models/admin-order.model';
|
||||
import { AdminOrdersLocalGateway } from '../services/admin-orders-local.gateway';
|
||||
import { ADMIN_ORDERS_GATEWAY } from '../services/admin-orders-gateway.token';
|
||||
import { LocalStorageService } from '../../../../core/storage/local-storage.service';
|
||||
|
||||
export type AdminOrdersViewMode = 'table' | 'cards';
|
||||
@@ -30,7 +30,7 @@ const COLUMNS_KEY = 'admin-orders:visible-columns';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AdminOrdersFacade {
|
||||
private readonly gateway = inject(AdminOrdersLocalGateway);
|
||||
private readonly gateway = inject(ADMIN_ORDERS_GATEWAY);
|
||||
private readonly localStorage = inject(LocalStorageService);
|
||||
|
||||
readonly filters = signal<AdminOrderListFilters>({ search: '', status: 'all', page: 1, pageSize: 10 });
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { InjectionToken, inject } from '@angular/core';
|
||||
import { AdminOrdersGateway } from './admin-orders-gateway.interface';
|
||||
import { AdminOrdersLocalGateway } from './admin-orders-local.gateway';
|
||||
|
||||
/** Swap point for a real Orders backend - see BACKEND-API-REFERENCE.md §8 (no seam existed before this token). */
|
||||
export const ADMIN_ORDERS_GATEWAY = new InjectionToken<AdminOrdersGateway>('ADMIN_ORDERS_GATEWAY', {
|
||||
providedIn: 'root',
|
||||
factory: () => inject(AdminOrdersLocalGateway),
|
||||
});
|
||||
@@ -2,7 +2,7 @@ import { Injectable, computed, inject, signal } from '@angular/core';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { AdminProduct, AdminProductCategoryOption, AdminProductEditorMode, AdminProductListFilters } from '../models/admin-product.model';
|
||||
import { AdminProductsFormFactory } from '../services/admin-products-form.factory';
|
||||
import { AdminProductsLocalGateway } from '../services/admin-products-local.gateway';
|
||||
import { ADMIN_PRODUCTS_GATEWAY } from '../services/admin-products-gateway.token';
|
||||
import { ProjectEditorFacade } from '../../../project-editor/facade/project-editor.facade';
|
||||
import { LocalStorageService } from '../../../../core/storage/local-storage.service';
|
||||
|
||||
@@ -41,7 +41,7 @@ export type AdminProductColumn = typeof ALL_PRODUCT_COLUMNS[number];
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AdminProductsFacade {
|
||||
private readonly gateway = inject(AdminProductsLocalGateway);
|
||||
private readonly gateway = inject(ADMIN_PRODUCTS_GATEWAY);
|
||||
private readonly formFactory = inject(AdminProductsFormFactory);
|
||||
private readonly projectEditor = inject(ProjectEditorFacade);
|
||||
private readonly localStorage = inject(LocalStorageService);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { InjectionToken, inject } from '@angular/core';
|
||||
import { AdminProductsGateway } from './admin-products-gateway.interface';
|
||||
import { AdminProductsLocalGateway } from './admin-products-local.gateway';
|
||||
|
||||
/** Swap point for a real Products backend - see BACKEND-API-REFERENCE.md §8 (no seam existed before this token). */
|
||||
export const ADMIN_PRODUCTS_GATEWAY = new InjectionToken<AdminProductsGateway>('ADMIN_PRODUCTS_GATEWAY', {
|
||||
providedIn: 'root',
|
||||
factory: () => inject(AdminProductsLocalGateway),
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Injectable, Signal, computed, effect, inject, signal } from '@angular/core';
|
||||
import { AdminOrder } from '../../orders/models/admin-order.model';
|
||||
import { AdminOrdersLocalGateway } from '../../orders/services/admin-orders-local.gateway';
|
||||
import { ADMIN_ORDERS_GATEWAY } from '../../orders/services/admin-orders-gateway.token';
|
||||
import { LocalStorageService } from '../../../../core/storage/local-storage.service';
|
||||
import { UserNotificationService } from '../../../website/user-experience/services/user-notification.service';
|
||||
import { LanguageService } from '../../../../services/language.service';
|
||||
@@ -19,7 +19,7 @@ const TOAST_DURATION_MS = 4000;
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AdminOrderWatcherService {
|
||||
private readonly gateway = inject(AdminOrdersLocalGateway);
|
||||
private readonly gateway = inject(ADMIN_ORDERS_GATEWAY);
|
||||
private readonly storage = inject(LocalStorageService);
|
||||
private readonly notifications = inject(UserNotificationService);
|
||||
private readonly languageService = inject(LanguageService);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Injectable, inject, signal } from '@angular/core';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { AdminTransaction, AdminTransactionListFilters } from '../models/admin-transaction.model';
|
||||
import { AdminTransactionsLocalGateway } from '../services/admin-transactions-local.gateway';
|
||||
import { ADMIN_TRANSACTIONS_GATEWAY } from '../services/admin-transactions-gateway.token';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AdminTransactionsFacade {
|
||||
private readonly gateway = inject(AdminTransactionsLocalGateway);
|
||||
private readonly gateway = inject(ADMIN_TRANSACTIONS_GATEWAY);
|
||||
|
||||
readonly filters = signal<AdminTransactionListFilters>({ search: '', status: 'all', type: 'all', page: 1, pageSize: 10 });
|
||||
readonly transactions = signal<AdminTransaction[]>([]);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { InjectionToken, inject } from '@angular/core';
|
||||
import { AdminTransactionsGateway } from './admin-transactions-gateway.interface';
|
||||
import { AdminTransactionsLocalGateway } from './admin-transactions-local.gateway';
|
||||
|
||||
/** Swap point for a real Transactions backend - see BACKEND-API-REFERENCE.md §8 (no seam existed before this token). */
|
||||
export const ADMIN_TRANSACTIONS_GATEWAY = new InjectionToken<AdminTransactionsGateway>('ADMIN_TRANSACTIONS_GATEWAY', {
|
||||
providedIn: 'root',
|
||||
factory: () => inject(AdminTransactionsLocalGateway),
|
||||
});
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Injectable, inject, signal } from '@angular/core';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { AdminInvitation, AdminUserRoleRecord, AdminSession, AdminUser, AdminUserAuditEntry, AdminUserScope, AdminUserStatus } from '../models/admin-user.model';
|
||||
import { AdminUsersLocalGateway } from '../services/admin-users-local.gateway';
|
||||
import { ADMIN_USERS_GATEWAY } from '../services/admin-users-gateway.token';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AdminUsersFacade {
|
||||
private readonly gateway = inject(AdminUsersLocalGateway);
|
||||
private readonly gateway = inject(ADMIN_USERS_GATEWAY);
|
||||
|
||||
readonly users = signal<AdminUser[]>([]);
|
||||
readonly roles = signal<AdminUserRoleRecord[]>([]);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { InjectionToken, inject } from '@angular/core';
|
||||
import { AdminUsersGateway } from './admin-users-gateway.interface';
|
||||
import { AdminUsersLocalGateway } from './admin-users-local.gateway';
|
||||
|
||||
/** Swap point for a real Users backend - see BACKEND-API-REFERENCE.md §8 (no seam existed before this token). */
|
||||
export const ADMIN_USERS_GATEWAY = new InjectionToken<AdminUsersGateway>('ADMIN_USERS_GATEWAY', {
|
||||
providedIn: 'root',
|
||||
factory: () => inject(AdminUsersLocalGateway),
|
||||
});
|
||||
Reference in New Issue
Block a user