2 Commits

Author SHA1 Message Date
sdarbinyan
357d346787 refactor: remove dead mock-mode branches in PRODUCT_DATA_PROVIDER/CATEGORY_REPOSITORY
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Both tokens switched on getProductProviderMode()/getCategoryProviderMode()
but every case (including 'mock') fell through to the same real API
provider - no mock implementation of either interface exists. Removed
the dead switch instead of leaving code that implies a mock mode which
was never built.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 11:18:14 +04:00
sdarbinyan
23d9f2f66f refactor: rename duplicate AdminRole interface, type sellerId as UUID
AdminRole was defined twice with unrelated shapes (core/auth's real
JWT role union vs. the Users admin page's display interface), flagged
in BACKEND-API-REFERENCE.md \u00a72b as needing a rename. Renamed the
Users-page one to AdminUserRoleRecord.

sellerId was bare string in admin-order/admin-product/item models
while core/sellers/models/seller-scope.model.ts already used the
shared UUID alias. Aligned all three to UUID for consistency (UUID is
currently just = string, so this is a documentation-level type change,
not a behavior change).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 11:16:16 +04:00
9 changed files with 32 additions and 38 deletions

View File

@@ -1,20 +1,13 @@
import { InjectionToken, inject } from '@angular/core'; import { InjectionToken, inject } from '@angular/core';
import { RuntimeProviderStrategyService } from '../providers/runtime-provider-strategy.service';
import { ApiCategoryRepository } from './repositories/api-category.repository'; import { ApiCategoryRepository } from './repositories/api-category.repository';
import { CategoryRepository } from './repositories/category.repository'; import { CategoryRepository } from './repositories/category.repository';
/**
* No mock CategoryRepository implementation exists - same dead branch as
* PRODUCT_DATA_PROVIDER. Always resolved to the real API repository
* regardless of getCategoryProviderMode(); removed the dead switch.
*/
export const CATEGORY_REPOSITORY = new InjectionToken<CategoryRepository>('CATEGORY_REPOSITORY', { export const CATEGORY_REPOSITORY = new InjectionToken<CategoryRepository>('CATEGORY_REPOSITORY', {
providedIn: 'root', providedIn: 'root',
factory: () => { factory: () => inject(ApiCategoryRepository)
const strategy = inject(RuntimeProviderStrategyService);
const apiRepository = inject(ApiCategoryRepository);
switch (strategy.getCategoryProviderMode()) {
case 'mock':
case 'remote-config':
case 'api':
default:
return apiRepository;
}
}
}); });

View File

@@ -1,20 +1,14 @@
import { InjectionToken, inject } from '@angular/core'; import { InjectionToken, inject } from '@angular/core';
import { RuntimeProviderStrategyService } from '../providers/runtime-provider-strategy.service';
import { ApiProductDataProvider } from './providers/api-product-data.provider'; import { ApiProductDataProvider } from './providers/api-product-data.provider';
import { ProductDataProvider } from './providers/product-data-provider.interface'; import { ProductDataProvider } from './providers/product-data-provider.interface';
/**
* No mock ProductDataProvider implementation exists - RuntimeProviderStrategyService.
* getProductProviderMode() can report 'mock', but there was never a branch that acted
* on it, so this always resolved to the real API provider regardless. Removed the dead
* switch instead of leaving code that implies a mock mode which doesn't exist.
*/
export const PRODUCT_DATA_PROVIDER = new InjectionToken<ProductDataProvider>('PRODUCT_DATA_PROVIDER', { export const PRODUCT_DATA_PROVIDER = new InjectionToken<ProductDataProvider>('PRODUCT_DATA_PROVIDER', {
providedIn: 'root', providedIn: 'root',
factory: () => { factory: () => inject(ApiProductDataProvider)
const strategy = inject(RuntimeProviderStrategyService);
const apiProvider = inject(ApiProductDataProvider);
switch (strategy.getProductProviderMode()) {
case 'mock':
case 'remote-config':
case 'api':
default:
return apiProvider;
}
}
}); });

View File

@@ -1,3 +1,5 @@
import { UUID } from '../../../../shared/types/primitive.types';
export type AdminOrderStatus = 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled' | 'refunded'; export type AdminOrderStatus = 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled' | 'refunded';
export type AdminOrderPaymentStatus = 'unpaid' | 'paid' | 'refund_requested' | 'refunded'; export type AdminOrderPaymentStatus = 'unpaid' | 'paid' | 'refund_requested' | 'refunded';
@@ -57,7 +59,7 @@ export interface AdminOrder {
* Absent means marketplace-owned, exactly like every order today - * Absent means marketplace-owned, exactly like every order today -
* nothing reads this field yet, no behavior change. * nothing reads this field yet, no behavior change.
*/ */
sellerId?: string; sellerId?: UUID;
} }
export interface AdminOrderListFilters { export interface AdminOrderListFilters {

View File

@@ -1,3 +1,5 @@
import { UUID } from '../../../../shared/types/primitive.types';
export type AdminProductStockStatus = 'in_stock' | 'low_stock' | 'out_of_stock'; export type AdminProductStockStatus = 'in_stock' | 'low_stock' | 'out_of_stock';
export type AdminProductSort = 'title' | 'price' | 'priority' | 'stock' | 'updated'; export type AdminProductSort = 'title' | 'price' | 'priority' | 'stock' | 'updated';
export type AdminProductEditorMode = 'create' | 'edit' | 'duplicate'; export type AdminProductEditorMode = 'create' | 'edit' | 'duplicate';
@@ -117,7 +119,7 @@ export interface AdminProduct {
* Absent means marketplace-owned, exactly like every product today - * Absent means marketplace-owned, exactly like every product today -
* nothing reads this field yet, nothing breaks by it being undefined. * nothing reads this field yet, nothing breaks by it being undefined.
*/ */
sellerId?: string; sellerId?: UUID;
} }
export interface AdminProductListFilters { export interface AdminProductListFilters {

View File

@@ -1,6 +1,6 @@
import { Injectable, inject, signal } from '@angular/core'; import { Injectable, inject, signal } from '@angular/core';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { AdminInvitation, AdminRole, AdminSession, AdminUser, AdminUserAuditEntry, AdminUserScope, AdminUserStatus } from '../models/admin-user.model'; import { AdminInvitation, AdminUserRoleRecord, AdminSession, AdminUser, AdminUserAuditEntry, AdminUserScope, AdminUserStatus } from '../models/admin-user.model';
import { AdminUsersLocalGateway } from '../services/admin-users-local.gateway'; import { AdminUsersLocalGateway } from '../services/admin-users-local.gateway';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@@ -8,7 +8,7 @@ export class AdminUsersFacade {
private readonly gateway = inject(AdminUsersLocalGateway); private readonly gateway = inject(AdminUsersLocalGateway);
readonly users = signal<AdminUser[]>([]); readonly users = signal<AdminUser[]>([]);
readonly roles = signal<AdminRole[]>([]); readonly roles = signal<AdminUserRoleRecord[]>([]);
readonly invitations = signal<AdminInvitation[]>([]); readonly invitations = signal<AdminInvitation[]>([]);
readonly loading = signal(false); readonly loading = signal(false);
readonly error = signal(false); readonly error = signal(false);

View File

@@ -2,7 +2,8 @@ export type AdminUserScope = 'marketplace' | 'office';
export type AdminUserStatus = 'active' | 'invited' | 'suspended'; export type AdminUserStatus = 'active' | 'invited' | 'suspended';
export type AdminInvitationStatus = 'pending' | 'accepted' | 'expired' | 'revoked'; export type AdminInvitationStatus = 'pending' | 'accepted' | 'expired' | 'revoked';
export interface AdminRole { /** Users-admin display/permissions shape - unrelated to core/auth/models/permission.model.ts's AdminRole (the real JWT/auth role union). */
export interface AdminUserRoleRecord {
id: string; id: string;
name: string; name: string;
permissions: string[]; permissions: string[];

View File

@@ -1,9 +1,9 @@
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { AdminInvitation, AdminRole, AdminSession, AdminUser, AdminUserAuditEntry, AdminUserScope, AdminUserStatus } from '../models/admin-user.model'; import { AdminInvitation, AdminUserRoleRecord, AdminSession, AdminUser, AdminUserAuditEntry, AdminUserScope, AdminUserStatus } from '../models/admin-user.model';
export interface AdminUsersGateway { export interface AdminUsersGateway {
loadUsers(): Observable<AdminUser[]>; loadUsers(): Observable<AdminUser[]>;
loadRoles(): Observable<AdminRole[]>; loadRoles(): Observable<AdminUserRoleRecord[]>;
loadInvitations(): Observable<AdminInvitation[]>; loadInvitations(): Observable<AdminInvitation[]>;
loadSessions(userId: string): Observable<AdminSession[]>; loadSessions(userId: string): Observable<AdminSession[]>;
loadAudit(userId: string): Observable<AdminUserAuditEntry[]>; loadAudit(userId: string): Observable<AdminUserAuditEntry[]>;

View File

@@ -1,11 +1,11 @@
import { Injectable, inject } from '@angular/core'; import { Injectable, inject } from '@angular/core';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators'; import { delay } from 'rxjs/operators';
import { AdminInvitation, AdminRole, AdminSession, AdminUser, AdminUserAuditEntry, AdminUserScope, AdminUserStatus } from '../models/admin-user.model'; import { AdminInvitation, AdminUserRoleRecord, AdminSession, AdminUser, AdminUserAuditEntry, AdminUserScope, AdminUserStatus } from '../models/admin-user.model';
import { AdminUsersGateway } from './admin-users-gateway.interface'; import { AdminUsersGateway } from './admin-users-gateway.interface';
import { AdminAuthService } from '../../../../core/admin-auth/admin-auth.service'; import { AdminAuthService } from '../../../../core/admin-auth/admin-auth.service';
const BUILT_IN_ROLES: AdminRole[] = [ const BUILT_IN_ROLES: AdminUserRoleRecord[] = [
{ id: 'owner', name: 'Owner', permissions: ['*'], builtIn: true }, { id: 'owner', name: 'Owner', permissions: ['*'], builtIn: true },
{ id: 'admin', name: 'Admin', permissions: ['products.manage', 'categories.manage', 'orders.manage', 'media.manage', 'users.manage'], builtIn: true }, { id: 'admin', name: 'Admin', permissions: ['products.manage', 'categories.manage', 'orders.manage', 'media.manage', 'users.manage'], builtIn: true },
{ id: 'editor', name: 'Editor', permissions: ['products.manage', 'categories.manage', 'media.manage'], builtIn: true }, { id: 'editor', name: 'Editor', permissions: ['products.manage', 'categories.manage', 'media.manage'], builtIn: true },
@@ -16,7 +16,7 @@ const BUILT_IN_ROLES: AdminRole[] = [
export class AdminUsersLocalGateway implements AdminUsersGateway { export class AdminUsersLocalGateway implements AdminUsersGateway {
private readonly adminAuth = inject(AdminAuthService); private readonly adminAuth = inject(AdminAuthService);
private users: AdminUser[] | null = null; private users: AdminUser[] | null = null;
private roles: AdminRole[] = [...BUILT_IN_ROLES]; private roles: AdminUserRoleRecord[] = [...BUILT_IN_ROLES];
private invitations: AdminInvitation[] = []; private invitations: AdminInvitation[] = [];
private sessions: Record<string, AdminSession[]> = {}; private sessions: Record<string, AdminSession[]> = {};
private audit: Record<string, AdminUserAuditEntry[]> = {}; private audit: Record<string, AdminUserAuditEntry[]> = {};
@@ -25,7 +25,7 @@ export class AdminUsersLocalGateway implements AdminUsersGateway {
return of(this.ensureUsers()).pipe(delay(50)); return of(this.ensureUsers()).pipe(delay(50));
} }
loadRoles(): Observable<AdminRole[]> { loadRoles(): Observable<AdminUserRoleRecord[]> {
return of(this.roles).pipe(delay(50)); return of(this.roles).pipe(delay(50));
} }

View File

@@ -1,3 +1,5 @@
import { UUID } from '../shared/types/primitive.types';
interface Photo { interface Photo {
photo?: string; photo?: string;
video?: string; video?: string;
@@ -182,7 +184,7 @@ export interface Item {
* Absent means marketplace-owned, exactly like every product today - * Absent means marketplace-owned, exactly like every product today -
* nothing reads this field yet, nothing breaks by it being undefined. * nothing reads this field yet, nothing breaks by it being undefined.
*/ */
sellerId?: string; sellerId?: UUID;
} }
export interface CartItem extends Item { export interface CartItem extends Item {