@flusys/ng-layout 3.0.0 → 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 } 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,51 +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
-
950
1011
  /**
951
1012
  * View Transitions API type definitions
952
1013
  * @see https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API
@@ -958,6 +1019,7 @@ class AppCompanyBranchSelector {
958
1019
  authState = inject(LAYOUT_AUTH_STATE, { optional: true });
959
1020
  authApi = inject(LAYOUT_AUTH_API, { optional: true });
960
1021
  messageService = inject(MessageService);
1022
+ translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
961
1023
  document = inject(DOCUMENT$1);
962
1024
  elementRef = inject(ElementRef);
963
1025
  _isActive = signal(false, ...(ngDevMode ? [{ debugName: "_isActive" }] : []));
@@ -972,7 +1034,7 @@ class AppCompanyBranchSelector {
972
1034
  }
973
1035
  });
974
1036
  }
975
- 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" }] : []));
976
1038
  currentBranchName = computed(() => this.authState?.currentBranchInfo()?.name ?? null, ...(ngDevMode ? [{ debugName: "currentBranchName" }] : []));
977
1039
  _companies = signal([], ...(ngDevMode ? [{ debugName: "_companies" }] : []));
978
1040
  companies = this._companies.asReadonly();
@@ -1064,8 +1126,8 @@ class AppCompanyBranchSelector {
1064
1126
  if (this.branches().length > 0 && !selectedBranch) {
1065
1127
  this.messageService.add({
1066
1128
  severity: 'warn',
1067
- summary: 'Branch Required',
1068
- 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'),
1069
1131
  });
1070
1132
  return;
1071
1133
  }
@@ -1081,6 +1143,9 @@ class AppCompanyBranchSelector {
1081
1143
  },
1082
1144
  });
1083
1145
  }
1146
+ translate(key) {
1147
+ return this.translateAdapter?.translate(key) ?? key;
1148
+ }
1084
1149
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppCompanyBranchSelector, deps: [], target: i0.ɵɵFactoryTarget.Component });
1085
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: `
1086
1151
  <button
@@ -1102,24 +1167,24 @@ class AppCompanyBranchSelector {
1102
1167
  }
1103
1168
  </button>
1104
1169
  <div
1105
- 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]"
1106
- 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;"
1107
1172
  >
1108
1173
  <div class="flex flex-col gap-4">
1109
1174
  <span class="text-sm text-muted-color font-semibold"
1110
- >Switch Company & Branch</span
1175
+ >{{ 'layout.company.branch.selector.title' | translate }}</span
1111
1176
  >
1112
1177
 
1113
1178
  <!-- Company Selector -->
1114
1179
  <div class="flex flex-col gap-2">
1115
- <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>
1116
1181
  <p-select
1117
1182
  [options]="companies()"
1118
1183
  [ngModel]="selectedCompanyId()"
1119
1184
  (ngModelChange)="onCompanyChange($event)"
1120
1185
  optionLabel="name"
1121
1186
  optionValue="id"
1122
- placeholder="Select Company"
1187
+ [placeholder]="'layout.company.branch.selector.select.company' | translate"
1123
1188
  class="w-full"
1124
1189
  [loading]="isLoadingCompanies()"
1125
1190
  />
@@ -1128,14 +1193,14 @@ class AppCompanyBranchSelector {
1128
1193
  <!-- Branch Selector -->
1129
1194
  @if (selectedCompanyId() && branches().length > 0) {
1130
1195
  <div class="flex flex-col gap-2">
1131
- <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>
1132
1197
  <p-select
1133
1198
  [options]="branches()"
1134
1199
  [ngModel]="selectedBranchId()"
1135
1200
  (ngModelChange)="onBranchChange($event)"
1136
1201
  optionLabel="name"
1137
1202
  optionValue="id"
1138
- placeholder="Select Branch"
1203
+ [placeholder]="'layout.company.branch.selector.select.branch' | translate"
1139
1204
  class="w-full"
1140
1205
  [loading]="isLoadingBranches()"
1141
1206
  />
@@ -1145,7 +1210,7 @@ class AppCompanyBranchSelector {
1145
1210
  <!-- Actions -->
1146
1211
  <div class="flex gap-2 pt-2">
1147
1212
  <p-button
1148
- label="Switch"
1213
+ [label]="'layout.company.branch.selector.switch.button' | translate"
1149
1214
  icon="pi pi-sync"
1150
1215
  styleClass="flex-1"
1151
1216
  [loading]="isSwitching()"
@@ -1155,14 +1220,13 @@ class AppCompanyBranchSelector {
1155
1220
  </div>
1156
1221
  </div>
1157
1222
  </div>
1158
- `, 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" }] });
1159
1224
  }
1160
1225
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppCompanyBranchSelector, decorators: [{
1161
1226
  type: Component,
1162
1227
  args: [{
1163
1228
  selector: 'app-company-branch-selector',
1164
- changeDetection: ChangeDetectionStrategy.OnPush,
1165
- imports: [AngularModule, StyleClassModule, ButtonModule, SelectModule],
1229
+ imports: [AngularModule, StyleClassModule, ButtonModule, SelectModule, TranslatePipe],
1166
1230
  host: { class: 'relative' },
1167
1231
  template: `
1168
1232
  <button
@@ -1184,24 +1248,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1184
1248
  }
1185
1249
  </button>
1186
1250
  <div
1187
- 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]"
1188
- 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;"
1189
1253
  >
1190
1254
  <div class="flex flex-col gap-4">
1191
1255
  <span class="text-sm text-muted-color font-semibold"
1192
- >Switch Company & Branch</span
1256
+ >{{ 'layout.company.branch.selector.title' | translate }}</span
1193
1257
  >
1194
1258
 
1195
1259
  <!-- Company Selector -->
1196
1260
  <div class="flex flex-col gap-2">
1197
- <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>
1198
1262
  <p-select
1199
1263
  [options]="companies()"
1200
1264
  [ngModel]="selectedCompanyId()"
1201
1265
  (ngModelChange)="onCompanyChange($event)"
1202
1266
  optionLabel="name"
1203
1267
  optionValue="id"
1204
- placeholder="Select Company"
1268
+ [placeholder]="'layout.company.branch.selector.select.company' | translate"
1205
1269
  class="w-full"
1206
1270
  [loading]="isLoadingCompanies()"
1207
1271
  />
@@ -1210,14 +1274,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1210
1274
  <!-- Branch Selector -->
1211
1275
  @if (selectedCompanyId() && branches().length > 0) {
1212
1276
  <div class="flex flex-col gap-2">
1213
- <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>
1214
1278
  <p-select
1215
1279
  [options]="branches()"
1216
1280
  [ngModel]="selectedBranchId()"
1217
1281
  (ngModelChange)="onBranchChange($event)"
1218
1282
  optionLabel="name"
1219
1283
  optionValue="id"
1220
- placeholder="Select Branch"
1284
+ [placeholder]="'layout.company.branch.selector.select.branch' | translate"
1221
1285
  class="w-full"
1222
1286
  [loading]="isLoadingBranches()"
1223
1287
  />
@@ -1227,7 +1291,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1227
1291
  <!-- Actions -->
1228
1292
  <div class="flex gap-2 pt-2">
1229
1293
  <p-button
1230
- label="Switch"
1294
+ [label]="'layout.company.branch.selector.switch.button' | translate"
1231
1295
  icon="pi pi-sync"
1232
1296
  styleClass="flex-1"
1233
1297
  [loading]="isSwitching()"
@@ -1276,14 +1340,14 @@ class AppLauncher {
1276
1340
  (click)="togglePanel()"
1277
1341
  >
1278
1342
  <i class="pi pi-th-large"></i>
1279
- <span>Apps</span>
1343
+ <span>{{ 'layout.launcher.apps' | translate }}</span>
1280
1344
  </button>
1281
1345
  <div
1282
- 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]"
1283
- 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;"
1284
1348
  >
1285
1349
  <div class="flex flex-col gap-2">
1286
- <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>
1287
1351
  <div class="grid grid-cols-2 sm:grid-cols-3 gap-2">
1288
1352
  @for (app of layoutService.apps(); track app.id) {
1289
1353
  <a
@@ -1303,21 +1367,20 @@ class AppLauncher {
1303
1367
  </div>
1304
1368
  <span
1305
1369
  class="text-xs mt-2 text-center text-muted-color group-hover:text-color transition-colors duration-200 truncate max-w-full"
1306
- >{{ app.name }}</span
1370
+ >{{ app.nameKey ? (app.nameKey | translate) : app.name }}</span
1307
1371
  >
1308
1372
  </a>
1309
1373
  }
1310
1374
  </div>
1311
1375
  </div>
1312
1376
  </div>
1313
- `, 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" }] });
1314
1378
  }
1315
1379
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppLauncher, decorators: [{
1316
1380
  type: Component,
1317
1381
  args: [{
1318
1382
  selector: 'app-launcher',
1319
- changeDetection: ChangeDetectionStrategy.OnPush,
1320
- imports: [StyleClassModule, IconComponent],
1383
+ imports: [StyleClassModule, IconComponent, TranslatePipe],
1321
1384
  host: { class: 'relative' },
1322
1385
  template: `
1323
1386
  <button
@@ -1333,14 +1396,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1333
1396
  (click)="togglePanel()"
1334
1397
  >
1335
1398
  <i class="pi pi-th-large"></i>
1336
- <span>Apps</span>
1399
+ <span>{{ 'layout.launcher.apps' | translate }}</span>
1337
1400
  </button>
1338
1401
  <div
1339
- 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]"
1340
- 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;"
1341
1404
  >
1342
1405
  <div class="flex flex-col gap-2">
1343
- <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>
1344
1407
  <div class="grid grid-cols-2 sm:grid-cols-3 gap-2">
1345
1408
  @for (app of layoutService.apps(); track app.id) {
1346
1409
  <a
@@ -1360,7 +1423,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1360
1423
  </div>
1361
1424
  <span
1362
1425
  class="text-xs mt-2 text-center text-muted-color group-hover:text-color transition-colors duration-200 truncate max-w-full"
1363
- >{{ app.name }}</span
1426
+ >{{ app.nameKey ? (app.nameKey | translate) : app.name }}</span
1364
1427
  >
1365
1428
  </a>
1366
1429
  }
@@ -1377,6 +1440,7 @@ class AppProfile {
1377
1440
  authApi = inject(LAYOUT_AUTH_API, { optional: true });
1378
1441
  layoutService = inject(LayoutService);
1379
1442
  messageService = inject(MessageService);
1443
+ translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
1380
1444
  document = inject(DOCUMENT$1);
1381
1445
  elementRef = inject(ElementRef);
1382
1446
  _isActive = signal(false, ...(ngDevMode ? [{ debugName: "_isActive" }] : []));
@@ -1394,7 +1458,7 @@ class AppProfile {
1394
1458
  togglePanel() {
1395
1459
  this._isActive.update((v) => !v);
1396
1460
  }
1397
- 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" }] : []));
1398
1462
  userEmail = computed(() => this.authState?.loginUserData()?.email ?? '', ...(ngDevMode ? [{ debugName: "userEmail" }] : []));
1399
1463
  profilePicture = computed(() => this.layoutService.userProfilePictureUrl() ?? '', ...(ngDevMode ? [{ debugName: "profilePicture" }] : []));
1400
1464
  logout() {
@@ -1407,7 +1471,7 @@ class AppProfile {
1407
1471
  next: (result) => {
1408
1472
  this.messageService.add({
1409
1473
  severity: 'success',
1410
- summary: 'Success!',
1474
+ summary: this.translate('shared.success'),
1411
1475
  detail: result.message,
1412
1476
  });
1413
1477
  this.authApi?.navigateLogin(false);
@@ -1425,18 +1489,21 @@ class AppProfile {
1425
1489
  .then(() => {
1426
1490
  this.messageService.add({
1427
1491
  severity: 'success',
1428
- summary: 'Link Copied',
1429
- detail: 'Sign Up Link copied.',
1492
+ summary: this.translate('layout.profile.link.copied'),
1493
+ detail: this.translate('layout.profile.sign.up.link.copied'),
1430
1494
  });
1431
1495
  })
1432
1496
  .catch(() => {
1433
1497
  this.messageService.add({
1434
1498
  severity: 'error',
1435
- summary: 'Sorry',
1436
- detail: 'Failed to copy signup link.',
1499
+ summary: this.translate('shared.error'),
1500
+ detail: this.translate('layout.profile.failed.copy.sign.up.link'),
1437
1501
  });
1438
1502
  });
1439
1503
  }
1504
+ translate(key) {
1505
+ return this.translateAdapter?.translate(key) ?? key;
1506
+ }
1440
1507
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppProfile, deps: [], target: i0.ɵɵFactoryTarget.Component });
1441
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: `
1442
1509
  <button
@@ -1452,11 +1519,11 @@ class AppProfile {
1452
1519
  (click)="togglePanel()"
1453
1520
  >
1454
1521
  <i class="pi pi-user"></i>
1455
- <span>Profile</span>
1522
+ <span>{{ 'layout.profile.title' | translate }}</span>
1456
1523
  </button>
1457
1524
  <div
1458
- 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]"
1459
- 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;"
1460
1527
  >
1461
1528
  <div class="flex flex-col gap-3">
1462
1529
  <!-- User Info -->
@@ -1468,7 +1535,7 @@ class AppProfile {
1468
1535
  <img
1469
1536
  [src]="profilePicture()"
1470
1537
  class="w-12 h-12 rounded-border object-cover"
1471
- alt="Profile"
1538
+ [alt]="'layout.profile.profile.picture.alt' | translate"
1472
1539
  />
1473
1540
  } @else {
1474
1541
  <div
@@ -1493,26 +1560,25 @@ class AppProfile {
1493
1560
  (click)="copySignUpLink()"
1494
1561
  >
1495
1562
  <i class="pi pi-link text-muted-color"></i>
1496
- <span>Copy SignUp Link</span>
1563
+ <span>{{ 'layout.profile.copy.sign.up.link' | translate }}</span>
1497
1564
  </a>
1498
1565
  <a
1499
1566
  class="flex items-center gap-3 p-3 rounded-border cursor-pointer no-underline text-color hover:bg-emphasis transition-all duration-200"
1500
1567
  (click)="logout()"
1501
1568
  >
1502
1569
  <i class="pi pi-sign-out text-muted-color"></i>
1503
- <span>Logout</span>
1570
+ <span>{{ 'layout.profile.logout' | translate }}</span>
1504
1571
  </a>
1505
1572
  </div>
1506
1573
  </div>
1507
1574
  </div>
1508
- `, 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" }] });
1509
1576
  }
1510
1577
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppProfile, decorators: [{
1511
1578
  type: Component,
1512
1579
  args: [{
1513
1580
  selector: 'app-profile',
1514
- changeDetection: ChangeDetectionStrategy.OnPush,
1515
- imports: [AngularModule, StyleClassModule],
1581
+ imports: [AngularModule, StyleClassModule, TranslatePipe],
1516
1582
  host: { class: 'relative' },
1517
1583
  template: `
1518
1584
  <button
@@ -1528,11 +1594,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1528
1594
  (click)="togglePanel()"
1529
1595
  >
1530
1596
  <i class="pi pi-user"></i>
1531
- <span>Profile</span>
1597
+ <span>{{ 'layout.profile.title' | translate }}</span>
1532
1598
  </button>
1533
1599
  <div
1534
- 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]"
1535
- 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;"
1536
1602
  >
1537
1603
  <div class="flex flex-col gap-3">
1538
1604
  <!-- User Info -->
@@ -1544,7 +1610,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1544
1610
  <img
1545
1611
  [src]="profilePicture()"
1546
1612
  class="w-12 h-12 rounded-border object-cover"
1547
- alt="Profile"
1613
+ [alt]="'layout.profile.profile.picture.alt' | translate"
1548
1614
  />
1549
1615
  } @else {
1550
1616
  <div
@@ -1569,14 +1635,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1569
1635
  (click)="copySignUpLink()"
1570
1636
  >
1571
1637
  <i class="pi pi-link text-muted-color"></i>
1572
- <span>Copy SignUp Link</span>
1638
+ <span>{{ 'layout.profile.copy.sign.up.link' | translate }}</span>
1573
1639
  </a>
1574
1640
  <a
1575
1641
  class="flex items-center gap-3 p-3 rounded-border cursor-pointer no-underline text-color hover:bg-emphasis transition-all duration-200"
1576
1642
  (click)="logout()"
1577
1643
  >
1578
1644
  <i class="pi pi-sign-out text-muted-color"></i>
1579
- <span>Logout</span>
1645
+ <span>{{ 'layout.profile.logout' | translate }}</span>
1580
1646
  </a>
1581
1647
  </div>
1582
1648
  </div>
@@ -1590,6 +1656,12 @@ class AppTopbar {
1590
1656
  document = inject(DOCUMENT$1);
1591
1657
  destroyRef = inject(DestroyRef);
1592
1658
  layoutService = inject(LayoutService);
1659
+ notificationBellComponent = inject(LAYOUT_NOTIFICATION_BELL, {
1660
+ optional: true,
1661
+ });
1662
+ languageSelectorComponent = inject(LAYOUT_LANGUAGE_SELECTOR, {
1663
+ optional: true,
1664
+ });
1593
1665
  configContainer = viewChild('configContainer', ...(ngDevMode ? [{ debugName: "configContainer" }] : []));
1594
1666
  activePanel = signal(null, ...(ngDevMode ? [{ debugName: "activePanel" }] : []));
1595
1667
  constructor() {
@@ -1600,9 +1672,7 @@ class AppTopbar {
1600
1672
  });
1601
1673
  }
1602
1674
  companyName = this.layoutService.companyName;
1603
- enableCompanyFeature = computed(() => {
1604
- return isCompanyFeatureEnabled(this.appConfig);
1605
- }, ...(ngDevMode ? [{ debugName: "enableCompanyFeature" }] : []));
1675
+ enableCompanyFeature = isCompanyFeatureEnabled(this.appConfig);
1606
1676
  toggleDarkMode() {
1607
1677
  const currentDarkTheme = this.layoutService.layoutConfig().darkTheme;
1608
1678
  this.layoutService.updateLayoutConfig({ darkTheme: !currentDarkTheme });
@@ -1622,6 +1692,7 @@ class AppTopbar {
1622
1692
  <div class="layout-topbar-logo-container">
1623
1693
  <button
1624
1694
  class="layout-menu-button layout-topbar-action"
1695
+ [attr.aria-label]="'layout.topbar.toggle.menu' | translate"
1625
1696
  (click)="layoutService.onMenuToggle()"
1626
1697
  >
1627
1698
  <i class="pi pi-bars"></i>
@@ -1636,6 +1707,7 @@ class AppTopbar {
1636
1707
  <button
1637
1708
  type="button"
1638
1709
  class="layout-topbar-action"
1710
+ [attr.aria-label]="'layout.topbar.toggle.dark.mode' | translate"
1639
1711
  (click)="toggleDarkMode()"
1640
1712
  >
1641
1713
  <i
@@ -1648,6 +1720,7 @@ class AppTopbar {
1648
1720
  <button
1649
1721
  class="layout-topbar-action"
1650
1722
  [class.layout-topbar-action-highlight]="activePanel() === 'config'"
1723
+ [attr.aria-label]="'layout.topbar.open.theme.settings' | translate"
1651
1724
  pStyleClass="@next"
1652
1725
  enterFromClass="hidden"
1653
1726
  enterActiveClass="animate-scalein"
@@ -1664,6 +1737,7 @@ class AppTopbar {
1664
1737
 
1665
1738
  <button
1666
1739
  class="layout-topbar-menu-button layout-topbar-action"
1740
+ [attr.aria-label]="'layout.topbar.more.options' | translate"
1667
1741
  pStyleClass="@next"
1668
1742
  enterFromClass="hidden"
1669
1743
  enterActiveClass="animate-scalein"
@@ -1679,23 +1753,30 @@ class AppTopbar {
1679
1753
  @if (layoutService.hasApps()) {
1680
1754
  <app-launcher />
1681
1755
  }
1682
- @if (enableCompanyFeature()) {
1756
+ @if (languageSelectorComponent) {
1757
+ <ng-container *ngComponentOutlet="languageSelectorComponent" />
1758
+ }
1759
+ @if (enableCompanyFeature) {
1683
1760
  <app-company-branch-selector />
1684
1761
  }
1762
+ @if (notificationBellComponent) {
1763
+ <ng-container *ngComponentOutlet="notificationBellComponent" />
1764
+ }
1685
1765
  <app-profile />
1686
1766
  </div>
1687
1767
  </div>
1688
1768
  </div>
1689
- </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: "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" }] });
1690
1770
  }
1691
1771
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppTopbar, decorators: [{
1692
1772
  type: Component,
1693
1773
  args: [{
1694
1774
  selector: 'app-topbar',
1695
- changeDetection: ChangeDetectionStrategy.OnPush,
1696
1775
  imports: [
1697
1776
  RouterModule,
1698
1777
  StyleClassModule,
1778
+ NgComponentOutlet,
1779
+ TranslatePipe,
1699
1780
  AppConfigurator,
1700
1781
  AppProfile,
1701
1782
  AppCompanyBranchSelector,
@@ -1705,6 +1786,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1705
1786
  <div class="layout-topbar-logo-container">
1706
1787
  <button
1707
1788
  class="layout-menu-button layout-topbar-action"
1789
+ [attr.aria-label]="'layout.topbar.toggle.menu' | translate"
1708
1790
  (click)="layoutService.onMenuToggle()"
1709
1791
  >
1710
1792
  <i class="pi pi-bars"></i>
@@ -1719,6 +1801,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1719
1801
  <button
1720
1802
  type="button"
1721
1803
  class="layout-topbar-action"
1804
+ [attr.aria-label]="'layout.topbar.toggle.dark.mode' | translate"
1722
1805
  (click)="toggleDarkMode()"
1723
1806
  >
1724
1807
  <i
@@ -1731,6 +1814,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1731
1814
  <button
1732
1815
  class="layout-topbar-action"
1733
1816
  [class.layout-topbar-action-highlight]="activePanel() === 'config'"
1817
+ [attr.aria-label]="'layout.topbar.open.theme.settings' | translate"
1734
1818
  pStyleClass="@next"
1735
1819
  enterFromClass="hidden"
1736
1820
  enterActiveClass="animate-scalein"
@@ -1747,6 +1831,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1747
1831
 
1748
1832
  <button
1749
1833
  class="layout-topbar-menu-button layout-topbar-action"
1834
+ [attr.aria-label]="'layout.topbar.more.options' | translate"
1750
1835
  pStyleClass="@next"
1751
1836
  enterFromClass="hidden"
1752
1837
  enterActiveClass="animate-scalein"
@@ -1762,9 +1847,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1762
1847
  @if (layoutService.hasApps()) {
1763
1848
  <app-launcher />
1764
1849
  }
1765
- @if (enableCompanyFeature()) {
1850
+ @if (languageSelectorComponent) {
1851
+ <ng-container *ngComponentOutlet="languageSelectorComponent" />
1852
+ }
1853
+ @if (enableCompanyFeature) {
1766
1854
  <app-company-branch-selector />
1767
1855
  }
1856
+ @if (notificationBellComponent) {
1857
+ <ng-container *ngComponentOutlet="notificationBellComponent" />
1858
+ }
1768
1859
  <app-profile />
1769
1860
  </div>
1770
1861
  </div>
@@ -1880,7 +1971,7 @@ class AppMenuitem {
1880
1971
  class="layout-menuitem-icon"
1881
1972
  />
1882
1973
  }
1883
- <span class="layout-menuitem-text">{{ item().label }}</span>
1974
+ <span class="layout-menuitem-text">{{ item().labelKey ? (item().labelKey! | translate) : item().label }}</span>
1884
1975
  @if (item().children) {
1885
1976
  <i class="pi pi-fw pi-angle-down layout-submenu-toggler"></i>
1886
1977
  }
@@ -1902,7 +1993,7 @@ class AppMenuitem {
1902
1993
  class="layout-menuitem-icon"
1903
1994
  />
1904
1995
  }
1905
- <span class="layout-menuitem-text">{{ item().label }}</span>
1996
+ <span class="layout-menuitem-text">{{ item().labelKey ? (item().labelKey! | translate) : item().label }}</span>
1906
1997
  </a>
1907
1998
  }
1908
1999
  @if (item().children) {
@@ -1925,11 +2016,11 @@ class AppMenuitem {
1925
2016
  </ul>
1926
2017
  }
1927
2018
  </ng-container>
1928
- `, 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" }] });
1929
2020
  }
1930
2021
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppMenuitem, decorators: [{
1931
2022
  type: Component,
1932
- args: [{ selector: '[app-menuitem]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [IconComponent, RouterModule, RippleModule], template: `
2023
+ args: [{ selector: '[app-menuitem]', imports: [IconComponent, RouterModule, RippleModule, TranslatePipe], template: `
1933
2024
  <ng-container>
1934
2025
  @if (item().children?.length) {
1935
2026
  <a (click)="itemClick()" tabindex="0" pRipple>
@@ -1940,7 +2031,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1940
2031
  class="layout-menuitem-icon"
1941
2032
  />
1942
2033
  }
1943
- <span class="layout-menuitem-text">{{ item().label }}</span>
2034
+ <span class="layout-menuitem-text">{{ item().labelKey ? (item().labelKey! | translate) : item().label }}</span>
1944
2035
  @if (item().children) {
1945
2036
  <i class="pi pi-fw pi-angle-down layout-submenu-toggler"></i>
1946
2037
  }
@@ -1962,7 +2053,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1962
2053
  class="layout-menuitem-icon"
1963
2054
  />
1964
2055
  }
1965
- <span class="layout-menuitem-text">{{ item().label }}</span>
2056
+ <span class="layout-menuitem-text">{{ item().labelKey ? (item().labelKey! | translate) : item().label }}</span>
1966
2057
  </a>
1967
2058
  }
1968
2059
  @if (item().children) {
@@ -2015,13 +2106,12 @@ class AppMenu {
2015
2106
  <li app-menuitem [item]="item" [index]="i"></li>
2016
2107
  }
2017
2108
  </ul>
2018
- </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 }] });
2019
2110
  }
2020
2111
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppMenu, decorators: [{
2021
2112
  type: Component,
2022
2113
  args: [{
2023
2114
  selector: 'app-menu',
2024
- changeDetection: ChangeDetectionStrategy.OnPush,
2025
2115
  imports: [AppMenuitem, RouterModule],
2026
2116
  template: `<div class="layout-menu">
2027
2117
  <ul>
@@ -2043,13 +2133,12 @@ class AppSidebar {
2043
2133
  <div class="layout-sidebar">
2044
2134
  <app-menu />
2045
2135
  </div>
2046
- `, 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" }] });
2047
2137
  }
2048
2138
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppSidebar, decorators: [{
2049
2139
  type: Component,
2050
2140
  args: [{
2051
2141
  selector: 'app-sidebar',
2052
- changeDetection: ChangeDetectionStrategy.OnPush,
2053
2142
  imports: [AppMenu],
2054
2143
  template: `
2055
2144
  <div class="layout-sidebar">
@@ -2136,13 +2225,12 @@ class AppLayout {
2136
2225
  </div>
2137
2226
  <div class="layout-mask animate-fadein"></div>
2138
2227
  </div>
2139
- `, 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" }] });
2140
2229
  }
2141
2230
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AppLayout, decorators: [{
2142
2231
  type: Component,
2143
2232
  args: [{
2144
2233
  selector: 'app-layout',
2145
- changeDetection: ChangeDetectionStrategy.OnPush,
2146
2234
  imports: [NgClass, AppTopbar, AppSidebar, RouterModule, AppFooter],
2147
2235
  template: `
2148
2236
  <div class="layout-wrapper" [ngClass]="containerClass()">
@@ -2202,11 +2290,71 @@ const GreenTheme = definePreset(Material, {
2202
2290
  },
2203
2291
  });
2204
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
+
2205
2353
  // Components
2206
2354
 
2207
2355
  /**
2208
2356
  * Generated bundle index. Do not edit.
2209
2357
  */
2210
2358
 
2211
- export { AppCompanyBranchSelector, AppConfigurator, AppFloatingConfigurator, AppFooter, AppLauncher, AppLayout, AppMenu, AppMenuitem, AppProfile, AppSidebar, AppTopbar, GreenTheme, LAYOUT_AUTH_API, LAYOUT_AUTH_STATE, 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 };
2212
2360
  //# sourceMappingURL=flusys-ng-layout.mjs.map