@flusys/ng-layout 3.0.1 → 4.0.0-rc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, PLATFORM_ID, Injectable, DOCUMENT, signal, computed, effect, ChangeDetectionStrategy, Component, afterNextRender, InjectionToken, DestroyRef, ElementRef, viewChild, input, Renderer2 } from '@angular/core';
3
- import { isPlatformBrowser, NgClass, DOCUMENT as DOCUMENT$1, NgComponentOutlet } from '@angular/common';
4
- import { APP_CONFIG, DEFAULT_APP_NAME, DEFAULT_AUTHOR, isCompanyFeatureEnabled } from '@flusys/ng-core';
2
+ import { inject, PLATFORM_ID, Injectable, DOCUMENT, signal, computed, effect, Component, afterNextRender, InjectionToken, DestroyRef, ElementRef, viewChild, input, Renderer2 } from '@angular/core';
5
3
  import * as i2$2 from '@flusys/ng-shared';
6
- import { evaluateLogicNode, PermissionValidatorService, AngularModule, IconComponent } from '@flusys/ng-shared';
4
+ import { evaluateLogicNode, PermissionValidatorService, TranslatePipe, AngularModule, IconComponent } from '@flusys/ng-shared';
5
+ import { isPlatformBrowser, NgComponentOutlet, DOCUMENT as DOCUMENT$1, NgClass } from '@angular/common';
6
+ import { APP_CONFIG, TRANSLATE_ADAPTER, DEFAULT_APP_NAME, DEFAULT_AUTHOR, isCompanyFeatureEnabled } from '@flusys/ng-core';
7
7
  import { Subject, fromEvent, filter as filter$1 } from 'rxjs';
8
8
  import * as i1 from '@angular/forms';
9
9
  import { FormsModule } from '@angular/forms';
@@ -209,6 +209,7 @@ class LayoutService {
209
209
  isBrowser = isPlatformBrowser(this.platformId);
210
210
  persistence = inject(LayoutPersistenceService);
211
211
  appConfig = inject(APP_CONFIG, { optional: true });
212
+ translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
212
213
  DEFAULT_CONFIG = {
213
214
  preset: 'Aura',
214
215
  primary: 'emerald',
@@ -255,7 +256,7 @@ class LayoutService {
255
256
  getSurface = computed(() => this._layoutConfig().surface, ...(ngDevMode ? [{ debugName: "getSurface" }] : []));
256
257
  isOverlay = computed(() => this._layoutConfig().menuMode === 'overlay', ...(ngDevMode ? [{ debugName: "isOverlay" }] : []));
257
258
  // User profile computed signals
258
- userName = computed(() => this._userProfile()?.name ?? 'User', ...(ngDevMode ? [{ debugName: "userName" }] : []));
259
+ userName = computed(() => this._userProfile()?.name ?? this.translateAdapter?.translate('layout.profile.guest') ?? 'Guest', ...(ngDevMode ? [{ debugName: "userName" }] : []));
259
260
  userEmail = computed(() => this._userProfile()?.email ?? '', ...(ngDevMode ? [{ debugName: "userEmail" }] : []));
260
261
  userProfilePictureUrl = computed(() => this._userProfile()?.profilePictureUrl ?? null, ...(ngDevMode ? [{ debugName: "userProfilePictureUrl" }] : []));
261
262
  companyLogoUrl = computed(() => this._companyProfile()?.logoUrl ?? null, ...(ngDevMode ? [{ debugName: "companyLogoUrl" }] : []));
@@ -309,25 +310,27 @@ class LayoutService {
309
310
  }
310
311
  onMenuToggle() {
311
312
  const state = this._layoutState();
313
+ // Mobile always uses staticMenuMobileActive regardless of menu mode
314
+ if (!this.isDesktop()) {
315
+ const newMobileActive = !state.staticMenuMobileActive;
316
+ this._layoutState.update((prev) => ({ ...prev, staticMenuMobileActive: newMobileActive }));
317
+ if (newMobileActive)
318
+ this.overlayOpen.next();
319
+ return;
320
+ }
321
+ // Desktop: use overlay or static mode
312
322
  if (this.isOverlay()) {
313
323
  const newOverlayActive = !state.overlayMenuActive;
314
324
  this._layoutState.update((prev) => ({ ...prev, overlayMenuActive: newOverlayActive }));
315
325
  if (newOverlayActive)
316
326
  this.overlayOpen.next();
317
- return;
318
327
  }
319
- if (this.isDesktop()) {
328
+ else {
320
329
  this._layoutState.update((prev) => ({
321
330
  ...prev,
322
331
  staticMenuDesktopInactive: !state.staticMenuDesktopInactive,
323
332
  }));
324
333
  }
325
- else {
326
- const newMobileActive = !state.staticMenuMobileActive;
327
- this._layoutState.update((prev) => ({ ...prev, staticMenuMobileActive: newMobileActive }));
328
- if (newMobileActive)
329
- this.overlayOpen.next();
330
- }
331
334
  }
332
335
  isDesktop() {
333
336
  return this.isBrowser ? window.innerWidth > 991 : true;
@@ -378,23 +381,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
378
381
 
379
382
  class AppFooter {
380
383
  layoutService = inject(LayoutService);
381
- // Footer shows product branding (appName), not user's company
382
384
  appName = this.layoutService.appName;
383
385
  authorName = this.layoutService.authorName;
384
386
  authorUrl = this.layoutService.authorUrl;
385
387
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppFooter, deps: [], target: i0.ɵɵFactoryTarget.Component });
386
388
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.5", type: AppFooter, isStandalone: true, selector: "app-footer", ngImport: i0, template: `<div class="layout-footer">
387
- {{ appName }} by
389
+ {{ appName }} {{ 'layout.footer.by' | translate }}
388
390
  <a [href]="authorUrl" target="_blank" rel="noopener noreferrer" class="text-primary font-bold hover:underline">{{ authorName }}</a>
389
- </div>`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
391
+ </div>`, isInline: true, dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }] });
390
392
  }
391
393
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppFooter, decorators: [{
392
394
  type: Component,
393
395
  args: [{
394
396
  selector: 'app-footer',
395
- changeDetection: ChangeDetectionStrategy.OnPush,
397
+ imports: [TranslatePipe],
396
398
  template: `<div class="layout-footer">
397
- {{ appName }} by
399
+ {{ appName }} {{ 'layout.footer.by' | translate }}
398
400
  <a [href]="authorUrl" target="_blank" rel="noopener noreferrer" class="text-primary font-bold hover:underline">{{ authorName }}</a>
399
401
  </div>`,
400
402
  }]
@@ -407,13 +409,14 @@ const presets = {
407
409
  };
408
410
  class AppConfigurator {
409
411
  router = inject(Router);
412
+ translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
410
413
  layoutService = inject(LayoutService);
411
414
  presets = Object.keys(presets);
412
415
  showMenuModeButton = signal(!this.router.url.includes('auth'), ...(ngDevMode ? [{ debugName: "showMenuModeButton" }] : []));
413
- menuModeOptions = [
414
- { label: 'Static', value: 'static' },
415
- { label: 'Overlay', value: 'overlay' },
416
- ];
416
+ menuModeOptions = computed(() => [
417
+ { label: this.translate('layout.configurator.menu.mode.static'), value: 'static' },
418
+ { label: this.translate('layout.configurator.menu.mode.overlay'), value: 'overlay' },
419
+ ], ...(ngDevMode ? [{ debugName: "menuModeOptions" }] : []));
417
420
  constructor() {
418
421
  // Use afterNextRender for browser-only initialization (replaces isServer check)
419
422
  afterNextRender(() => {
@@ -563,6 +566,16 @@ class AppConfigurator {
563
566
  return this.layoutService.layoutConfig().primary;
564
567
  }, ...(ngDevMode ? [{ debugName: "selectedPrimaryColor" }] : []));
565
568
  selectedSurfaceColor = computed(() => this.layoutService.layoutConfig().surface, ...(ngDevMode ? [{ debugName: "selectedSurfaceColor" }] : []));
569
+ isSurfaceSelected(surfaceName) {
570
+ const selectedSurface = this.selectedSurfaceColor();
571
+ if (selectedSurface) {
572
+ return selectedSurface === surfaceName;
573
+ }
574
+ // Default: zinc for dark theme, slate for light theme
575
+ return this.layoutService.layoutConfig().darkTheme
576
+ ? surfaceName === 'zinc'
577
+ : surfaceName === 'slate';
578
+ }
566
579
  selectedPreset = computed(() => this.layoutService.layoutConfig().preset, ...(ngDevMode ? [{ debugName: "selectedPreset" }] : []));
567
580
  menuMode = computed(() => this.layoutService.layoutConfig().menuMode, ...(ngDevMode ? [{ debugName: "menuMode" }] : []));
568
581
  primaryColors = computed(() => {
@@ -757,53 +770,44 @@ class AppConfigurator {
757
770
  onMenuModeChange(event) {
758
771
  this.layoutService.updateLayoutConfig({ menuMode: event });
759
772
  }
773
+ translate(key) {
774
+ return this.translateAdapter?.translate(key) ?? key;
775
+ }
760
776
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppConfigurator, deps: [], target: i0.ɵɵFactoryTarget.Component });
761
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: AppConfigurator, isStandalone: true, selector: "app-configurator", host: { styleAttribute: "background-color: var(--surface-overlay)", classAttribute: "hidden absolute top-[3.25rem] right-0 w-[calc(100vw-2rem)] sm:w-72 max-w-72 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)]" }, ngImport: i0, template: `
777
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: AppConfigurator, isStandalone: true, selector: "app-configurator", host: { styleAttribute: "background-color: var(--surface-overlay); inset-inline-end: 0;", classAttribute: "hidden absolute top-[3.25rem] w-[calc(100vw-2rem)] sm:w-72 max-w-72 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)]" }, ngImport: i0, template: `
762
778
  <div class="flex flex-col gap-4">
763
779
  <div>
764
- <span class="text-sm text-muted-color font-semibold">Primary</span>
780
+ <span class="text-sm text-muted-color font-semibold">{{ 'layout.configurator.primary' | translate }}</span>
765
781
  <div class="pt-2 flex gap-2 flex-wrap justify-start">
766
782
  @for (primaryColor of primaryColors(); track primaryColor.name) {
767
783
  <button
768
784
  type="button"
769
785
  [title]="primaryColor.name"
770
786
  (click)="updateColors($event, 'primary', primaryColor)"
771
- [ngClass]="{
772
- 'outline-primary': primaryColor.name === selectedPrimaryColor()
773
- }"
787
+ [class.outline-primary]="primaryColor.name === selectedPrimaryColor()"
774
788
  class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
775
- [style]="{
776
- 'background-color': primaryColor?.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette?.['500']
777
- }"
789
+ [style.background-color]="primaryColor?.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette?.['500']"
778
790
  ></button>
779
791
  }
780
792
  </div>
781
793
  </div>
782
794
  <div>
783
- <span class="text-sm text-muted-color font-semibold">Surface</span>
795
+ <span class="text-sm text-muted-color font-semibold">{{ 'layout.configurator.surface' | translate }}</span>
784
796
  <div class="pt-2 flex gap-2 flex-wrap justify-start">
785
797
  @for (surface of surfaces; track surface.name) {
786
798
  <button
787
799
  type="button"
788
800
  [title]="surface.name"
789
801
  (click)="updateColors($event, 'surface', surface)"
790
- [ngClass]="{
791
- 'outline-primary': selectedSurfaceColor()
792
- ? selectedSurfaceColor() === surface.name
793
- : layoutService.layoutConfig().darkTheme
794
- ? surface.name === 'zinc'
795
- : surface.name === 'slate'
796
- }"
802
+ [class.outline-primary]="isSurfaceSelected(surface.name)"
797
803
  class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
798
- [style]="{
799
- 'background-color': surface?.name === 'noir' ? 'var(--text-color)' : surface?.palette?.['500']
800
- }"
804
+ [style.background-color]="surface?.name === 'noir' ? 'var(--text-color)' : surface?.palette?.['500']"
801
805
  ></button>
802
806
  }
803
807
  </div>
804
808
  </div>
805
809
  <div class="flex flex-col gap-2">
806
- <span class="text-sm text-muted-color font-semibold">Presets</span>
810
+ <span class="text-sm text-muted-color font-semibold">{{ 'layout.configurator.presets' | translate }}</span>
807
811
  <p-selectbutton
808
812
  [options]="presets"
809
813
  [ngModel]="selectedPreset()"
@@ -814,71 +818,58 @@ class AppConfigurator {
814
818
  </div>
815
819
  @if (showMenuModeButton()) {
816
820
  <div class="flex flex-col gap-2">
817
- <span class="text-sm text-muted-color font-semibold">Menu Mode</span>
821
+ <span class="text-sm text-muted-color font-semibold">{{ 'layout.configurator.menu.mode' | translate }}</span>
818
822
  <p-selectbutton
819
823
  [ngModel]="menuMode()"
820
824
  (ngModelChange)="onMenuModeChange($event)"
821
- [options]="menuModeOptions"
825
+ [options]="menuModeOptions()"
822
826
  [allowEmpty]="false"
823
827
  size="small"
824
828
  />
825
829
  </div>
826
830
  }
827
831
  </div>
828
- `, isInline: true, dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i2.SelectButton, selector: "p-selectButton, p-selectbutton, p-select-button", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "styleClass", "ariaLabelledBy", "dataKey", "autofocus", "size", "fluid"], outputs: ["onOptionClick", "onChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
832
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i2.SelectButton, selector: "p-selectButton, p-selectbutton, p-select-button", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "styleClass", "ariaLabelledBy", "dataKey", "autofocus", "size", "fluid"], outputs: ["onOptionClick", "onChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
829
833
  }
830
834
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppConfigurator, decorators: [{
831
835
  type: Component,
832
836
  args: [{
833
837
  selector: 'app-configurator',
834
- changeDetection: ChangeDetectionStrategy.OnPush,
835
- imports: [NgClass, FormsModule, SelectButtonModule],
838
+ imports: [FormsModule, SelectButtonModule, TranslatePipe],
836
839
  template: `
837
840
  <div class="flex flex-col gap-4">
838
841
  <div>
839
- <span class="text-sm text-muted-color font-semibold">Primary</span>
842
+ <span class="text-sm text-muted-color font-semibold">{{ 'layout.configurator.primary' | translate }}</span>
840
843
  <div class="pt-2 flex gap-2 flex-wrap justify-start">
841
844
  @for (primaryColor of primaryColors(); track primaryColor.name) {
842
845
  <button
843
846
  type="button"
844
847
  [title]="primaryColor.name"
845
848
  (click)="updateColors($event, 'primary', primaryColor)"
846
- [ngClass]="{
847
- 'outline-primary': primaryColor.name === selectedPrimaryColor()
848
- }"
849
+ [class.outline-primary]="primaryColor.name === selectedPrimaryColor()"
849
850
  class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
850
- [style]="{
851
- 'background-color': primaryColor?.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette?.['500']
852
- }"
851
+ [style.background-color]="primaryColor?.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette?.['500']"
853
852
  ></button>
854
853
  }
855
854
  </div>
856
855
  </div>
857
856
  <div>
858
- <span class="text-sm text-muted-color font-semibold">Surface</span>
857
+ <span class="text-sm text-muted-color font-semibold">{{ 'layout.configurator.surface' | translate }}</span>
859
858
  <div class="pt-2 flex gap-2 flex-wrap justify-start">
860
859
  @for (surface of surfaces; track surface.name) {
861
860
  <button
862
861
  type="button"
863
862
  [title]="surface.name"
864
863
  (click)="updateColors($event, 'surface', surface)"
865
- [ngClass]="{
866
- 'outline-primary': selectedSurfaceColor()
867
- ? selectedSurfaceColor() === surface.name
868
- : layoutService.layoutConfig().darkTheme
869
- ? surface.name === 'zinc'
870
- : surface.name === 'slate'
871
- }"
864
+ [class.outline-primary]="isSurfaceSelected(surface.name)"
872
865
  class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
873
- [style]="{
874
- 'background-color': surface?.name === 'noir' ? 'var(--text-color)' : surface?.palette?.['500']
875
- }"
866
+ [style.background-color]="surface?.name === 'noir' ? 'var(--text-color)' : surface?.palette?.['500']"
876
867
  ></button>
877
868
  }
878
869
  </div>
879
870
  </div>
880
871
  <div class="flex flex-col gap-2">
881
- <span class="text-sm text-muted-color font-semibold">Presets</span>
872
+ <span class="text-sm text-muted-color font-semibold">{{ 'layout.configurator.presets' | translate }}</span>
882
873
  <p-selectbutton
883
874
  [options]="presets"
884
875
  [ngModel]="selectedPreset()"
@@ -889,11 +880,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
889
880
  </div>
890
881
  @if (showMenuModeButton()) {
891
882
  <div class="flex flex-col gap-2">
892
- <span class="text-sm text-muted-color font-semibold">Menu Mode</span>
883
+ <span class="text-sm text-muted-color font-semibold">{{ 'layout.configurator.menu.mode' | translate }}</span>
893
884
  <p-selectbutton
894
885
  [ngModel]="menuMode()"
895
886
  (ngModelChange)="onMenuModeChange($event)"
896
- [options]="menuModeOptions"
887
+ [options]="menuModeOptions()"
897
888
  [allowEmpty]="false"
898
889
  size="small"
899
890
  />
@@ -902,52 +893,121 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
902
893
  </div>
903
894
  `,
904
895
  host: {
905
- class: 'hidden absolute top-[3.25rem] right-0 w-[calc(100vw-2rem)] sm:w-72 max-w-72 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)]',
906
- style: 'background-color: var(--surface-overlay)',
896
+ class: 'hidden absolute top-[3.25rem] w-[calc(100vw-2rem)] sm:w-72 max-w-72 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)]',
897
+ style: 'background-color: var(--surface-overlay); inset-inline-end: 0;',
907
898
  },
908
899
  }]
909
900
  }], ctorParameters: () => [] });
910
901
 
902
+ const LAYOUT_AUTH_STATE = new InjectionToken('LAYOUT_AUTH_STATE');
903
+ const LAYOUT_AUTH_API = new InjectionToken('LAYOUT_AUTH_API');
904
+ const LAYOUT_NOTIFICATION_BELL = new InjectionToken('LAYOUT_NOTIFICATION_BELL');
905
+ const LAYOUT_LANGUAGE_SELECTOR = new InjectionToken('LAYOUT_LANGUAGE_SELECTOR');
906
+ const LAYOUT_LANGUAGE_SELECTOR_PANEL = new InjectionToken('LAYOUT_LANGUAGE_SELECTOR_PANEL');
907
+ const LAYOUT_IS_RTL = new InjectionToken('LAYOUT_IS_RTL');
908
+
911
909
  class AppFloatingConfigurator {
912
910
  layoutService = inject(LayoutService);
911
+ languageSelectorPanel = inject(LAYOUT_LANGUAGE_SELECTOR_PANEL, {
912
+ optional: true,
913
+ });
913
914
  isDarkTheme = computed(() => this.layoutService.layoutConfig().darkTheme, ...(ngDevMode ? [{ debugName: "isDarkTheme" }] : []));
914
915
  toggleDarkMode() {
915
916
  const currentDarkTheme = this.layoutService.layoutConfig().darkTheme;
916
917
  this.layoutService.updateLayoutConfig({ darkTheme: !currentDarkTheme });
917
918
  }
918
919
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppFloatingConfigurator, deps: [], target: i0.ɵɵFactoryTarget.Component });
919
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.5", type: AppFloatingConfigurator, isStandalone: true, selector: "app-floating-configurator", ngImport: i0, template: `
920
- <div class="fixed flex flex-col md:flex-row gap-2 md:gap-4 top-4 md:top-8 right-2 md:right-8 z-50">
921
- <p-button type="button" (onClick)="toggleDarkMode()" [rounded]="true" [icon]="isDarkTheme() ? 'pi pi-moon' : 'pi pi-sun'" severity="secondary" />
922
- <div class="relative">
923
- <p-button icon="pi pi-palette" pStyleClass="@next" enterFromClass="hidden" enterActiveClass="animate-scalein" leaveToClass="hidden" leaveActiveClass="animate-fadeout" [hideOnOutsideClick]="true" type="button" rounded />
924
- <app-configurator />
925
- </div>
920
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: AppFloatingConfigurator, isStandalone: true, selector: "app-floating-configurator", ngImport: i0, template: `
921
+ <div
922
+ class="floating-configurator fixed flex flex-col md:flex-row gap-2 md:gap-4 top-4 md:top-8 z-50"
923
+ >
924
+ <p-button
925
+ type="button"
926
+ (onClick)="toggleDarkMode()"
927
+ [rounded]="true"
928
+ [icon]="isDarkTheme() ? 'pi pi-moon' : 'pi pi-sun'"
929
+ severity="secondary"
930
+ />
931
+ @if (languageSelectorPanel) {
932
+ <div class="relative">
933
+ <p-button
934
+ icon="pi pi-globe"
935
+ pStyleClass="@next"
936
+ enterFromClass="hidden"
937
+ enterActiveClass="animate-scalein"
938
+ leaveToClass="hidden"
939
+ leaveActiveClass="animate-fadeout"
940
+ [hideOnOutsideClick]="true"
941
+ type="button"
942
+ rounded
943
+ />
944
+ <ng-container *ngComponentOutlet="languageSelectorPanel" />
926
945
  </div>
927
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i2$1.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "component", type: AppConfigurator, selector: "app-configurator" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
946
+ }
947
+ <div class="relative">
948
+ <p-button
949
+ icon="pi pi-palette"
950
+ pStyleClass="@next"
951
+ enterFromClass="hidden"
952
+ enterActiveClass="animate-scalein"
953
+ leaveToClass="hidden"
954
+ leaveActiveClass="animate-fadeout"
955
+ [hideOnOutsideClick]="true"
956
+ type="button"
957
+ rounded
958
+ />
959
+ <app-configurator />
960
+ </div>
961
+ </div>
962
+ `, isInline: true, styles: [".floating-configurator{inset-inline-end:.5rem}@media(min-width:768px){.floating-configurator{inset-inline-end:2rem}}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i2$1.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "component", type: AppConfigurator, selector: "app-configurator" }] });
928
963
  }
929
964
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppFloatingConfigurator, decorators: [{
930
965
  type: Component,
931
- args: [{
932
- selector: 'app-floating-configurator',
933
- changeDetection: ChangeDetectionStrategy.OnPush,
934
- imports: [ButtonModule, StyleClassModule, AppConfigurator],
935
- template: `
936
- <div class="fixed flex flex-col md:flex-row gap-2 md:gap-4 top-4 md:top-8 right-2 md:right-8 z-50">
937
- <p-button type="button" (onClick)="toggleDarkMode()" [rounded]="true" [icon]="isDarkTheme() ? 'pi pi-moon' : 'pi pi-sun'" severity="secondary" />
938
- <div class="relative">
939
- <p-button icon="pi pi-palette" pStyleClass="@next" enterFromClass="hidden" enterActiveClass="animate-scalein" leaveToClass="hidden" leaveActiveClass="animate-fadeout" [hideOnOutsideClick]="true" type="button" rounded />
940
- <app-configurator />
941
- </div>
966
+ args: [{ selector: 'app-floating-configurator', imports: [NgComponentOutlet, ButtonModule, StyleClassModule, AppConfigurator], template: `
967
+ <div
968
+ class="floating-configurator fixed flex flex-col md:flex-row gap-2 md:gap-4 top-4 md:top-8 z-50"
969
+ >
970
+ <p-button
971
+ type="button"
972
+ (onClick)="toggleDarkMode()"
973
+ [rounded]="true"
974
+ [icon]="isDarkTheme() ? 'pi pi-moon' : 'pi pi-sun'"
975
+ severity="secondary"
976
+ />
977
+ @if (languageSelectorPanel) {
978
+ <div class="relative">
979
+ <p-button
980
+ icon="pi pi-globe"
981
+ pStyleClass="@next"
982
+ enterFromClass="hidden"
983
+ enterActiveClass="animate-scalein"
984
+ leaveToClass="hidden"
985
+ leaveActiveClass="animate-fadeout"
986
+ [hideOnOutsideClick]="true"
987
+ type="button"
988
+ rounded
989
+ />
990
+ <ng-container *ngComponentOutlet="languageSelectorPanel" />
942
991
  </div>
943
- `
944
- }]
992
+ }
993
+ <div class="relative">
994
+ <p-button
995
+ icon="pi pi-palette"
996
+ pStyleClass="@next"
997
+ enterFromClass="hidden"
998
+ enterActiveClass="animate-scalein"
999
+ leaveToClass="hidden"
1000
+ leaveActiveClass="animate-fadeout"
1001
+ [hideOnOutsideClick]="true"
1002
+ type="button"
1003
+ rounded
1004
+ />
1005
+ <app-configurator />
1006
+ </div>
1007
+ </div>
1008
+ `, styles: [".floating-configurator{inset-inline-end:.5rem}@media(min-width:768px){.floating-configurator{inset-inline-end:2rem}}\n"] }]
945
1009
  }] });
946
1010
 
947
- const LAYOUT_AUTH_STATE = new InjectionToken('LAYOUT_AUTH_STATE');
948
- const LAYOUT_AUTH_API = new InjectionToken('LAYOUT_AUTH_API');
949
- const LAYOUT_NOTIFICATION_BELL = new InjectionToken('LAYOUT_NOTIFICATION_BELL');
950
-
951
1011
  /**
952
1012
  * View Transitions API type definitions
953
1013
  * @see https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API
@@ -959,6 +1019,7 @@ class AppCompanyBranchSelector {
959
1019
  authState = inject(LAYOUT_AUTH_STATE, { optional: true });
960
1020
  authApi = inject(LAYOUT_AUTH_API, { optional: true });
961
1021
  messageService = inject(MessageService);
1022
+ translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
962
1023
  document = inject(DOCUMENT$1);
963
1024
  elementRef = inject(ElementRef);
964
1025
  _isActive = signal(false, ...(ngDevMode ? [{ debugName: "_isActive" }] : []));
@@ -973,7 +1034,7 @@ class AppCompanyBranchSelector {
973
1034
  }
974
1035
  });
975
1036
  }
976
- currentCompanyName = computed(() => this.authState?.currentCompanyInfo()?.name ?? 'No Company', ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
1037
+ currentCompanyName = computed(() => this.authState?.currentCompanyInfo()?.name ?? this.translate('layout.company.branch.selector.no.company'), ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
977
1038
  currentBranchName = computed(() => this.authState?.currentBranchInfo()?.name ?? null, ...(ngDevMode ? [{ debugName: "currentBranchName" }] : []));
978
1039
  _companies = signal([], ...(ngDevMode ? [{ debugName: "_companies" }] : []));
979
1040
  companies = this._companies.asReadonly();
@@ -1065,8 +1126,8 @@ class AppCompanyBranchSelector {
1065
1126
  if (this.branches().length > 0 && !selectedBranch) {
1066
1127
  this.messageService.add({
1067
1128
  severity: 'warn',
1068
- summary: 'Branch Required',
1069
- detail: 'Please select a branch',
1129
+ summary: this.translate('layout.company.branch.selector.branch.required'),
1130
+ detail: this.translate('layout.company.branch.selector.please.select.branch'),
1070
1131
  });
1071
1132
  return;
1072
1133
  }
@@ -1082,6 +1143,9 @@ class AppCompanyBranchSelector {
1082
1143
  },
1083
1144
  });
1084
1145
  }
1146
+ translate(key) {
1147
+ return this.translateAdapter?.translate(key) ?? key;
1148
+ }
1085
1149
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppCompanyBranchSelector, deps: [], target: i0.ɵɵFactoryTarget.Component });
1086
1150
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: AppCompanyBranchSelector, isStandalone: true, selector: "app-company-branch-selector", host: { classAttribute: "relative" }, ngImport: i0, template: `
1087
1151
  <button
@@ -1103,24 +1167,24 @@ class AppCompanyBranchSelector {
1103
1167
  }
1104
1168
  </button>
1105
1169
  <div
1106
- class="hidden absolute top-[3.25rem] right-0 z-50 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)] w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[300px] max-w-[360px]"
1107
- style="background-color: var(--surface-overlay)"
1170
+ class="hidden absolute top-[3.25rem] z-50 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)] w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[300px] max-w-[360px]"
1171
+ style="background-color: var(--surface-overlay); inset-inline-end: 0;"
1108
1172
  >
1109
1173
  <div class="flex flex-col gap-4">
1110
1174
  <span class="text-sm text-muted-color font-semibold"
1111
- >Switch Company & Branch</span
1175
+ >{{ 'layout.company.branch.selector.title' | translate }}</span
1112
1176
  >
1113
1177
 
1114
1178
  <!-- Company Selector -->
1115
1179
  <div class="flex flex-col gap-2">
1116
- <label class="text-sm text-muted-color font-semibold">Company</label>
1180
+ <label class="text-sm text-muted-color font-semibold">{{ 'layout.company.branch.selector.company' | translate }}</label>
1117
1181
  <p-select
1118
1182
  [options]="companies()"
1119
1183
  [ngModel]="selectedCompanyId()"
1120
1184
  (ngModelChange)="onCompanyChange($event)"
1121
1185
  optionLabel="name"
1122
1186
  optionValue="id"
1123
- placeholder="Select Company"
1187
+ [placeholder]="'layout.company.branch.selector.select.company' | translate"
1124
1188
  class="w-full"
1125
1189
  [loading]="isLoadingCompanies()"
1126
1190
  />
@@ -1129,14 +1193,14 @@ class AppCompanyBranchSelector {
1129
1193
  <!-- Branch Selector -->
1130
1194
  @if (selectedCompanyId() && branches().length > 0) {
1131
1195
  <div class="flex flex-col gap-2">
1132
- <label class="text-sm text-muted-color font-semibold">Branch</label>
1196
+ <label class="text-sm text-muted-color font-semibold">{{ 'layout.company.branch.selector.branch' | translate }}</label>
1133
1197
  <p-select
1134
1198
  [options]="branches()"
1135
1199
  [ngModel]="selectedBranchId()"
1136
1200
  (ngModelChange)="onBranchChange($event)"
1137
1201
  optionLabel="name"
1138
1202
  optionValue="id"
1139
- placeholder="Select Branch"
1203
+ [placeholder]="'layout.company.branch.selector.select.branch' | translate"
1140
1204
  class="w-full"
1141
1205
  [loading]="isLoadingBranches()"
1142
1206
  />
@@ -1146,7 +1210,7 @@ class AppCompanyBranchSelector {
1146
1210
  <!-- Actions -->
1147
1211
  <div class="flex gap-2 pt-2">
1148
1212
  <p-button
1149
- label="Switch"
1213
+ [label]="'layout.company.branch.selector.switch.button' | translate"
1150
1214
  icon="pi pi-sync"
1151
1215
  styleClass="flex-1"
1152
1216
  [loading]="isSwitching()"
@@ -1156,14 +1220,13 @@ class AppCompanyBranchSelector {
1156
1220
  </div>
1157
1221
  </div>
1158
1222
  </div>
1159
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i2$1.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1223
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i2$1.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1160
1224
  }
1161
1225
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppCompanyBranchSelector, decorators: [{
1162
1226
  type: Component,
1163
1227
  args: [{
1164
1228
  selector: 'app-company-branch-selector',
1165
- changeDetection: ChangeDetectionStrategy.OnPush,
1166
- imports: [AngularModule, StyleClassModule, ButtonModule, SelectModule],
1229
+ imports: [AngularModule, StyleClassModule, ButtonModule, SelectModule, TranslatePipe],
1167
1230
  host: { class: 'relative' },
1168
1231
  template: `
1169
1232
  <button
@@ -1185,24 +1248,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1185
1248
  }
1186
1249
  </button>
1187
1250
  <div
1188
- class="hidden absolute top-[3.25rem] right-0 z-50 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)] w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[300px] max-w-[360px]"
1189
- style="background-color: var(--surface-overlay)"
1251
+ class="hidden absolute top-[3.25rem] z-50 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)] w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[300px] max-w-[360px]"
1252
+ style="background-color: var(--surface-overlay); inset-inline-end: 0;"
1190
1253
  >
1191
1254
  <div class="flex flex-col gap-4">
1192
1255
  <span class="text-sm text-muted-color font-semibold"
1193
- >Switch Company & Branch</span
1256
+ >{{ 'layout.company.branch.selector.title' | translate }}</span
1194
1257
  >
1195
1258
 
1196
1259
  <!-- Company Selector -->
1197
1260
  <div class="flex flex-col gap-2">
1198
- <label class="text-sm text-muted-color font-semibold">Company</label>
1261
+ <label class="text-sm text-muted-color font-semibold">{{ 'layout.company.branch.selector.company' | translate }}</label>
1199
1262
  <p-select
1200
1263
  [options]="companies()"
1201
1264
  [ngModel]="selectedCompanyId()"
1202
1265
  (ngModelChange)="onCompanyChange($event)"
1203
1266
  optionLabel="name"
1204
1267
  optionValue="id"
1205
- placeholder="Select Company"
1268
+ [placeholder]="'layout.company.branch.selector.select.company' | translate"
1206
1269
  class="w-full"
1207
1270
  [loading]="isLoadingCompanies()"
1208
1271
  />
@@ -1211,14 +1274,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1211
1274
  <!-- Branch Selector -->
1212
1275
  @if (selectedCompanyId() && branches().length > 0) {
1213
1276
  <div class="flex flex-col gap-2">
1214
- <label class="text-sm text-muted-color font-semibold">Branch</label>
1277
+ <label class="text-sm text-muted-color font-semibold">{{ 'layout.company.branch.selector.branch' | translate }}</label>
1215
1278
  <p-select
1216
1279
  [options]="branches()"
1217
1280
  [ngModel]="selectedBranchId()"
1218
1281
  (ngModelChange)="onBranchChange($event)"
1219
1282
  optionLabel="name"
1220
1283
  optionValue="id"
1221
- placeholder="Select Branch"
1284
+ [placeholder]="'layout.company.branch.selector.select.branch' | translate"
1222
1285
  class="w-full"
1223
1286
  [loading]="isLoadingBranches()"
1224
1287
  />
@@ -1228,7 +1291,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1228
1291
  <!-- Actions -->
1229
1292
  <div class="flex gap-2 pt-2">
1230
1293
  <p-button
1231
- label="Switch"
1294
+ [label]="'layout.company.branch.selector.switch.button' | translate"
1232
1295
  icon="pi pi-sync"
1233
1296
  styleClass="flex-1"
1234
1297
  [loading]="isSwitching()"
@@ -1277,14 +1340,14 @@ class AppLauncher {
1277
1340
  (click)="togglePanel()"
1278
1341
  >
1279
1342
  <i class="pi pi-th-large"></i>
1280
- <span>Apps</span>
1343
+ <span>{{ 'layout.launcher.apps' | translate }}</span>
1281
1344
  </button>
1282
1345
  <div
1283
- class="hidden absolute top-[3.25rem] right-0 z-50 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)] w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]"
1284
- style="background-color: var(--surface-overlay)"
1346
+ class="hidden absolute top-[3.25rem] z-50 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)] w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]"
1347
+ style="background-color: var(--surface-overlay); inset-inline-end: 0;"
1285
1348
  >
1286
1349
  <div class="flex flex-col gap-2">
1287
- <span class="text-sm text-muted-color font-semibold">Applications</span>
1350
+ <span class="text-sm text-muted-color font-semibold">{{ 'layout.launcher.applications' | translate }}</span>
1288
1351
  <div class="grid grid-cols-2 sm:grid-cols-3 gap-2">
1289
1352
  @for (app of layoutService.apps(); track app.id) {
1290
1353
  <a
@@ -1304,21 +1367,20 @@ class AppLauncher {
1304
1367
  </div>
1305
1368
  <span
1306
1369
  class="text-xs mt-2 text-center text-muted-color group-hover:text-color transition-colors duration-200 truncate max-w-full"
1307
- >{{ app.name }}</span
1370
+ >{{ app.nameKey ? (app.nameKey | translate) : app.name }}</span
1308
1371
  >
1309
1372
  </a>
1310
1373
  }
1311
1374
  </div>
1312
1375
  </div>
1313
1376
  </div>
1314
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i2$1.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "component", type: IconComponent, selector: "lib-icon", inputs: ["icon", "iconType"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1377
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i2$1.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "component", type: IconComponent, selector: "lib-icon", inputs: ["icon", "iconType"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1315
1378
  }
1316
1379
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppLauncher, decorators: [{
1317
1380
  type: Component,
1318
1381
  args: [{
1319
1382
  selector: 'app-launcher',
1320
- changeDetection: ChangeDetectionStrategy.OnPush,
1321
- imports: [StyleClassModule, IconComponent],
1383
+ imports: [StyleClassModule, IconComponent, TranslatePipe],
1322
1384
  host: { class: 'relative' },
1323
1385
  template: `
1324
1386
  <button
@@ -1334,14 +1396,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1334
1396
  (click)="togglePanel()"
1335
1397
  >
1336
1398
  <i class="pi pi-th-large"></i>
1337
- <span>Apps</span>
1399
+ <span>{{ 'layout.launcher.apps' | translate }}</span>
1338
1400
  </button>
1339
1401
  <div
1340
- class="hidden absolute top-[3.25rem] right-0 z-50 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)] w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]"
1341
- style="background-color: var(--surface-overlay)"
1402
+ class="hidden absolute top-[3.25rem] z-50 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)] w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]"
1403
+ style="background-color: var(--surface-overlay); inset-inline-end: 0;"
1342
1404
  >
1343
1405
  <div class="flex flex-col gap-2">
1344
- <span class="text-sm text-muted-color font-semibold">Applications</span>
1406
+ <span class="text-sm text-muted-color font-semibold">{{ 'layout.launcher.applications' | translate }}</span>
1345
1407
  <div class="grid grid-cols-2 sm:grid-cols-3 gap-2">
1346
1408
  @for (app of layoutService.apps(); track app.id) {
1347
1409
  <a
@@ -1361,7 +1423,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1361
1423
  </div>
1362
1424
  <span
1363
1425
  class="text-xs mt-2 text-center text-muted-color group-hover:text-color transition-colors duration-200 truncate max-w-full"
1364
- >{{ app.name }}</span
1426
+ >{{ app.nameKey ? (app.nameKey | translate) : app.name }}</span
1365
1427
  >
1366
1428
  </a>
1367
1429
  }
@@ -1378,6 +1440,7 @@ class AppProfile {
1378
1440
  authApi = inject(LAYOUT_AUTH_API, { optional: true });
1379
1441
  layoutService = inject(LayoutService);
1380
1442
  messageService = inject(MessageService);
1443
+ translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
1381
1444
  document = inject(DOCUMENT$1);
1382
1445
  elementRef = inject(ElementRef);
1383
1446
  _isActive = signal(false, ...(ngDevMode ? [{ debugName: "_isActive" }] : []));
@@ -1395,7 +1458,7 @@ class AppProfile {
1395
1458
  togglePanel() {
1396
1459
  this._isActive.update((v) => !v);
1397
1460
  }
1398
- userName = computed(() => this.authState?.loginUserData()?.name ?? 'Guest', ...(ngDevMode ? [{ debugName: "userName" }] : []));
1461
+ userName = computed(() => this.authState?.loginUserData()?.name ?? this.translate('layout.profile.guest'), ...(ngDevMode ? [{ debugName: "userName" }] : []));
1399
1462
  userEmail = computed(() => this.authState?.loginUserData()?.email ?? '', ...(ngDevMode ? [{ debugName: "userEmail" }] : []));
1400
1463
  profilePicture = computed(() => this.layoutService.userProfilePictureUrl() ?? '', ...(ngDevMode ? [{ debugName: "profilePicture" }] : []));
1401
1464
  logout() {
@@ -1408,7 +1471,7 @@ class AppProfile {
1408
1471
  next: (result) => {
1409
1472
  this.messageService.add({
1410
1473
  severity: 'success',
1411
- summary: 'Success!',
1474
+ summary: this.translate('shared.success'),
1412
1475
  detail: result.message,
1413
1476
  });
1414
1477
  this.authApi?.navigateLogin(false);
@@ -1426,18 +1489,21 @@ class AppProfile {
1426
1489
  .then(() => {
1427
1490
  this.messageService.add({
1428
1491
  severity: 'success',
1429
- summary: 'Link Copied',
1430
- detail: 'Sign Up Link copied.',
1492
+ summary: this.translate('layout.profile.link.copied'),
1493
+ detail: this.translate('layout.profile.sign.up.link.copied'),
1431
1494
  });
1432
1495
  })
1433
1496
  .catch(() => {
1434
1497
  this.messageService.add({
1435
1498
  severity: 'error',
1436
- summary: 'Sorry',
1437
- detail: 'Failed to copy signup link.',
1499
+ summary: this.translate('shared.error'),
1500
+ detail: this.translate('layout.profile.failed.copy.sign.up.link'),
1438
1501
  });
1439
1502
  });
1440
1503
  }
1504
+ translate(key) {
1505
+ return this.translateAdapter?.translate(key) ?? key;
1506
+ }
1441
1507
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppProfile, deps: [], target: i0.ɵɵFactoryTarget.Component });
1442
1508
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: AppProfile, isStandalone: true, selector: "app-profile", host: { classAttribute: "relative" }, ngImport: i0, template: `
1443
1509
  <button
@@ -1453,11 +1519,11 @@ class AppProfile {
1453
1519
  (click)="togglePanel()"
1454
1520
  >
1455
1521
  <i class="pi pi-user"></i>
1456
- <span>Profile</span>
1522
+ <span>{{ 'layout.profile.title' | translate }}</span>
1457
1523
  </button>
1458
1524
  <div
1459
- class="hidden absolute top-[3.25rem] right-0 z-50 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)] w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]"
1460
- style="background-color: var(--surface-overlay)"
1525
+ class="hidden absolute top-[3.25rem] z-50 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)] w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]"
1526
+ style="background-color: var(--surface-overlay); inset-inline-end: 0;"
1461
1527
  >
1462
1528
  <div class="flex flex-col gap-3">
1463
1529
  <!-- User Info -->
@@ -1469,7 +1535,7 @@ class AppProfile {
1469
1535
  <img
1470
1536
  [src]="profilePicture()"
1471
1537
  class="w-12 h-12 rounded-border object-cover"
1472
- alt="Profile"
1538
+ [alt]="'layout.profile.profile.picture.alt' | translate"
1473
1539
  />
1474
1540
  } @else {
1475
1541
  <div
@@ -1494,26 +1560,25 @@ class AppProfile {
1494
1560
  (click)="copySignUpLink()"
1495
1561
  >
1496
1562
  <i class="pi pi-link text-muted-color"></i>
1497
- <span>Copy SignUp Link</span>
1563
+ <span>{{ 'layout.profile.copy.sign.up.link' | translate }}</span>
1498
1564
  </a>
1499
1565
  <a
1500
1566
  class="flex items-center gap-3 p-3 rounded-border cursor-pointer no-underline text-color hover:bg-emphasis transition-all duration-200"
1501
1567
  (click)="logout()"
1502
1568
  >
1503
1569
  <i class="pi pi-sign-out text-muted-color"></i>
1504
- <span>Logout</span>
1570
+ <span>{{ 'layout.profile.logout' | translate }}</span>
1505
1571
  </a>
1506
1572
  </div>
1507
1573
  </div>
1508
1574
  </div>
1509
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2$2.IsEmptyImageDirective, selector: "img", inputs: ["src"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i2$1.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1575
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2$2.IsEmptyImageDirective, selector: "img", inputs: ["src"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i2$1.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1510
1576
  }
1511
1577
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppProfile, decorators: [{
1512
1578
  type: Component,
1513
1579
  args: [{
1514
1580
  selector: 'app-profile',
1515
- changeDetection: ChangeDetectionStrategy.OnPush,
1516
- imports: [AngularModule, StyleClassModule],
1581
+ imports: [AngularModule, StyleClassModule, TranslatePipe],
1517
1582
  host: { class: 'relative' },
1518
1583
  template: `
1519
1584
  <button
@@ -1529,11 +1594,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1529
1594
  (click)="togglePanel()"
1530
1595
  >
1531
1596
  <i class="pi pi-user"></i>
1532
- <span>Profile</span>
1597
+ <span>{{ 'layout.profile.title' | translate }}</span>
1533
1598
  </button>
1534
1599
  <div
1535
- class="hidden absolute top-[3.25rem] right-0 z-50 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)] w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]"
1536
- style="background-color: var(--surface-overlay)"
1600
+ class="hidden absolute top-[3.25rem] z-50 p-4 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)] w-[calc(100vw-2rem)] sm:w-auto sm:min-w-[280px] max-w-[320px]"
1601
+ style="background-color: var(--surface-overlay); inset-inline-end: 0;"
1537
1602
  >
1538
1603
  <div class="flex flex-col gap-3">
1539
1604
  <!-- User Info -->
@@ -1545,7 +1610,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1545
1610
  <img
1546
1611
  [src]="profilePicture()"
1547
1612
  class="w-12 h-12 rounded-border object-cover"
1548
- alt="Profile"
1613
+ [alt]="'layout.profile.profile.picture.alt' | translate"
1549
1614
  />
1550
1615
  } @else {
1551
1616
  <div
@@ -1570,14 +1635,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1570
1635
  (click)="copySignUpLink()"
1571
1636
  >
1572
1637
  <i class="pi pi-link text-muted-color"></i>
1573
- <span>Copy SignUp Link</span>
1638
+ <span>{{ 'layout.profile.copy.sign.up.link' | translate }}</span>
1574
1639
  </a>
1575
1640
  <a
1576
1641
  class="flex items-center gap-3 p-3 rounded-border cursor-pointer no-underline text-color hover:bg-emphasis transition-all duration-200"
1577
1642
  (click)="logout()"
1578
1643
  >
1579
1644
  <i class="pi pi-sign-out text-muted-color"></i>
1580
- <span>Logout</span>
1645
+ <span>{{ 'layout.profile.logout' | translate }}</span>
1581
1646
  </a>
1582
1647
  </div>
1583
1648
  </div>
@@ -1594,6 +1659,9 @@ class AppTopbar {
1594
1659
  notificationBellComponent = inject(LAYOUT_NOTIFICATION_BELL, {
1595
1660
  optional: true,
1596
1661
  });
1662
+ languageSelectorComponent = inject(LAYOUT_LANGUAGE_SELECTOR, {
1663
+ optional: true,
1664
+ });
1597
1665
  configContainer = viewChild('configContainer', ...(ngDevMode ? [{ debugName: "configContainer" }] : []));
1598
1666
  activePanel = signal(null, ...(ngDevMode ? [{ debugName: "activePanel" }] : []));
1599
1667
  constructor() {
@@ -1624,6 +1692,7 @@ class AppTopbar {
1624
1692
  <div class="layout-topbar-logo-container">
1625
1693
  <button
1626
1694
  class="layout-menu-button layout-topbar-action"
1695
+ [attr.aria-label]="'layout.topbar.toggle.menu' | translate"
1627
1696
  (click)="layoutService.onMenuToggle()"
1628
1697
  >
1629
1698
  <i class="pi pi-bars"></i>
@@ -1638,6 +1707,7 @@ class AppTopbar {
1638
1707
  <button
1639
1708
  type="button"
1640
1709
  class="layout-topbar-action"
1710
+ [attr.aria-label]="'layout.topbar.toggle.dark.mode' | translate"
1641
1711
  (click)="toggleDarkMode()"
1642
1712
  >
1643
1713
  <i
@@ -1650,6 +1720,7 @@ class AppTopbar {
1650
1720
  <button
1651
1721
  class="layout-topbar-action"
1652
1722
  [class.layout-topbar-action-highlight]="activePanel() === 'config'"
1723
+ [attr.aria-label]="'layout.topbar.open.theme.settings' | translate"
1653
1724
  pStyleClass="@next"
1654
1725
  enterFromClass="hidden"
1655
1726
  enterActiveClass="animate-scalein"
@@ -1666,6 +1737,7 @@ class AppTopbar {
1666
1737
 
1667
1738
  <button
1668
1739
  class="layout-topbar-menu-button layout-topbar-action"
1740
+ [attr.aria-label]="'layout.topbar.more.options' | translate"
1669
1741
  pStyleClass="@next"
1670
1742
  enterFromClass="hidden"
1671
1743
  enterActiveClass="animate-scalein"
@@ -1681,6 +1753,9 @@ class AppTopbar {
1681
1753
  @if (layoutService.hasApps()) {
1682
1754
  <app-launcher />
1683
1755
  }
1756
+ @if (languageSelectorComponent) {
1757
+ <ng-container *ngComponentOutlet="languageSelectorComponent" />
1758
+ }
1684
1759
  @if (enableCompanyFeature) {
1685
1760
  <app-company-branch-selector />
1686
1761
  }
@@ -1691,17 +1766,17 @@ class AppTopbar {
1691
1766
  </div>
1692
1767
  </div>
1693
1768
  </div>
1694
- </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i2$1.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: AppConfigurator, selector: "app-configurator" }, { kind: "component", type: AppProfile, selector: "app-profile" }, { kind: "component", type: AppCompanyBranchSelector, selector: "app-company-branch-selector" }, { kind: "component", type: AppLauncher, selector: "app-launcher" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1769
+ </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i2$1.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: AppConfigurator, selector: "app-configurator" }, { kind: "component", type: AppProfile, selector: "app-profile" }, { kind: "component", type: AppCompanyBranchSelector, selector: "app-company-branch-selector" }, { kind: "component", type: AppLauncher, selector: "app-launcher" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1695
1770
  }
1696
1771
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppTopbar, decorators: [{
1697
1772
  type: Component,
1698
1773
  args: [{
1699
1774
  selector: 'app-topbar',
1700
- changeDetection: ChangeDetectionStrategy.OnPush,
1701
1775
  imports: [
1702
1776
  RouterModule,
1703
1777
  StyleClassModule,
1704
1778
  NgComponentOutlet,
1779
+ TranslatePipe,
1705
1780
  AppConfigurator,
1706
1781
  AppProfile,
1707
1782
  AppCompanyBranchSelector,
@@ -1711,6 +1786,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1711
1786
  <div class="layout-topbar-logo-container">
1712
1787
  <button
1713
1788
  class="layout-menu-button layout-topbar-action"
1789
+ [attr.aria-label]="'layout.topbar.toggle.menu' | translate"
1714
1790
  (click)="layoutService.onMenuToggle()"
1715
1791
  >
1716
1792
  <i class="pi pi-bars"></i>
@@ -1725,6 +1801,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1725
1801
  <button
1726
1802
  type="button"
1727
1803
  class="layout-topbar-action"
1804
+ [attr.aria-label]="'layout.topbar.toggle.dark.mode' | translate"
1728
1805
  (click)="toggleDarkMode()"
1729
1806
  >
1730
1807
  <i
@@ -1737,6 +1814,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1737
1814
  <button
1738
1815
  class="layout-topbar-action"
1739
1816
  [class.layout-topbar-action-highlight]="activePanel() === 'config'"
1817
+ [attr.aria-label]="'layout.topbar.open.theme.settings' | translate"
1740
1818
  pStyleClass="@next"
1741
1819
  enterFromClass="hidden"
1742
1820
  enterActiveClass="animate-scalein"
@@ -1753,6 +1831,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1753
1831
 
1754
1832
  <button
1755
1833
  class="layout-topbar-menu-button layout-topbar-action"
1834
+ [attr.aria-label]="'layout.topbar.more.options' | translate"
1756
1835
  pStyleClass="@next"
1757
1836
  enterFromClass="hidden"
1758
1837
  enterActiveClass="animate-scalein"
@@ -1768,6 +1847,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1768
1847
  @if (layoutService.hasApps()) {
1769
1848
  <app-launcher />
1770
1849
  }
1850
+ @if (languageSelectorComponent) {
1851
+ <ng-container *ngComponentOutlet="languageSelectorComponent" />
1852
+ }
1771
1853
  @if (enableCompanyFeature) {
1772
1854
  <app-company-branch-selector />
1773
1855
  }
@@ -1889,7 +1971,7 @@ class AppMenuitem {
1889
1971
  class="layout-menuitem-icon"
1890
1972
  />
1891
1973
  }
1892
- <span class="layout-menuitem-text">{{ item().label }}</span>
1974
+ <span class="layout-menuitem-text">{{ item().labelKey ? (item().labelKey! | translate) : item().label }}</span>
1893
1975
  @if (item().children) {
1894
1976
  <i class="pi pi-fw pi-angle-down layout-submenu-toggler"></i>
1895
1977
  }
@@ -1911,7 +1993,7 @@ class AppMenuitem {
1911
1993
  class="layout-menuitem-icon"
1912
1994
  />
1913
1995
  }
1914
- <span class="layout-menuitem-text">{{ item().label }}</span>
1996
+ <span class="layout-menuitem-text">{{ item().labelKey ? (item().labelKey! | translate) : item().label }}</span>
1915
1997
  </a>
1916
1998
  }
1917
1999
  @if (item().children) {
@@ -1934,11 +2016,11 @@ class AppMenuitem {
1934
2016
  </ul>
1935
2017
  }
1936
2018
  </ng-container>
1937
- `, isInline: true, styles: [":host ul{overflow:hidden;transition:max-height .4s cubic-bezier(.86,0,.07,1)}:host ul.submenu-collapsed{max-height:0}:host ul.submenu-expanded{max-height:1000px}\n"], dependencies: [{ kind: "component", type: AppMenuitem, selector: "[app-menuitem]", inputs: ["item", "index", "parentKey"] }, { kind: "component", type: IconComponent, selector: "lib-icon", inputs: ["icon", "iconType"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1$2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: RippleModule }, { kind: "directive", type: i2$3.Ripple, selector: "[pRipple]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2019
+ `, isInline: true, styles: [":host ul{overflow:hidden;transition:max-height .4s cubic-bezier(.86,0,.07,1)}:host ul.submenu-collapsed{max-height:0}:host ul.submenu-expanded{max-height:1000px}\n"], dependencies: [{ kind: "component", type: AppMenuitem, selector: "[app-menuitem]", inputs: ["item", "index", "parentKey"] }, { kind: "component", type: IconComponent, selector: "lib-icon", inputs: ["icon", "iconType"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1$2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: RippleModule }, { kind: "directive", type: i2$3.Ripple, selector: "[pRipple]" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1938
2020
  }
1939
2021
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppMenuitem, decorators: [{
1940
2022
  type: Component,
1941
- args: [{ selector: '[app-menuitem]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [IconComponent, RouterModule, RippleModule], template: `
2023
+ args: [{ selector: '[app-menuitem]', imports: [IconComponent, RouterModule, RippleModule, TranslatePipe], template: `
1942
2024
  <ng-container>
1943
2025
  @if (item().children?.length) {
1944
2026
  <a (click)="itemClick()" tabindex="0" pRipple>
@@ -1949,7 +2031,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1949
2031
  class="layout-menuitem-icon"
1950
2032
  />
1951
2033
  }
1952
- <span class="layout-menuitem-text">{{ item().label }}</span>
2034
+ <span class="layout-menuitem-text">{{ item().labelKey ? (item().labelKey! | translate) : item().label }}</span>
1953
2035
  @if (item().children) {
1954
2036
  <i class="pi pi-fw pi-angle-down layout-submenu-toggler"></i>
1955
2037
  }
@@ -1971,7 +2053,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1971
2053
  class="layout-menuitem-icon"
1972
2054
  />
1973
2055
  }
1974
- <span class="layout-menuitem-text">{{ item().label }}</span>
2056
+ <span class="layout-menuitem-text">{{ item().labelKey ? (item().labelKey! | translate) : item().label }}</span>
1975
2057
  </a>
1976
2058
  }
1977
2059
  @if (item().children) {
@@ -2024,13 +2106,12 @@ class AppMenu {
2024
2106
  <li app-menuitem [item]="item" [index]="i"></li>
2025
2107
  }
2026
2108
  </ul>
2027
- </div>`, isInline: true, dependencies: [{ kind: "component", type: AppMenuitem, selector: "[app-menuitem]", inputs: ["item", "index", "parentKey"] }, { kind: "ngmodule", type: RouterModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2109
+ </div>`, isInline: true, dependencies: [{ kind: "component", type: AppMenuitem, selector: "[app-menuitem]", inputs: ["item", "index", "parentKey"] }, { kind: "ngmodule", type: RouterModule }] });
2028
2110
  }
2029
2111
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppMenu, decorators: [{
2030
2112
  type: Component,
2031
2113
  args: [{
2032
2114
  selector: 'app-menu',
2033
- changeDetection: ChangeDetectionStrategy.OnPush,
2034
2115
  imports: [AppMenuitem, RouterModule],
2035
2116
  template: `<div class="layout-menu">
2036
2117
  <ul>
@@ -2052,13 +2133,12 @@ class AppSidebar {
2052
2133
  <div class="layout-sidebar">
2053
2134
  <app-menu />
2054
2135
  </div>
2055
- `, isInline: true, dependencies: [{ kind: "component", type: AppMenu, selector: "app-menu" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2136
+ `, isInline: true, dependencies: [{ kind: "component", type: AppMenu, selector: "app-menu" }] });
2056
2137
  }
2057
2138
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppSidebar, decorators: [{
2058
2139
  type: Component,
2059
2140
  args: [{
2060
2141
  selector: 'app-sidebar',
2061
- changeDetection: ChangeDetectionStrategy.OnPush,
2062
2142
  imports: [AppMenu],
2063
2143
  template: `
2064
2144
  <div class="layout-sidebar">
@@ -2145,13 +2225,12 @@ class AppLayout {
2145
2225
  </div>
2146
2226
  <div class="layout-mask animate-fadein"></div>
2147
2227
  </div>
2148
- `, isInline: true, dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: AppTopbar, selector: "app-topbar" }, { kind: "component", type: AppSidebar, selector: "app-sidebar" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$2.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: AppFooter, selector: "app-footer" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2228
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: AppTopbar, selector: "app-topbar" }, { kind: "component", type: AppSidebar, selector: "app-sidebar" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$2.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: AppFooter, selector: "app-footer" }] });
2149
2229
  }
2150
2230
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppLayout, decorators: [{
2151
2231
  type: Component,
2152
2232
  args: [{
2153
2233
  selector: 'app-layout',
2154
- changeDetection: ChangeDetectionStrategy.OnPush,
2155
2234
  imports: [NgClass, AppTopbar, AppSidebar, RouterModule, AppFooter],
2156
2235
  template: `
2157
2236
  <div class="layout-wrapper" [ngClass]="containerClass()">
@@ -2211,11 +2290,71 @@ const GreenTheme = definePreset(Material, {
2211
2290
  },
2212
2291
  });
2213
2292
 
2293
+ const LAYOUT_MESSAGES = {
2294
+ // Menu items
2295
+ 'menu.dashboard': 'Dashboard',
2296
+ 'menu.administration': 'Administration',
2297
+ 'menu.iam': 'IAM',
2298
+ 'menu.storage': 'Storage',
2299
+ 'menu.forms': 'Forms',
2300
+ 'menu.email': 'Email',
2301
+ 'menu.event.manager': 'Event Manager',
2302
+ 'menu.notifications': 'Notifications',
2303
+ 'menu.localization': 'Localization',
2304
+ // Profile
2305
+ 'layout.profile.title': 'Profile',
2306
+ 'layout.profile.profile.picture.alt': 'Profile picture',
2307
+ 'layout.profile.copy.sign.up.link': 'Copy SignUp Link',
2308
+ 'layout.profile.logout': 'Logout',
2309
+ 'layout.profile.link.copied': 'Link Copied',
2310
+ 'layout.profile.sign.up.link.copied': 'Sign Up Link copied.',
2311
+ 'layout.profile.failed.copy.sign.up.link': 'Failed to copy signup link.',
2312
+ 'layout.profile.guest': 'Guest',
2313
+ // Company/Branch Selector
2314
+ 'layout.company.branch.selector.title': 'Switch Company & Branch',
2315
+ 'layout.company.branch.selector.company': 'Company',
2316
+ 'layout.company.branch.selector.select.company': 'Select Company',
2317
+ 'layout.company.branch.selector.branch': 'Branch',
2318
+ 'layout.company.branch.selector.select.branch': 'Select Branch',
2319
+ 'layout.company.branch.selector.switch.button': 'Switch',
2320
+ 'layout.company.branch.selector.branch.required': 'Branch Required',
2321
+ 'layout.company.branch.selector.please.select.branch': 'Please select a branch',
2322
+ 'layout.company.branch.selector.no.company': 'No Company',
2323
+ // Launcher
2324
+ 'layout.launcher.apps': 'Apps',
2325
+ 'layout.launcher.applications': 'Applications',
2326
+ // Configurator
2327
+ 'layout.configurator.primary': 'Primary',
2328
+ 'layout.configurator.surface': 'Surface',
2329
+ 'layout.configurator.presets': 'Presets',
2330
+ 'layout.configurator.menu.mode': 'Menu Mode',
2331
+ 'layout.configurator.menu.mode.static': 'Static',
2332
+ 'layout.configurator.menu.mode.overlay': 'Overlay',
2333
+ // Topbar accessibility
2334
+ 'layout.topbar.toggle.menu': 'Toggle menu',
2335
+ 'layout.topbar.toggle.dark.mode': 'Toggle dark mode',
2336
+ 'layout.topbar.open.theme.settings': 'Open theme settings',
2337
+ 'layout.topbar.more.options': 'More options',
2338
+ // Footer
2339
+ 'layout.footer.by': 'by',
2340
+ // Notifications
2341
+ 'notification.title': 'Notifications',
2342
+ 'notification.mark.all.read': 'Mark all as read',
2343
+ 'notification.view.all': 'View all',
2344
+ 'notification.time.just.now': 'Just now',
2345
+ 'notification.time.minutes.ago': '{{count}}m ago',
2346
+ 'notification.time.hours.ago': '{{count}}h ago',
2347
+ 'notification.time.days.ago': '{{count}}d ago',
2348
+ 'notification.empty': 'No notifications',
2349
+ // Home/Dashboard
2350
+ 'home.where.users.from': 'Where users are from',
2351
+ };
2352
+
2214
2353
  // Components
2215
2354
 
2216
2355
  /**
2217
2356
  * Generated bundle index. Do not edit.
2218
2357
  */
2219
2358
 
2220
- export { AppCompanyBranchSelector, AppConfigurator, AppFloatingConfigurator, AppFooter, AppLauncher, AppLayout, AppMenu, AppMenuitem, AppProfile, AppSidebar, AppTopbar, GreenTheme, LAYOUT_AUTH_API, LAYOUT_AUTH_STATE, LAYOUT_NOTIFICATION_BELL, LayoutPersistenceService, LayoutService, NavyBlueTheme, filterAppsByPermissions, filterMenuByPermissions };
2359
+ export { AppCompanyBranchSelector, AppConfigurator, AppFloatingConfigurator, AppFooter, AppLauncher, AppLayout, AppMenu, AppMenuitem, AppProfile, AppSidebar, AppTopbar, GreenTheme, LAYOUT_AUTH_API, LAYOUT_AUTH_STATE, LAYOUT_IS_RTL, LAYOUT_LANGUAGE_SELECTOR, LAYOUT_LANGUAGE_SELECTOR_PANEL, LAYOUT_MESSAGES, LAYOUT_NOTIFICATION_BELL, LayoutPersistenceService, LayoutService, NavyBlueTheme, filterAppsByPermissions, filterMenuByPermissions };
2221
2360
  //# sourceMappingURL=flusys-ng-layout.mjs.map