diff --git a/src/app/features/project-editor/facade/project-editor.facade.ts b/src/app/features/project-editor/facade/project-editor.facade.ts index 8062bd0..9a29add 100644 --- a/src/app/features/project-editor/facade/project-editor.facade.ts +++ b/src/app/features/project-editor/facade/project-editor.facade.ts @@ -1,7 +1,7 @@ import { Injectable, computed, inject, signal } from '@angular/core'; import { take } from 'rxjs/operators'; import { BootstrapConfig, DEFAULT_CATALOG_CONFIG, DEFAULT_HEADER_CONFIG, DEFAULT_PRODUCT_PAGE_CONFIG, DEFAULT_USER_EXPERIENCE_CONFIG } from '../../../shared/models/config'; -import { NavigationItemConfig } from '../../../shared/models/config'; +import { NavigationItemConfig, NavigationLocalizedText } from '../../../shared/models/config'; import { ConfigService } from '../../../core/config/config.service'; import { ProjectEditorIoService } from '../services/project-editor-io.service'; import { ProjectEditorPreviewService } from '../services/project-editor-preview.service'; @@ -242,13 +242,14 @@ export class ProjectEditorFacade { * If it's already a NavigationLocalizedText object, only the current default * locale's key is overwritten; every other locale key is preserved untouched. */ - updateNavLinkLabel(target: 'header' | 'footer', id: string, value: string): void { + updateNavLinkLabel(target: 'header' | 'footer', id: string, value: string, locale?: string): void { this.updateBootstrap(current => { const list = current.navigation[target]; if (!this.isFlatNavList(list)) { return current; } const defaultLocale = current.localization.defaultLocale ?? 'en'; + const targetLocale = locale ?? defaultLocale; return { ...current, navigation: { @@ -258,9 +259,14 @@ export class ProjectEditorFacade { return item; } const currentLabel = item.label; - const nextLabel = currentLabel && typeof currentLabel === 'object' - ? { ...currentLabel, [defaultLocale]: value } - : value; + let nextLabel: string | NavigationLocalizedText; + if (currentLabel && typeof currentLabel === 'object') { + nextLabel = { ...currentLabel, [targetLocale]: value }; + } else if (targetLocale === defaultLocale) { + nextLabel = value; + } else { + nextLabel = { [defaultLocale]: currentLabel ?? '', [targetLocale]: value }; + } return { ...item, label: nextLabel }; }), }, diff --git a/src/app/features/project-editor/sections/languages-section.component.html b/src/app/features/project-editor/sections/languages-section.component.html index 225f4a5..13980ae 100644 --- a/src/app/features/project-editor/sections/languages-section.component.html +++ b/src/app/features/project-editor/sections/languages-section.component.html @@ -1,7 +1,5 @@ -
-
-

{{ 'builder.languagesTab' | translate }}

-
+ + {{ 'builder.languagesTabDesc' | translate }}
@@ -31,4 +29,4 @@ }
-
+ diff --git a/src/app/features/project-editor/sections/languages-section.component.ts b/src/app/features/project-editor/sections/languages-section.component.ts index c3a7123..4af2748 100644 --- a/src/app/features/project-editor/sections/languages-section.component.ts +++ b/src/app/features/project-editor/sections/languages-section.component.ts @@ -4,11 +4,13 @@ import { ProjectEditorFacade } from '../facade/project-editor.facade'; import { TranslatePipe } from '../../../i18n/translate.pipe'; import { ButtonComponent } from '../../../shared/ui/button/button.component'; import { InputComponent } from '../../../shared/ui/input/input.component'; +import { SectionCardComponent } from '../../../shared/ui/section-card/section-card.component'; +import { LocaleTabsComponent } from '../../../shared/ui/locale-tabs/locale-tabs.component'; @Component({ selector: 'app-project-editor-languages-section', standalone: true, - imports: [FormsModule, TranslatePipe, ButtonComponent, InputComponent], + imports: [FormsModule, TranslatePipe, ButtonComponent, InputComponent, SectionCardComponent, LocaleTabsComponent], templateUrl: './languages-section.component.html', styleUrls: ['./section.shared.scss'], changeDetection: ChangeDetectionStrategy.OnPush @@ -18,8 +20,15 @@ export class ProjectEditorLanguagesSectionComponent { readonly bootstrap = this.facade.bootstrap; readonly locales = computed(() => this.bootstrap()?.localization.supportedLocales ?? []); readonly defaultLocale = computed(() => this.bootstrap()?.localization.defaultLocale ?? ''); + readonly supportedLocales = computed(() => this.bootstrap()?.tenant.supportedLocales ?? []); + readonly tenantDefaultLocale = computed(() => this.bootstrap()?.tenant.defaultLocale ?? ''); + readonly activeLocale = signal(null); readonly newLocale = signal(''); + onActiveLocaleChange(locale: string): void { + this.activeLocale.set(locale); + } + updateNewLocale(value: string): void { this.newLocale.set(value); } diff --git a/src/app/features/project-editor/sections/navigation-section.component.html b/src/app/features/project-editor/sections/navigation-section.component.html index 3199f69..e3b58a0 100644 --- a/src/app/features/project-editor/sections/navigation-section.component.html +++ b/src/app/features/project-editor/sections/navigation-section.component.html @@ -1,6 +1,7 @@ -
+ + +
-

{{ 'builder.navigationHeader' | translate }}

{{ 'builder.addLink' | translate }}
@@ -16,9 +17,9 @@
- + - +
} @@ -44,9 +45,9 @@
- + - +
} @@ -54,4 +55,4 @@ } @else {

{{ 'builder.navigationFooterGrouped' | translate }}

} -
+ diff --git a/src/app/features/project-editor/sections/navigation-section.component.ts b/src/app/features/project-editor/sections/navigation-section.component.ts index 03f1f8f..e1680de 100644 --- a/src/app/features/project-editor/sections/navigation-section.component.ts +++ b/src/app/features/project-editor/sections/navigation-section.component.ts @@ -1,15 +1,18 @@ -import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { ProjectEditorFacade } from '../facade/project-editor.facade'; import { TranslatePipe } from '../../../i18n/translate.pipe'; import { NavigationItemConfig } from '../../../shared/models/config'; import { ButtonComponent } from '../../../shared/ui/button/button.component'; import { InputComponent } from '../../../shared/ui/input/input.component'; +import { SectionCardComponent } from '../../../shared/ui/section-card/section-card.component'; +import { ToggleComponent } from '../../../shared/ui/toggle/toggle.component'; +import { LocaleTabsComponent } from '../../../shared/ui/locale-tabs/locale-tabs.component'; @Component({ selector: 'app-project-editor-navigation-section', standalone: true, - imports: [FormsModule, TranslatePipe, ButtonComponent, InputComponent], + imports: [FormsModule, TranslatePipe, ButtonComponent, InputComponent, SectionCardComponent, ToggleComponent, LocaleTabsComponent], templateUrl: './navigation-section.component.html', styleUrls: ['./section.shared.scss'], changeDetection: ChangeDetectionStrategy.OnPush @@ -18,6 +21,10 @@ export class ProjectEditorNavigationSectionComponent { private readonly facade = inject(ProjectEditorFacade); readonly bootstrap = this.facade.bootstrap; + readonly supportedLocales = computed(() => this.bootstrap()?.tenant.supportedLocales ?? []); + readonly defaultLocale = computed(() => this.bootstrap()?.tenant.defaultLocale ?? ''); + readonly activeLocale = signal(null); + readonly headerLinks = computed(() => this.sorted(this.bootstrap()?.navigation.header ?? [])); readonly footerLinks = computed(() => { const footer = this.bootstrap()?.navigation.footer ?? []; @@ -27,6 +34,11 @@ export class ProjectEditorNavigationSectionComponent { return 'items' in footer[0] ? null : this.sorted(footer as NavigationItemConfig[]); }); + onActiveLocaleChange(locale: string): void { + this.activeLocale.set(locale); + } + + /** Display label used for the row heading — a stable identifier, not tied to the active locale. */ labelOf(item: NavigationItemConfig): string { if (typeof item.label === 'string' || !item.label) { return item.label ?? ''; @@ -35,6 +47,19 @@ export class ProjectEditorNavigationSectionComponent { return item.label[defaultLocale] ?? Object.values(item.label)[0] ?? ''; } + /** Editable label for the currently active locale tab. Falls back to an empty string + * when this locale has no translation yet, so switching locales lets you fill it in. */ + editableLabel(item: NavigationItemConfig): string { + const locale = this.activeLocale() ?? this.defaultLocale(); + if (!item.label) { + return ''; + } + if (typeof item.label === 'string') { + return locale === this.defaultLocale() ? item.label : ''; + } + return item.label[locale] ?? ''; + } + addLink(target: 'header' | 'footer'): void { this.facade.addNavLink(target); } @@ -48,7 +73,8 @@ export class ProjectEditorNavigationSectionComponent { } updateLabel(target: 'header' | 'footer', id: string, value: string): void { - this.facade.updateNavLinkLabel(target, id, value); + const locale = this.activeLocale() ?? this.defaultLocale(); + this.facade.updateNavLinkLabel(target, id, value, locale); } updateRoute(target: 'header' | 'footer', id: string, value: string): void {