fix(backoffice): release-candidate walkthrough fixes

- Admin Categories CRUD (create/edit/delete/reorder) silently failed
  end-to-end in local dev: ADMIN_CATEGORIES_GATEWAY resolved
  strategy.getBackofficeProviderMode(), which (unlike
  getBootstrapProviderMode()) has no localhost fallback, so it always
  picked AdminCategoriesApiGateway (real HTTP, 404s here) over the
  purpose-built AdminCategoriesLocalGateway mock. saveDraft()'s
  subscribe() has no error branch, so a create/publish click gave zero
  feedback: the category never saved, dirty stayed true forever, and
  the unsaved-changes guard then blocked navigation with no
  explanation. Live-verified end-to-end: created 3 categories, edited,
  reordered via the keyboard move-up/move-down buttons - all persist
  correctly now. Fixed by wiring the token to the category-specific
  strategy.getCategoryProviderMode() (was already defined, just never
  called) and giving it the same isLocalhost() mock fallback
  getBootstrapProviderMode() already uses. Production behavior
  (non-localhost) is unchanged - still resolves to the real API
  gateway.
- Categories list (tree/table/grid views) mislabeled its Edit button
  'Edit product' (adminProducts.edit) instead of 'Edit category' -
  copy-pasted the wrong existing i18n key; adminCategories.edit
  already exists with the correct translation in en/ru/hy. Not part
  of the tracked ~178-key missing-translation gap (docs/KNOWN-ISSUES.md) -
  this key exists and is simply wrong, not missing.

Verified live via browser walkthrough of every Backoffice route
(dashboard, products list/create/edit, categories list/create/edit/
reorder, orders list/detail, transactions list/detail+audit dialog,
customers list/detail, moderation list+reports queue, users, monitoring,
analytics, media library) at desktop and mobile widths. Console/network
noise from the mock backoffice API 404ing locally is pre-existing and
already documented (docs/ADMIN.md's prior bug-hunt audit pass) - not
re-reported. Product create/edit CRUD already worked end-to-end
(AdminProductsFacade injects its local gateway unconditionally, no
swappable-provider mistake there).

npx tsc --noEmit and npm run build both green (only the pre-existing
700kB initial-bundle budget warning, already tracked as out of scope).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-07-24 10:46:04 +04:00
parent 2cbb62a9cc
commit 907ac2cfe0
3 changed files with 12 additions and 3 deletions

View File

@@ -48,6 +48,15 @@ export class RuntimeProviderStrategyService {
return 'mock';
}
// Same rationale as getBootstrapProviderMode(): the admin Categories CRUD
// gateway has a real local-mock implementation (AdminCategoriesLocalGateway)
// built specifically so create/edit/delete/reorder can be exercised without
// a backend. Local dev has no reachable backoffice API, so fall back to it
// there while production keeps hitting the real API unconditionally.
if ((environment as any).useMockBootstrapOnLocal === true && this.isLocalhost()) {
return 'mock';
}
return 'api';
}
}

View File

@@ -125,7 +125,7 @@
@if (row.category.deletedAt) {
<app-button variant="secondary" size="sm" (click)="restore.emit(row.category.id)">{{ 'adminCategories.restore' | translate }}</app-button>
} @else {
<app-button variant="secondary" size="sm" (click)="edit.emit(row.category.id)">{{ 'adminProducts.edit' | translate }}</app-button>
<app-button variant="secondary" size="sm" (click)="edit.emit(row.category.id)">{{ 'adminCategories.edit' | translate }}</app-button>
<app-button variant="danger" size="sm" (click)="delete.emit(row.category.id)">{{ 'adminProducts.delete' | translate }}</app-button>
}
</div>
@@ -164,7 +164,7 @@
@if (category.deletedAt) {
<app-button variant="secondary" size="sm" (click)="restore.emit(category.id)">{{ 'adminCategories.restore' | translate }}</app-button>
} @else {
<app-button variant="secondary" size="sm" (click)="edit.emit(category.id)">{{ 'adminProducts.edit' | translate }}</app-button>
<app-button variant="secondary" size="sm" (click)="edit.emit(category.id)">{{ 'adminCategories.edit' | translate }}</app-button>
<app-button variant="danger" size="sm" (click)="delete.emit(category.id)">{{ 'adminProducts.delete' | translate }}</app-button>
}
</td>

View File

@@ -8,7 +8,7 @@ export const ADMIN_CATEGORIES_GATEWAY = new InjectionToken<AdminCategoriesGatewa
providedIn: 'root',
factory: () => {
const strategy = inject(RuntimeProviderStrategyService);
const mode = strategy.getBackofficeProviderMode();
const mode = strategy.getCategoryProviderMode();
return mode === 'mock' ? inject(AdminCategoriesLocalGateway) : inject(AdminCategoriesApiGateway);
}
});