fix(admin): read tenant supportedLocales instead of hardcoding en/ru/hy translation tabs
Bug: admin-product-form.component.html and admin-category-form.component.html
both `@for (locale of ['en','ru','hy']; ...)` over a fixed literal array
instead of the tenant's actual configured locales. A tenant with fewer,
more, or differently-ordered supported locales got translation tabs for
languages it doesn't support and none for ones it does - same class of
bug as the already-fixed general-section/LocaleSyncService gap in
project-editor (docs/EDITOR.md), just never wired here at all.
Fix: AdminProductsFacade and AdminCategoriesFacade each gained a
`supportedLocales` computed (reads ProjectEditorFacade.bootstrap()
.localization.supportedLocales, falling back to ['en'] before bootstrap
loads) and an `ensureLocalesLoaded()` that calls
ProjectEditorFacade.loadBootstrap() if it hasn't loaded yet - same
lazy-load pattern AdminDashboardFacade.ensureLoaded() already uses for
the same dependency. Both editor page components call
ensureLocalesLoaded() in their constructor and pass
`[locales]="facade.supportedLocales()"` down to the form components,
which now expose a `locales: string[]` @Input() and iterate that
instead of the hardcoded array.
Verified live via window.ng.getComponent() on
/ru/backoffice/{categories,products}/create?devBypassAdmin=true: both
facade.supportedLocales() and the form's bound `locales` input now
read the real tenant order ['ru','en','hy'] (default locale first, as
configured) instead of the previous hardcoded ['en','ru','hy'] -
confirmed by the rendered translation-tab order changing accordingly
in both admin/products and admin/categories editors. tsc --noEmit
clean.
This commit is contained in:
@@ -38,7 +38,7 @@
|
||||
</app-form-field>
|
||||
|
||||
<h3>{{ 'adminProducts.translations' | translate }}</h3>
|
||||
@for (locale of ['en','ru','hy']; track locale) {
|
||||
@for (locale of locales; track locale) {
|
||||
<div class="grid two sub-block">
|
||||
<app-form-field [label]="(('adminCategories.title' | translate) + ' ' + locale)">
|
||||
<app-input [ngModel]="category.translations[locale]?.title || ''" (ngModelChange)="updateTranslation(locale, 'title', $event)" />
|
||||
|
||||
@@ -21,6 +21,7 @@ export class AdminCategoryFormComponent {
|
||||
@Input() parentOptions: AdminCategory[] = [];
|
||||
@Input() breadcrumb: string[] = [];
|
||||
@Input() slugTaken = false;
|
||||
@Input() locales: string[] = ['en'];
|
||||
@Input() mode: 'create' | 'edit' = 'create';
|
||||
|
||||
@Output() categoryChange = new EventEmitter<Partial<AdminCategory>>();
|
||||
|
||||
@@ -4,6 +4,7 @@ import { AdminCategory, AdminCategoryEditorMode, AdminCategoryListFilters } from
|
||||
import { AdminCategoriesFormFactory } from '../services/admin-categories-form.factory';
|
||||
import { AdminCategoriesLocalGateway } from '../services/admin-categories-local.gateway';
|
||||
import { LocalStorageService } from '../../../../core/storage/local-storage.service';
|
||||
import { ProjectEditorFacade } from '../../../project-editor/facade/project-editor.facade';
|
||||
|
||||
const DRAFT_KEY_PREFIX = 'admin-category-draft:';
|
||||
const NEW_CATEGORY_DRAFT_KEY = `${DRAFT_KEY_PREFIX}new`;
|
||||
@@ -13,6 +14,15 @@ export class AdminCategoriesFacade {
|
||||
private readonly gateway = inject(AdminCategoriesLocalGateway);
|
||||
private readonly formFactory = inject(AdminCategoriesFormFactory);
|
||||
private readonly localStorage = inject(LocalStorageService);
|
||||
private readonly projectEditor = inject(ProjectEditorFacade);
|
||||
|
||||
readonly supportedLocales = computed(() => this.projectEditor.bootstrap()?.localization.supportedLocales ?? ['en']);
|
||||
|
||||
ensureLocalesLoaded(): void {
|
||||
if (!this.projectEditor.bootstrap()) {
|
||||
this.projectEditor.loadBootstrap();
|
||||
}
|
||||
}
|
||||
|
||||
readonly filters = signal<AdminCategoryListFilters>({ search: '', visibility: 'all', includeDeleted: false });
|
||||
readonly categories = signal<AdminCategory[]>([]);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { LanguageService } from '../../../../services/language.service';
|
||||
selector: 'app-admin-category-editor-page',
|
||||
standalone: true,
|
||||
imports: [AdminCategoryFormComponent, TranslatePipe],
|
||||
template: `@if (facade.draft(); as draft) {<main class="editor-page"><header><h1>{{ title() | translate }}</h1></header><app-admin-category-form [category]="draft" [parentOptions]="parentOptions()" [breadcrumb]="facade.breadcrumbFor(draft.parentId)" [slugTaken]="facade.slugTaken()" [mode]="facade.editorMode()" (categoryChange)="facade.updateDraft($event)" (saveDraft)="save(false)" (publish)="save(true)" /></main>} @else {<main class="editor-page"><p>{{ 'common.loading' | translate }}</p></main>}`,
|
||||
template: `@if (facade.draft(); as draft) {<main class="editor-page"><header><h1>{{ title() | translate }}</h1></header><app-admin-category-form [category]="draft" [parentOptions]="parentOptions()" [breadcrumb]="facade.breadcrumbFor(draft.parentId)" [slugTaken]="facade.slugTaken()" [locales]="facade.supportedLocales()" [mode]="facade.editorMode()" (categoryChange)="facade.updateDraft($event)" (saveDraft)="save(false)" (publish)="save(true)" /></main>} @else {<main class="editor-page"><p>{{ 'common.loading' | translate }}</p></main>}`,
|
||||
styles: [`.editor-page { max-width: 1120px; margin: 0 auto; padding: 24px; display: grid; gap: 16px; } .editor-page h1, .editor-page p { margin: 0; }`],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
@@ -32,6 +32,7 @@ export class AdminCategoryEditorPageComponent {
|
||||
if (this.facade.categories().length === 0) {
|
||||
this.facade.loadList();
|
||||
}
|
||||
this.facade.ensureLocalesLoaded();
|
||||
if (!id) {
|
||||
this.facade.startCreate();
|
||||
} else {
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
</div>
|
||||
|
||||
<h3>{{ 'adminProducts.translations' | translate }}</h3>
|
||||
@for (locale of ['en','ru','hy']; track locale) {
|
||||
@for (locale of locales; track locale) {
|
||||
<div class="grid two sub-block">
|
||||
<app-form-field [label]="(('adminProducts.name' | translate) + ' ' + locale)">
|
||||
<app-input [ngModel]="product.translations[locale]?.name || ''" (ngModelChange)="updateTranslation(locale, 'name', $event)" />
|
||||
|
||||
@@ -20,6 +20,7 @@ export class AdminProductFormComponent {
|
||||
@Input({ required: true }) product!: AdminProduct;
|
||||
@Input() categories: AdminProductCategoryOption[] = [];
|
||||
@Input() allProducts: AdminProduct[] = [];
|
||||
@Input() locales: string[] = ['en'];
|
||||
@Input() mode: 'create' | 'edit' | 'duplicate' = 'create';
|
||||
|
||||
protected mediaPickerOpen = false;
|
||||
|
||||
@@ -3,11 +3,21 @@ 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 { ProjectEditorFacade } from '../../../project-editor/facade/project-editor.facade';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AdminProductsFacade {
|
||||
private readonly gateway = inject(AdminProductsLocalGateway);
|
||||
private readonly formFactory = inject(AdminProductsFormFactory);
|
||||
private readonly projectEditor = inject(ProjectEditorFacade);
|
||||
|
||||
readonly supportedLocales = computed(() => this.projectEditor.bootstrap()?.localization.supportedLocales ?? ['en']);
|
||||
|
||||
ensureLocalesLoaded(): void {
|
||||
if (!this.projectEditor.bootstrap()) {
|
||||
this.projectEditor.loadBootstrap();
|
||||
}
|
||||
}
|
||||
|
||||
readonly filters = signal<AdminProductListFilters>({
|
||||
search: '',
|
||||
|
||||
@@ -9,7 +9,7 @@ import { LanguageService } from '../../../../services/language.service';
|
||||
selector: 'app-admin-product-editor-page',
|
||||
standalone: true,
|
||||
imports: [AdminProductFormComponent, TranslatePipe],
|
||||
template: `@if (facade.draft(); as draft) {<main class="editor-page"><header><h1>{{ title() | translate }}</h1></header><app-admin-product-form [product]="draft" [categories]="facade.categories()" [allProducts]="facade.products()" [mode]="facade.editorMode()" (productChange)="facade.updateDraft($event)" (save)="save()" /></main>} @else {<main class="editor-page"><p>{{ 'common.loading' | translate }}</p></main>}`,
|
||||
template: `@if (facade.draft(); as draft) {<main class="editor-page"><header><h1>{{ title() | translate }}</h1></header><app-admin-product-form [product]="draft" [categories]="facade.categories()" [allProducts]="facade.products()" [locales]="facade.supportedLocales()" [mode]="facade.editorMode()" (productChange)="facade.updateDraft($event)" (save)="save()" /></main>} @else {<main class="editor-page"><p>{{ 'common.loading' | translate }}</p></main>}`,
|
||||
styles: [`.editor-page { max-width: 1120px; margin: 0 auto; padding: 24px; display: grid; gap: 16px; } .editor-page h1, .editor-page p { margin: 0; }`],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
@@ -22,6 +22,7 @@ export class AdminProductEditorPageComponent {
|
||||
|
||||
constructor() {
|
||||
this.facade.loadCategories();
|
||||
this.facade.ensureLocalesLoaded();
|
||||
const id = this.route.snapshot.paramMap.get('id');
|
||||
const mode = this.route.snapshot.routeConfig?.path?.includes('duplicate') ? 'duplicate' : this.route.snapshot.routeConfig?.path?.includes('edit') ? 'edit' : 'create';
|
||||
if (mode === 'create') {
|
||||
|
||||
Reference in New Issue
Block a user