@es.framework/ng.ui.theme 2.0.59 → 2.0.61

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.
@@ -18,7 +18,7 @@ import { ButtonModule } from 'primeng/button';
18
18
  import { OverlayModule } from 'primeng/overlay';
19
19
  import { VisibleDirective, HasPermissionDirective, ReplaceableTemplateDirective } from '@es.framework/ng.core/directives';
20
20
  import { TranslatePipe, ToInjectorPipe } from '@es.framework/ng.core/pipes';
21
- import { toSignal } from '@angular/core/rxjs-interop';
21
+ import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
22
22
  import * as i2$1 from '@angular/forms';
23
23
  import { FormsModule } from '@angular/forms';
24
24
  import { SelectModule } from 'primeng/select';
@@ -210,36 +210,36 @@ class LanguagesComponent {
210
210
  }
211
211
  }
212
212
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LanguagesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
213
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: LanguagesComponent, isStandalone: true, selector: "abp-languages", ngImport: i0, template: `
214
- @if (languages(); as langs) {
215
- <div>
216
- <!-- -->
217
- <button
218
- pButton
219
- type="button"
220
- icon="pi pi-globe"
221
- class="p-button-text"
222
- [title]="'اختر اللغة'"
223
- (click)="overlay.toggle($event)"
224
- ></button>
225
- <p-popover #overlay >
226
- <ul class="w-48">
227
- @for (lang of langs; track lang) {
228
- <li
229
- class="flex items-center justify-between px-3 py-2 cursor-pointer hover:bg-gray-100"
230
- (click)="onSelectLanguage(lang.cultureName ?? ''); overlay.hide()"
231
- >
232
- <span>{{ lang.displayName }}</span>
233
- @if (lang.cultureName === selectedLang()) {
234
- <i class="pi pi-check text-green-500"></i>
235
- }
236
- </li>
237
- }
238
- </ul>
239
- </p-popover>
240
- </div>
241
- }
242
-
213
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: LanguagesComponent, isStandalone: true, selector: "abp-languages", ngImport: i0, template: `
214
+ @if (languages(); as langs) {
215
+ <div>
216
+ <!-- -->
217
+ <button
218
+ pButton
219
+ type="button"
220
+ icon="pi pi-globe"
221
+ class="p-button-text"
222
+ [title]="'اختر اللغة'"
223
+ (click)="overlay.toggle($event)"
224
+ ></button>
225
+ <p-popover #overlay >
226
+ <ul class="w-48">
227
+ @for (lang of langs; track lang) {
228
+ <li
229
+ class="flex items-center justify-between px-3 py-2 cursor-pointer hover:bg-gray-100"
230
+ (click)="onSelectLanguage(lang.cultureName ?? ''); overlay.hide()"
231
+ >
232
+ <span>{{ lang.displayName }}</span>
233
+ @if (lang.cultureName === selectedLang()) {
234
+ <i class="pi pi-check text-green-500"></i>
235
+ }
236
+ </li>
237
+ }
238
+ </ul>
239
+ </p-popover>
240
+ </div>
241
+ }
242
+
243
243
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: SelectModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i1.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }] });
244
244
  }
245
245
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LanguagesComponent, decorators: [{
@@ -248,36 +248,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
248
248
  selector: 'abp-languages',
249
249
  standalone: true,
250
250
  imports: [SelectModule, FormsModule, PopoverModule, ButtonModule],
251
- template: `
252
- @if (languages(); as langs) {
253
- <div>
254
- <!-- -->
255
- <button
256
- pButton
257
- type="button"
258
- icon="pi pi-globe"
259
- class="p-button-text"
260
- [title]="'اختر اللغة'"
261
- (click)="overlay.toggle($event)"
262
- ></button>
263
- <p-popover #overlay >
264
- <ul class="w-48">
265
- @for (lang of langs; track lang) {
266
- <li
267
- class="flex items-center justify-between px-3 py-2 cursor-pointer hover:bg-gray-100"
268
- (click)="onSelectLanguage(lang.cultureName ?? ''); overlay.hide()"
269
- >
270
- <span>{{ lang.displayName }}</span>
271
- @if (lang.cultureName === selectedLang()) {
272
- <i class="pi pi-check text-green-500"></i>
273
- }
274
- </li>
275
- }
276
- </ul>
277
- </p-popover>
278
- </div>
279
- }
280
-
251
+ template: `
252
+ @if (languages(); as langs) {
253
+ <div>
254
+ <!-- -->
255
+ <button
256
+ pButton
257
+ type="button"
258
+ icon="pi pi-globe"
259
+ class="p-button-text"
260
+ [title]="'اختر اللغة'"
261
+ (click)="overlay.toggle($event)"
262
+ ></button>
263
+ <p-popover #overlay >
264
+ <ul class="w-48">
265
+ @for (lang of langs; track lang) {
266
+ <li
267
+ class="flex items-center justify-between px-3 py-2 cursor-pointer hover:bg-gray-100"
268
+ (click)="onSelectLanguage(lang.cultureName ?? ''); overlay.hide()"
269
+ >
270
+ <span>{{ lang.displayName }}</span>
271
+ @if (lang.cultureName === selectedLang()) {
272
+ <i class="pi pi-check text-green-500"></i>
273
+ }
274
+ </li>
275
+ }
276
+ </ul>
277
+ </p-popover>
278
+ </div>
279
+ }
280
+
281
281
  `
282
282
  }]
283
283
  }], ctorParameters: () => [] });
@@ -452,7 +452,45 @@ class AppConfigurator {
452
452
  primaryColors = computed(() => {
453
453
  const presetPalette = presets[this.layoutService.layoutConfig().preset].primitive;
454
454
  const colors = ['blue', 'green', 'lime', 'orange', 'amber', 'yellow', 'teal', 'cyan', 'sky', 'emerald', 'indigo', 'violet', 'purple', 'fuchsia', 'pink', 'rose'];
455
- const palettes = [{ name: 'noir', palette: {} }];
455
+ const customColors = [
456
+ {
457
+ name: 'ghazali',
458
+ palette: {
459
+ 50: '#fbf7f2',
460
+ 100: '#f4eadf',
461
+ 200: '#e8d3bf',
462
+ 300: '#d8b596',
463
+ 400: '#c99572',
464
+ 500: '#b97855',
465
+ 600: '#a45f3f',
466
+ 700: '#884a34',
467
+ 800: '#713d2f',
468
+ 900: '#5e342b',
469
+ 950: '#351914'
470
+ }
471
+ },
472
+ {
473
+ name: 'brown',
474
+ palette: {
475
+ 50: '#faf6f3',
476
+ 100: '#f1e5dc',
477
+ 200: '#e2c9b8',
478
+ 300: '#cea58a',
479
+ 400: '#b97e5d',
480
+ 500: '#9f6244',
481
+ 600: '#814a34',
482
+ 700: '#6a3d2e',
483
+ 800: '#59342a',
484
+ 900: '#4c2e27',
485
+ 950: '#2b1712'
486
+ }
487
+ },
488
+ ];
489
+ // const palettes: SurfacesType[] = [{ name: 'noir', palette: {} }];
490
+ const palettes = [
491
+ { name: 'noir', palette: {} },
492
+ ...customColors
493
+ ];
456
494
  colors.forEach((color) => {
457
495
  palettes.push({
458
496
  name: color,
@@ -636,53 +674,53 @@ class AppConfigurator {
636
674
  this.layoutService.layoutConfig.update((prev) => ({ ...prev, menuMode: event }));
637
675
  }
638
676
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AppConfigurator, deps: [], target: i0.ɵɵFactoryTarget.Component });
639
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: AppConfigurator, isStandalone: true, selector: "app-configurator", ngImport: i0, template: `
640
- <div class="flex flex-col gap-4">
641
- <div>
642
- <span class="text-sm text-muted-color font-semibold">Primary</span>
643
- <div class="pt-2 flex gap-2 flex-wrap justify-start">
644
- @for (primaryColor of primaryColors(); track primaryColor.name) {
645
- <button
646
- type="button"
647
- [title]="primaryColor.name"
648
- (click)="updateColors($event, 'primary', primaryColor)"
649
- [ngClass]="{ 'outline-primary': primaryColor.name === selectedPrimaryColor() }"
650
- class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
651
- [style]="{
652
- 'background-color': primaryColor?.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette?.['500']
653
- }"
654
- ></button>
655
- }
656
- </div>
657
- </div>
658
- <div>
659
- <span class="text-sm text-muted-color font-semibold">Surface</span>
660
- <div class="pt-2 flex gap-2 flex-wrap justify-start">
661
- @for (surface of surfaces; track surface.name) {
662
- <button
663
- type="button"
664
- [title]="surface.name"
665
- (click)="updateColors($event, 'surface', surface)"
666
- [ngClass]="{ 'outline-primary': selectedSurfaceColor() ? selectedSurfaceColor() === surface.name : layoutService.layoutConfig().darkTheme ? surface.name === 'zinc' : surface.name === 'slate' }"
667
- class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
668
- [style]="{
669
- 'background-color': surface?.name === 'noir' ? 'var(--text-color)' : surface?.palette?.['500']
670
- }"
671
- ></button>
672
- }
673
- </div>
674
- </div>
675
- <div class="flex flex-col gap-2">
676
- <span class="text-sm text-muted-color font-semibold">Presets</span>
677
- <p-selectbutton [options]="presets" [ngModel]="selectedPreset()" (ngModelChange)="onPresetChange($event)" [allowEmpty]="false" size="small" />
678
- </div>
679
- @if (showMenuModeButton()) {
680
- <div class="flex flex-col gap-2">
681
- <span class="text-sm text-muted-color font-semibold">Menu Mode</span>
682
- <p-selectbutton [ngModel]="menuMode()" (ngModelChange)="onMenuModeChange($event)" [options]="menuModeOptions" [allowEmpty]="false" size="small" />
683
- </div>
684
- }
685
- </div>
677
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: AppConfigurator, isStandalone: true, selector: "app-configurator", ngImport: i0, template: `
678
+ <div class="flex flex-col gap-4">
679
+ <div>
680
+ <span class="text-sm text-muted-color font-semibold">Primary</span>
681
+ <div class="pt-2 flex gap-2 flex-wrap justify-start">
682
+ @for (primaryColor of primaryColors(); track primaryColor.name) {
683
+ <button
684
+ type="button"
685
+ [title]="primaryColor.name"
686
+ (click)="updateColors($event, 'primary', primaryColor)"
687
+ [ngClass]="{ 'outline-primary': primaryColor.name === selectedPrimaryColor() }"
688
+ class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
689
+ [style]="{
690
+ 'background-color': primaryColor?.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette?.['500']
691
+ }"
692
+ ></button>
693
+ }
694
+ </div>
695
+ </div>
696
+ <div>
697
+ <span class="text-sm text-muted-color font-semibold">Surface</span>
698
+ <div class="pt-2 flex gap-2 flex-wrap justify-start">
699
+ @for (surface of surfaces; track surface.name) {
700
+ <button
701
+ type="button"
702
+ [title]="surface.name"
703
+ (click)="updateColors($event, 'surface', surface)"
704
+ [ngClass]="{ 'outline-primary': selectedSurfaceColor() ? selectedSurfaceColor() === surface.name : layoutService.layoutConfig().darkTheme ? surface.name === 'zinc' : surface.name === 'slate' }"
705
+ class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
706
+ [style]="{
707
+ 'background-color': surface?.name === 'noir' ? 'var(--text-color)' : surface?.palette?.['500']
708
+ }"
709
+ ></button>
710
+ }
711
+ </div>
712
+ </div>
713
+ <div class="flex flex-col gap-2">
714
+ <span class="text-sm text-muted-color font-semibold">Presets</span>
715
+ <p-selectbutton [options]="presets" [ngModel]="selectedPreset()" (ngModelChange)="onPresetChange($event)" [allowEmpty]="false" size="small" />
716
+ </div>
717
+ @if (showMenuModeButton()) {
718
+ <div class="flex flex-col gap-2">
719
+ <span class="text-sm text-muted-color font-semibold">Menu Mode</span>
720
+ <p-selectbutton [ngModel]="menuMode()" (ngModelChange)="onMenuModeChange($event)" [options]="menuModeOptions" [allowEmpty]="false" size="small" />
721
+ </div>
722
+ }
723
+ </div>
686
724
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i3.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"] }] });
687
725
  }
688
726
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AppConfigurator, decorators: [{
@@ -691,53 +729,53 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
691
729
  selector: 'app-configurator',
692
730
  standalone: true,
693
731
  imports: [CommonModule, FormsModule, SelectButtonModule],
694
- template: `
695
- <div class="flex flex-col gap-4">
696
- <div>
697
- <span class="text-sm text-muted-color font-semibold">Primary</span>
698
- <div class="pt-2 flex gap-2 flex-wrap justify-start">
699
- @for (primaryColor of primaryColors(); track primaryColor.name) {
700
- <button
701
- type="button"
702
- [title]="primaryColor.name"
703
- (click)="updateColors($event, 'primary', primaryColor)"
704
- [ngClass]="{ 'outline-primary': primaryColor.name === selectedPrimaryColor() }"
705
- class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
706
- [style]="{
707
- 'background-color': primaryColor?.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette?.['500']
708
- }"
709
- ></button>
710
- }
711
- </div>
712
- </div>
713
- <div>
714
- <span class="text-sm text-muted-color font-semibold">Surface</span>
715
- <div class="pt-2 flex gap-2 flex-wrap justify-start">
716
- @for (surface of surfaces; track surface.name) {
717
- <button
718
- type="button"
719
- [title]="surface.name"
720
- (click)="updateColors($event, 'surface', surface)"
721
- [ngClass]="{ 'outline-primary': selectedSurfaceColor() ? selectedSurfaceColor() === surface.name : layoutService.layoutConfig().darkTheme ? surface.name === 'zinc' : surface.name === 'slate' }"
722
- class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
723
- [style]="{
724
- 'background-color': surface?.name === 'noir' ? 'var(--text-color)' : surface?.palette?.['500']
725
- }"
726
- ></button>
727
- }
728
- </div>
729
- </div>
730
- <div class="flex flex-col gap-2">
731
- <span class="text-sm text-muted-color font-semibold">Presets</span>
732
- <p-selectbutton [options]="presets" [ngModel]="selectedPreset()" (ngModelChange)="onPresetChange($event)" [allowEmpty]="false" size="small" />
733
- </div>
734
- @if (showMenuModeButton()) {
735
- <div class="flex flex-col gap-2">
736
- <span class="text-sm text-muted-color font-semibold">Menu Mode</span>
737
- <p-selectbutton [ngModel]="menuMode()" (ngModelChange)="onMenuModeChange($event)" [options]="menuModeOptions" [allowEmpty]="false" size="small" />
738
- </div>
739
- }
740
- </div>
732
+ template: `
733
+ <div class="flex flex-col gap-4">
734
+ <div>
735
+ <span class="text-sm text-muted-color font-semibold">Primary</span>
736
+ <div class="pt-2 flex gap-2 flex-wrap justify-start">
737
+ @for (primaryColor of primaryColors(); track primaryColor.name) {
738
+ <button
739
+ type="button"
740
+ [title]="primaryColor.name"
741
+ (click)="updateColors($event, 'primary', primaryColor)"
742
+ [ngClass]="{ 'outline-primary': primaryColor.name === selectedPrimaryColor() }"
743
+ class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
744
+ [style]="{
745
+ 'background-color': primaryColor?.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette?.['500']
746
+ }"
747
+ ></button>
748
+ }
749
+ </div>
750
+ </div>
751
+ <div>
752
+ <span class="text-sm text-muted-color font-semibold">Surface</span>
753
+ <div class="pt-2 flex gap-2 flex-wrap justify-start">
754
+ @for (surface of surfaces; track surface.name) {
755
+ <button
756
+ type="button"
757
+ [title]="surface.name"
758
+ (click)="updateColors($event, 'surface', surface)"
759
+ [ngClass]="{ 'outline-primary': selectedSurfaceColor() ? selectedSurfaceColor() === surface.name : layoutService.layoutConfig().darkTheme ? surface.name === 'zinc' : surface.name === 'slate' }"
760
+ class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
761
+ [style]="{
762
+ 'background-color': surface?.name === 'noir' ? 'var(--text-color)' : surface?.palette?.['500']
763
+ }"
764
+ ></button>
765
+ }
766
+ </div>
767
+ </div>
768
+ <div class="flex flex-col gap-2">
769
+ <span class="text-sm text-muted-color font-semibold">Presets</span>
770
+ <p-selectbutton [options]="presets" [ngModel]="selectedPreset()" (ngModelChange)="onPresetChange($event)" [allowEmpty]="false" size="small" />
771
+ </div>
772
+ @if (showMenuModeButton()) {
773
+ <div class="flex flex-col gap-2">
774
+ <span class="text-sm text-muted-color font-semibold">Menu Mode</span>
775
+ <p-selectbutton [ngModel]="menuMode()" (ngModelChange)="onMenuModeChange($event)" [options]="menuModeOptions" [allowEmpty]="false" size="small" />
776
+ </div>
777
+ }
778
+ </div>
741
779
  `,
742
780
  }]
743
781
  }] });
@@ -868,167 +906,167 @@ class NavItemsComponent {
868
906
  this.authService.navigateToLogin();
869
907
  }
870
908
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: NavItemsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
871
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: NavItemsComponent, isStandalone: true, selector: "app-nav-items", ngImport: i0, template: `
872
- <header class="flex items-center justify-between px-6 h-16 bg-white shadow sticky top-0 z-9">
873
- <!-- Left: Logo / Title -->
874
- <div class="flex items-center space-x-3 rtl:space-x-reverse">
875
- <img src="assets/logo.png" alt="Logo" class="h-8 w-8" />
876
- <span class="text-lg font-semibold text-gray-800">{{ '' | translate }}</span>
877
- </div>
878
-
879
- <!-- Center: Nav Items -->
880
- <div class="flex items-center space-x-4 rtl:space-x-reverse">
881
- <ul class="flex space-x-4">
882
- @for (item of navItems.items$ | async; track item.id) {
883
- @if (!item.visible || item.visible(item)) {
884
- <ng-container *frameworkVisible="!item.visible || item.visible(item)">
885
- <li class="flex items-center">
886
- @if (item.component) {
887
- <ng-container
888
- [ngComponentOutlet]="item.component"
889
- [ngComponentOutletInjector]="item | toInjector"
890
- ></ng-container>
891
- } @else {
892
- <button
893
- pButton
894
- type="button"
895
- class="p-button-text hover:bg-gray-100 focus:outline-none flex items-center"
896
- (click)="item.action?.()"
897
- >
898
- <span [innerHTML]="item.html"></span>
899
- </button>
900
- }
901
- </li>
902
- </ng-container>
903
- }
904
- }
905
- </ul>
906
- </div>
907
-
908
- <!-- Right: Actions -->
909
- <div class="flex items-center rtl:space-x-reverse">
910
- <!-- Configurator -->
911
-
912
- <div>
913
- <!-- -->
914
- <button
915
- pButton
916
- type="button"
917
- icon="pi pi-palette"
918
- class="p-button-text"
919
- [title]="''"
920
- (click)="overlay.toggle($event)"
921
- >
922
- </button>
923
-
924
- <p-popover #overlay >
925
- <app-configurator />
926
- </p-popover>
927
- </div>
928
-
929
-
930
- <!-- Dark Mode Toggle -->
931
- <button
932
- pButton
933
- type="button"
934
- [icon]="layoutService.isDarkTheme() ?'pi pi-moon': 'pi pi-sun'"
935
- class="p-button-text"
936
- (click)="toggleDarkMode()">
937
- </button>
938
-
939
- <!-- Language Switcher -->
940
- <abp-languages></abp-languages>
941
-
942
- <!-- User Menu -->
943
- <div class="relative">
944
- <button
945
- class="p-link p-2"
946
- (click)="menu.toggle($event)"
947
- aria-label="User menu"
948
- >
949
- <p-avatar
950
- [label]="loading ? '' : userName"
951
- [icon]="loading ? 'pi pi-spinner' : ''"
952
- [class]="{'pi-spin': loading}"
953
- shape="circle"
954
- class="bg-blue-600 text-white cursor-pointer"
955
- [title]="(currentUser$ | async)?.name || ''"
956
- ></p-avatar>
957
- </button>
958
-
959
- <p-menu
960
- #menu
961
- [model]="userMenuItems"
962
- [popup]="true"
963
- [appendTo]="'body'"
964
- styleClass="w-64 sm:w-72 shadow-lg rounded-md"
965
- >
966
- <ng-template pTemplate="start">
967
- <div class="px-5 py-4 border-b border-gray-200 bg-gray-50 flex flex-col items-center text-center">
968
- <p-avatar
969
- [label]="userName"
970
- shape="circle"
971
- size="xlarge"
972
- styleClass="bg-blue-600 text-white mb-3 shadow-lg"
973
- [title]="(currentUser$ | async)?.name || ''"
974
- ></p-avatar>
975
- <h5 class="text-xl font-semibold text-gray-800 leading-tight">
976
- {{ (currentUser$ | async)?.name || 'اسم المستخدم' }}
977
- </h5>
978
- <p class="text-sm text-gray-600 truncate max-w-full">
979
- {{ (currentUser$ | async)?.email || 'لا يوجد بريد إلكتروني' }}
980
- </p>
981
- @if ((selectedTenant$ | async); as tenant) {
982
- <span
983
- class="inline-block mt-2 px-3 py-1 text-xs rounded-full font-semibold"
984
- [ngClass]="{
985
- 'bg-green-100 text-green-800': true
986
- }"
987
- >
988
- {{ tenant?.name || '_' }}
989
- </span>
990
- }
991
- </div>
992
- </ng-template>
993
-
994
- <ng-template pTemplate="item" let-item>
995
- <!-- *frameworkVisible="!item.visible || item.visible(item)" -->
996
- <ng-container >
997
- <!-- *hasPermission="item.requiredPolicy" -->
998
- <a
999
-
1000
- class="p-menuitem-link flex items-center space-x-3 rtl:space-x-reverse px-5 py-2 hover:bg-gray-100 rounded-md"
1001
- [ngClass]="item.styleClass || ''"
1002
- (click)="item.command ? item.command() : null"
1003
- [routerLink]="item.routerLink ? item.routerLink : null"
1004
- tabindex="0"
1005
- >
1006
- @if (item.component) {
1007
- <ng-container
1008
- [ngComponentOutlet]="item.component"
1009
- [ngComponentOutletInjector]="item | toInjector"
1010
- ></ng-container>
1011
- } @else {
1012
- <i [class]="item.icon + ' text-lg text-gray-600'"></i>
1013
- <span class="text-gray-700">{{ item.label | translate }}</span>
1014
- }
1015
- </a>
1016
- </ng-container>
1017
- </ng-template>
1018
- </p-menu>
1019
- </div>
1020
-
1021
- <!-- Logout Button (separate from menu) -->
1022
- <!-- <button
1023
- type="button"
1024
- class="layout-topbar-action"
1025
- (click)="logout()"
1026
- title="تسجيل الخروج"
1027
- >
1028
- <i class="pi pi-sign-out"></i>
1029
- </button> -->
1030
- </div>
1031
- </header>
909
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: NavItemsComponent, isStandalone: true, selector: "app-nav-items", ngImport: i0, template: `
910
+ <header class="flex items-center justify-between px-6 h-16 bg-white shadow sticky top-0 z-9">
911
+ <!-- Left: Logo / Title -->
912
+ <div class="flex items-center space-x-3 rtl:space-x-reverse">
913
+ <img src="assets/logo.png" alt="Logo" class="h-8 w-8" />
914
+ <span class="text-lg font-semibold text-gray-800">{{ '' | translate }}</span>
915
+ </div>
916
+
917
+ <!-- Center: Nav Items -->
918
+ <div class="flex items-center space-x-4 rtl:space-x-reverse">
919
+ <ul class="flex space-x-4">
920
+ @for (item of navItems.items$ | async; track item.id) {
921
+ @if (!item.visible || item.visible(item)) {
922
+ <ng-container *frameworkVisible="!item.visible || item.visible(item)">
923
+ <li class="flex items-center">
924
+ @if (item.component) {
925
+ <ng-container
926
+ [ngComponentOutlet]="item.component"
927
+ [ngComponentOutletInjector]="item | toInjector"
928
+ ></ng-container>
929
+ } @else {
930
+ <button
931
+ pButton
932
+ type="button"
933
+ class="p-button-text hover:bg-gray-100 focus:outline-none flex items-center"
934
+ (click)="item.action?.()"
935
+ >
936
+ <span [innerHTML]="item.html"></span>
937
+ </button>
938
+ }
939
+ </li>
940
+ </ng-container>
941
+ }
942
+ }
943
+ </ul>
944
+ </div>
945
+
946
+ <!-- Right: Actions -->
947
+ <div class="flex items-center rtl:space-x-reverse">
948
+ <!-- Configurator -->
949
+
950
+ <div>
951
+ <!-- -->
952
+ <button
953
+ pButton
954
+ type="button"
955
+ icon="pi pi-palette"
956
+ class="p-button-text"
957
+ [title]="''"
958
+ (click)="overlay.toggle($event)"
959
+ >
960
+ </button>
961
+
962
+ <p-popover #overlay >
963
+ <app-configurator />
964
+ </p-popover>
965
+ </div>
966
+
967
+
968
+ <!-- Dark Mode Toggle -->
969
+ <button
970
+ pButton
971
+ type="button"
972
+ [icon]="layoutService.isDarkTheme() ?'pi pi-moon': 'pi pi-sun'"
973
+ class="p-button-text"
974
+ (click)="toggleDarkMode()">
975
+ </button>
976
+
977
+ <!-- Language Switcher -->
978
+ <abp-languages></abp-languages>
979
+
980
+ <!-- User Menu -->
981
+ <div class="relative">
982
+ <button
983
+ class="p-link p-2"
984
+ (click)="menu.toggle($event)"
985
+ aria-label="User menu"
986
+ >
987
+ <p-avatar
988
+ [label]="loading ? '' : userName"
989
+ [icon]="loading ? 'pi pi-spinner' : ''"
990
+ [class]="{'pi-spin': loading}"
991
+ shape="circle"
992
+ class="bg-blue-600 text-white cursor-pointer"
993
+ [title]="(currentUser$ | async)?.name || ''"
994
+ ></p-avatar>
995
+ </button>
996
+
997
+ <p-menu
998
+ #menu
999
+ [model]="userMenuItems"
1000
+ [popup]="true"
1001
+ [appendTo]="'body'"
1002
+ styleClass="w-64 sm:w-72 shadow-lg rounded-md"
1003
+ >
1004
+ <ng-template pTemplate="start">
1005
+ <div class="px-5 py-4 border-b border-gray-200 bg-gray-50 flex flex-col items-center text-center">
1006
+ <p-avatar
1007
+ [label]="userName"
1008
+ shape="circle"
1009
+ size="xlarge"
1010
+ styleClass="bg-blue-600 text-white mb-3 shadow-lg"
1011
+ [title]="(currentUser$ | async)?.name || ''"
1012
+ ></p-avatar>
1013
+ <h5 class="text-xl font-semibold text-gray-800 leading-tight">
1014
+ {{ (currentUser$ | async)?.name || 'اسم المستخدم' }}
1015
+ </h5>
1016
+ <p class="text-sm text-gray-600 truncate max-w-full">
1017
+ {{ (currentUser$ | async)?.email || 'لا يوجد بريد إلكتروني' }}
1018
+ </p>
1019
+ @if ((selectedTenant$ | async); as tenant) {
1020
+ <span
1021
+ class="inline-block mt-2 px-3 py-1 text-xs rounded-full font-semibold"
1022
+ [ngClass]="{
1023
+ 'bg-green-100 text-green-800': true
1024
+ }"
1025
+ >
1026
+ {{ tenant?.name || '_' }}
1027
+ </span>
1028
+ }
1029
+ </div>
1030
+ </ng-template>
1031
+
1032
+ <ng-template pTemplate="item" let-item>
1033
+ <!-- *frameworkVisible="!item.visible || item.visible(item)" -->
1034
+ <ng-container >
1035
+ <!-- *hasPermission="item.requiredPolicy" -->
1036
+ <a
1037
+
1038
+ class="p-menuitem-link flex items-center space-x-3 rtl:space-x-reverse px-5 py-2 hover:bg-gray-100 rounded-md"
1039
+ [ngClass]="item.styleClass || ''"
1040
+ (click)="item.command ? item.command() : null"
1041
+ [routerLink]="item.routerLink ? item.routerLink : null"
1042
+ tabindex="0"
1043
+ >
1044
+ @if (item.component) {
1045
+ <ng-container
1046
+ [ngComponentOutlet]="item.component"
1047
+ [ngComponentOutletInjector]="item | toInjector"
1048
+ ></ng-container>
1049
+ } @else {
1050
+ <i [class]="item.icon + ' text-lg text-gray-600'"></i>
1051
+ <span class="text-gray-700">{{ item.label | translate }}</span>
1052
+ }
1053
+ </a>
1054
+ </ng-container>
1055
+ </ng-template>
1056
+ </p-menu>
1057
+ </div>
1058
+
1059
+ <!-- Logout Button (separate from menu) -->
1060
+ <!-- <button
1061
+ type="button"
1062
+ class="layout-topbar-action"
1063
+ (click)="logout()"
1064
+ title="تسجيل الخروج"
1065
+ >
1066
+ <i class="pi pi-sign-out"></i>
1067
+ </button> -->
1068
+ </div>
1069
+ </header>
1032
1070
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "component", type: AppConfigurator, selector: "app-configurator" }, { kind: "component", type: LanguagesComponent, selector: "abp-languages" }, { kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i3$1.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "directive", type: i4.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: MenuModule }, { kind: "component", type: i5.Menu, selector: "p-menu", inputs: ["model", "popup", "style", "styleClass", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "ariaLabel", "ariaLabelledBy", "id", "tabindex", "appendTo"], outputs: ["onShow", "onHide", "onBlur", "onFocus"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: VisibleDirective, selector: "[frameworkVisible]", inputs: ["frameworkVisible"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i1.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: ToInjectorPipe, name: "toInjector" }] });
1033
1071
  }
1034
1072
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: NavItemsComponent, decorators: [{
@@ -1052,292 +1090,281 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
1052
1090
  ToInjectorPipe,
1053
1091
  PopoverModule
1054
1092
  ],
1055
- template: `
1056
- <header class="flex items-center justify-between px-6 h-16 bg-white shadow sticky top-0 z-9">
1057
- <!-- Left: Logo / Title -->
1058
- <div class="flex items-center space-x-3 rtl:space-x-reverse">
1059
- <img src="assets/logo.png" alt="Logo" class="h-8 w-8" />
1060
- <span class="text-lg font-semibold text-gray-800">{{ '' | translate }}</span>
1061
- </div>
1062
-
1063
- <!-- Center: Nav Items -->
1064
- <div class="flex items-center space-x-4 rtl:space-x-reverse">
1065
- <ul class="flex space-x-4">
1066
- @for (item of navItems.items$ | async; track item.id) {
1067
- @if (!item.visible || item.visible(item)) {
1068
- <ng-container *frameworkVisible="!item.visible || item.visible(item)">
1069
- <li class="flex items-center">
1070
- @if (item.component) {
1071
- <ng-container
1072
- [ngComponentOutlet]="item.component"
1073
- [ngComponentOutletInjector]="item | toInjector"
1074
- ></ng-container>
1075
- } @else {
1076
- <button
1077
- pButton
1078
- type="button"
1079
- class="p-button-text hover:bg-gray-100 focus:outline-none flex items-center"
1080
- (click)="item.action?.()"
1081
- >
1082
- <span [innerHTML]="item.html"></span>
1083
- </button>
1084
- }
1085
- </li>
1086
- </ng-container>
1087
- }
1088
- }
1089
- </ul>
1090
- </div>
1091
-
1092
- <!-- Right: Actions -->
1093
- <div class="flex items-center rtl:space-x-reverse">
1094
- <!-- Configurator -->
1095
-
1096
- <div>
1097
- <!-- -->
1098
- <button
1099
- pButton
1100
- type="button"
1101
- icon="pi pi-palette"
1102
- class="p-button-text"
1103
- [title]="''"
1104
- (click)="overlay.toggle($event)"
1105
- >
1106
- </button>
1107
-
1108
- <p-popover #overlay >
1109
- <app-configurator />
1110
- </p-popover>
1111
- </div>
1112
-
1113
-
1114
- <!-- Dark Mode Toggle -->
1115
- <button
1116
- pButton
1117
- type="button"
1118
- [icon]="layoutService.isDarkTheme() ?'pi pi-moon': 'pi pi-sun'"
1119
- class="p-button-text"
1120
- (click)="toggleDarkMode()">
1121
- </button>
1122
-
1123
- <!-- Language Switcher -->
1124
- <abp-languages></abp-languages>
1125
-
1126
- <!-- User Menu -->
1127
- <div class="relative">
1128
- <button
1129
- class="p-link p-2"
1130
- (click)="menu.toggle($event)"
1131
- aria-label="User menu"
1132
- >
1133
- <p-avatar
1134
- [label]="loading ? '' : userName"
1135
- [icon]="loading ? 'pi pi-spinner' : ''"
1136
- [class]="{'pi-spin': loading}"
1137
- shape="circle"
1138
- class="bg-blue-600 text-white cursor-pointer"
1139
- [title]="(currentUser$ | async)?.name || ''"
1140
- ></p-avatar>
1141
- </button>
1142
-
1143
- <p-menu
1144
- #menu
1145
- [model]="userMenuItems"
1146
- [popup]="true"
1147
- [appendTo]="'body'"
1148
- styleClass="w-64 sm:w-72 shadow-lg rounded-md"
1149
- >
1150
- <ng-template pTemplate="start">
1151
- <div class="px-5 py-4 border-b border-gray-200 bg-gray-50 flex flex-col items-center text-center">
1152
- <p-avatar
1153
- [label]="userName"
1154
- shape="circle"
1155
- size="xlarge"
1156
- styleClass="bg-blue-600 text-white mb-3 shadow-lg"
1157
- [title]="(currentUser$ | async)?.name || ''"
1158
- ></p-avatar>
1159
- <h5 class="text-xl font-semibold text-gray-800 leading-tight">
1160
- {{ (currentUser$ | async)?.name || 'اسم المستخدم' }}
1161
- </h5>
1162
- <p class="text-sm text-gray-600 truncate max-w-full">
1163
- {{ (currentUser$ | async)?.email || 'لا يوجد بريد إلكتروني' }}
1164
- </p>
1165
- @if ((selectedTenant$ | async); as tenant) {
1166
- <span
1167
- class="inline-block mt-2 px-3 py-1 text-xs rounded-full font-semibold"
1168
- [ngClass]="{
1169
- 'bg-green-100 text-green-800': true
1170
- }"
1171
- >
1172
- {{ tenant?.name || '_' }}
1173
- </span>
1174
- }
1175
- </div>
1176
- </ng-template>
1177
-
1178
- <ng-template pTemplate="item" let-item>
1179
- <!-- *frameworkVisible="!item.visible || item.visible(item)" -->
1180
- <ng-container >
1181
- <!-- *hasPermission="item.requiredPolicy" -->
1182
- <a
1183
-
1184
- class="p-menuitem-link flex items-center space-x-3 rtl:space-x-reverse px-5 py-2 hover:bg-gray-100 rounded-md"
1185
- [ngClass]="item.styleClass || ''"
1186
- (click)="item.command ? item.command() : null"
1187
- [routerLink]="item.routerLink ? item.routerLink : null"
1188
- tabindex="0"
1189
- >
1190
- @if (item.component) {
1191
- <ng-container
1192
- [ngComponentOutlet]="item.component"
1193
- [ngComponentOutletInjector]="item | toInjector"
1194
- ></ng-container>
1195
- } @else {
1196
- <i [class]="item.icon + ' text-lg text-gray-600'"></i>
1197
- <span class="text-gray-700">{{ item.label | translate }}</span>
1198
- }
1199
- </a>
1200
- </ng-container>
1201
- </ng-template>
1202
- </p-menu>
1203
- </div>
1204
-
1205
- <!-- Logout Button (separate from menu) -->
1206
- <!-- <button
1207
- type="button"
1208
- class="layout-topbar-action"
1209
- (click)="logout()"
1210
- title="تسجيل الخروج"
1211
- >
1212
- <i class="pi pi-sign-out"></i>
1213
- </button> -->
1214
- </div>
1215
- </header>
1093
+ template: `
1094
+ <header class="flex items-center justify-between px-6 h-16 bg-white shadow sticky top-0 z-9">
1095
+ <!-- Left: Logo / Title -->
1096
+ <div class="flex items-center space-x-3 rtl:space-x-reverse">
1097
+ <img src="assets/logo.png" alt="Logo" class="h-8 w-8" />
1098
+ <span class="text-lg font-semibold text-gray-800">{{ '' | translate }}</span>
1099
+ </div>
1100
+
1101
+ <!-- Center: Nav Items -->
1102
+ <div class="flex items-center space-x-4 rtl:space-x-reverse">
1103
+ <ul class="flex space-x-4">
1104
+ @for (item of navItems.items$ | async; track item.id) {
1105
+ @if (!item.visible || item.visible(item)) {
1106
+ <ng-container *frameworkVisible="!item.visible || item.visible(item)">
1107
+ <li class="flex items-center">
1108
+ @if (item.component) {
1109
+ <ng-container
1110
+ [ngComponentOutlet]="item.component"
1111
+ [ngComponentOutletInjector]="item | toInjector"
1112
+ ></ng-container>
1113
+ } @else {
1114
+ <button
1115
+ pButton
1116
+ type="button"
1117
+ class="p-button-text hover:bg-gray-100 focus:outline-none flex items-center"
1118
+ (click)="item.action?.()"
1119
+ >
1120
+ <span [innerHTML]="item.html"></span>
1121
+ </button>
1122
+ }
1123
+ </li>
1124
+ </ng-container>
1125
+ }
1126
+ }
1127
+ </ul>
1128
+ </div>
1129
+
1130
+ <!-- Right: Actions -->
1131
+ <div class="flex items-center rtl:space-x-reverse">
1132
+ <!-- Configurator -->
1133
+
1134
+ <div>
1135
+ <!-- -->
1136
+ <button
1137
+ pButton
1138
+ type="button"
1139
+ icon="pi pi-palette"
1140
+ class="p-button-text"
1141
+ [title]="''"
1142
+ (click)="overlay.toggle($event)"
1143
+ >
1144
+ </button>
1145
+
1146
+ <p-popover #overlay >
1147
+ <app-configurator />
1148
+ </p-popover>
1149
+ </div>
1150
+
1151
+
1152
+ <!-- Dark Mode Toggle -->
1153
+ <button
1154
+ pButton
1155
+ type="button"
1156
+ [icon]="layoutService.isDarkTheme() ?'pi pi-moon': 'pi pi-sun'"
1157
+ class="p-button-text"
1158
+ (click)="toggleDarkMode()">
1159
+ </button>
1160
+
1161
+ <!-- Language Switcher -->
1162
+ <abp-languages></abp-languages>
1163
+
1164
+ <!-- User Menu -->
1165
+ <div class="relative">
1166
+ <button
1167
+ class="p-link p-2"
1168
+ (click)="menu.toggle($event)"
1169
+ aria-label="User menu"
1170
+ >
1171
+ <p-avatar
1172
+ [label]="loading ? '' : userName"
1173
+ [icon]="loading ? 'pi pi-spinner' : ''"
1174
+ [class]="{'pi-spin': loading}"
1175
+ shape="circle"
1176
+ class="bg-blue-600 text-white cursor-pointer"
1177
+ [title]="(currentUser$ | async)?.name || ''"
1178
+ ></p-avatar>
1179
+ </button>
1180
+
1181
+ <p-menu
1182
+ #menu
1183
+ [model]="userMenuItems"
1184
+ [popup]="true"
1185
+ [appendTo]="'body'"
1186
+ styleClass="w-64 sm:w-72 shadow-lg rounded-md"
1187
+ >
1188
+ <ng-template pTemplate="start">
1189
+ <div class="px-5 py-4 border-b border-gray-200 bg-gray-50 flex flex-col items-center text-center">
1190
+ <p-avatar
1191
+ [label]="userName"
1192
+ shape="circle"
1193
+ size="xlarge"
1194
+ styleClass="bg-blue-600 text-white mb-3 shadow-lg"
1195
+ [title]="(currentUser$ | async)?.name || ''"
1196
+ ></p-avatar>
1197
+ <h5 class="text-xl font-semibold text-gray-800 leading-tight">
1198
+ {{ (currentUser$ | async)?.name || 'اسم المستخدم' }}
1199
+ </h5>
1200
+ <p class="text-sm text-gray-600 truncate max-w-full">
1201
+ {{ (currentUser$ | async)?.email || 'لا يوجد بريد إلكتروني' }}
1202
+ </p>
1203
+ @if ((selectedTenant$ | async); as tenant) {
1204
+ <span
1205
+ class="inline-block mt-2 px-3 py-1 text-xs rounded-full font-semibold"
1206
+ [ngClass]="{
1207
+ 'bg-green-100 text-green-800': true
1208
+ }"
1209
+ >
1210
+ {{ tenant?.name || '_' }}
1211
+ </span>
1212
+ }
1213
+ </div>
1214
+ </ng-template>
1215
+
1216
+ <ng-template pTemplate="item" let-item>
1217
+ <!-- *frameworkVisible="!item.visible || item.visible(item)" -->
1218
+ <ng-container >
1219
+ <!-- *hasPermission="item.requiredPolicy" -->
1220
+ <a
1221
+
1222
+ class="p-menuitem-link flex items-center space-x-3 rtl:space-x-reverse px-5 py-2 hover:bg-gray-100 rounded-md"
1223
+ [ngClass]="item.styleClass || ''"
1224
+ (click)="item.command ? item.command() : null"
1225
+ [routerLink]="item.routerLink ? item.routerLink : null"
1226
+ tabindex="0"
1227
+ >
1228
+ @if (item.component) {
1229
+ <ng-container
1230
+ [ngComponentOutlet]="item.component"
1231
+ [ngComponentOutletInjector]="item | toInjector"
1232
+ ></ng-container>
1233
+ } @else {
1234
+ <i [class]="item.icon + ' text-lg text-gray-600'"></i>
1235
+ <span class="text-gray-700">{{ item.label | translate }}</span>
1236
+ }
1237
+ </a>
1238
+ </ng-container>
1239
+ </ng-template>
1240
+ </p-menu>
1241
+ </div>
1242
+
1243
+ <!-- Logout Button (separate from menu) -->
1244
+ <!-- <button
1245
+ type="button"
1246
+ class="layout-topbar-action"
1247
+ (click)="logout()"
1248
+ title="تسجيل الخروج"
1249
+ >
1250
+ <i class="pi pi-sign-out"></i>
1251
+ </button> -->
1252
+ </div>
1253
+ </header>
1216
1254
  `
1217
1255
  }]
1218
1256
  }], ctorParameters: () => [] });
1219
1257
 
1220
- class RoutesComponent {
1221
- subSidebarVisible = false;
1222
- sidebarVisible = true;
1223
- isMobile = false;
1258
+ class RoutesMenuService {
1224
1259
  router = inject(Router);
1225
- localization = inject(LocalizationService);
1226
- layoutService = inject(LayoutService);
1227
1260
  routesService = inject(RoutesService);
1228
- configState = inject(ConfigStateService);
1229
- secondToLastLevel$;
1261
+ layoutService = inject(LayoutService);
1262
+ sidebarVisible = true;
1263
+ subSidebarVisible = false;
1264
+ isMobile = false;
1265
+ showTreeSearch = false;
1266
+ searchText = '';
1230
1267
  _menuItemsSubject = new BehaviorSubject([]);
1231
- // menuItems$!: Observable<MenuItem[]>;
1268
+ expandedStateBeforeSearch = null;
1232
1269
  menuItems$ = this._menuItemsSubject.asObservable();
1233
- constructor() {
1234
- // this.menuItems$ = this.routesService.visible$.pipe(
1235
- // map(routes => this.buildItems(routes).map(item => ({ ...item, expanded: true })))
1236
- // );
1237
- this.routesService.visible$.pipe(map(routes => this.buildItems(routes).map(item => ({ ...item, expanded: true })))).subscribe(items => this._menuItemsSubject.next(items));
1238
- }
1239
- ngOnInit() {
1240
- this.checkViewport();
1241
- this.secondToLastLevel$ = this.menuItems$.pipe(map(items => {
1242
- const result = [];
1243
- for (const item of items) {
1244
- if (item.items && item.items.length > 0) {
1245
- // إضافة جميع الأبناء المباشرين (المستوى الثاني)
1246
- result.push(...item.items);
1247
- }
1248
- }
1249
- return result;
1250
- }));
1251
- // this.secondToLastLevel$ = this.menuItems$.pipe(
1252
- // map(items => this.getSecondToLastLevel(items))
1253
- // );
1254
- }
1255
- getSecondToLastLevel(items) {
1270
+ secondToLastLevel$ = this.menuItems$.pipe(map((items) => {
1256
1271
  const result = [];
1257
- const traverse = (nodes) => {
1258
- for (const node of nodes) {
1259
- if (node.items && node.items.length > 0) {
1260
- // Check if any child has its own children
1261
- const hasGrandChildren = node.items.some(c => c.items && c.items.length > 0);
1262
- if (!hasGrandChildren) {
1263
- // Node is right before the last level
1264
- result.push(node);
1265
- }
1266
- else {
1267
- traverse(node.items);
1268
- }
1269
- }
1272
+ for (const item of items) {
1273
+ if (item.items?.length) {
1274
+ result.push(...item.items);
1270
1275
  }
1271
- };
1272
- traverse(items);
1276
+ }
1273
1277
  return result;
1278
+ }));
1279
+ constructor() {
1280
+ this.routesService.visible$
1281
+ .pipe(map((routes) => this.buildItems(routes)), takeUntilDestroyed())
1282
+ .subscribe((items) => this.setItems(items));
1274
1283
  }
1275
- onParentClick(item) {
1276
- // toggle expand
1277
- item.expanded = !item.expanded;
1278
- // TODO: Navigate to shell route if needed
1279
- // if (item.items?.length) {
1280
- // // Navigate to shell route
1281
- // // Split the path into segments for Angular
1282
- // const pathSegments = item.routerLink
1283
- // ? (Array.isArray(item.routerLink) ? item.routerLink : item.routerLink.split('/').filter(Boolean))
1284
- // : [];
1285
- // this.router.navigate(pathSegments);
1286
- // } else if (item.routerLink) {
1287
- // const pathSegments = Array.isArray(item.routerLink) ? item.routerLink : item.routerLink.split('/').filter(Boolean);
1288
- // this.router.navigate(pathSegments);
1289
- // }
1290
- // this.subSidebarVisible = true;
1291
- }
1292
- onResize() { this.checkViewport(); }
1293
- checkViewport() {
1294
- this.isMobile = window.innerWidth < 1024;
1295
- if (this.isMobile) {
1296
- this.sidebarVisible = false;
1297
- this.subSidebarVisible = false;
1284
+ get items() {
1285
+ return this._menuItemsSubject.value;
1286
+ }
1287
+ setItems(items) {
1288
+ this.setExpandedByLevel(items);
1289
+ this._menuItemsSubject.next(items);
1290
+ }
1291
+ refresh() {
1292
+ this._menuItemsSubject.next([...this.items]);
1293
+ }
1294
+ expandAll() {
1295
+ this.setExpandedRecursive(this.items, true);
1296
+ this.subSidebarVisible = true;
1297
+ this.refresh();
1298
+ }
1299
+ collapseAll() {
1300
+ this.setExpandedRecursive(this.items, false);
1301
+ this.refresh();
1302
+ }
1303
+ search(keyword) {
1304
+ if (!this.expandedStateBeforeSearch) {
1305
+ this.expandedStateBeforeSearch = this.captureExpandedState(this.items);
1298
1306
  }
1299
- else {
1300
- this.sidebarVisible = true;
1301
- this.subSidebarVisible = true;
1307
+ this.searchText = keyword;
1308
+ if (!this.searchText.trim()) {
1309
+ this.restoreExpandedStateBeforeSearch();
1310
+ this.refresh();
1311
+ return;
1302
1312
  }
1313
+ this.expandMatchedParents(this.items, this.searchText);
1314
+ this.refresh();
1303
1315
  }
1304
- toggleAll() {
1305
- if (this.isMobile) {
1306
- this.sidebarVisible = !this.sidebarVisible;
1307
- this.subSidebarVisible = this.sidebarVisible;
1308
- }
1309
- else {
1310
- this.subSidebarVisible = !this.subSidebarVisible;
1316
+ clearSearch() {
1317
+ this.searchText = '';
1318
+ this.restoreExpandedStateBeforeSearch();
1319
+ this.refresh();
1320
+ }
1321
+ toggleSearch() {
1322
+ this.showTreeSearch = !this.showTreeSearch;
1323
+ if (!this.showTreeSearch) {
1324
+ this.clearSearch();
1311
1325
  }
1312
1326
  }
1327
+ isSearchMatch(item) {
1328
+ const term = this.searchText.trim().toLowerCase();
1329
+ if (!term)
1330
+ return false;
1331
+ const label = String(item.label ?? '').toLowerCase();
1332
+ const title = String(item.title ?? '').toLowerCase();
1333
+ return label.includes(term) || title.includes(term);
1334
+ }
1335
+ closeMenu() {
1336
+ this.subSidebarVisible = false;
1337
+ this.sidebarVisible = false;
1338
+ }
1313
1339
  openMenu() {
1314
1340
  this.sidebarVisible = true;
1315
1341
  this.subSidebarVisible = true;
1316
1342
  }
1317
- expandPath(items, target) {
1318
- for (const item of items) {
1319
- if (item === target)
1320
- return true;
1321
- if (item.items && item.items.length > 0) {
1322
- const found = this.expandPath(item.items, target);
1323
- if (found) {
1324
- item.expanded = true; // expand parent
1325
- return true;
1326
- }
1327
- }
1343
+ syncViewport() {
1344
+ this.isMobile = window.innerWidth < 1024;
1345
+ this.layoutService.smallScreen = this.isMobile;
1346
+ if (this.isMobile) {
1347
+ this.sidebarVisible = false;
1348
+ this.subSidebarVisible = false;
1349
+ return;
1328
1350
  }
1329
- return false;
1351
+ this.sidebarVisible = true;
1352
+ this.subSidebarVisible = true;
1353
+ }
1354
+ toggleSidebar() {
1355
+ if (this.isMobile) {
1356
+ this.sidebarVisible = !this.sidebarVisible;
1357
+ this.subSidebarVisible = this.sidebarVisible;
1358
+ return;
1359
+ }
1360
+ this.subSidebarVisible = !this.subSidebarVisible;
1330
1361
  }
1331
- onMainClick(target) {
1332
- const items = this._menuItemsSubject.value;
1362
+ activateItem(target) {
1333
1363
  const isExternal = target.isExternal ?? false;
1334
1364
  if (!target.items?.length) {
1335
1365
  if (isExternal && target.url) {
1336
1366
  window.open(target.url, '_blank', 'noopener,noreferrer');
1337
- if (this.isMobile)
1338
- this.closeMenu();
1339
- this.expandPath(items, target);
1340
- this._menuItemsSubject.next([...items]);
1367
+ this.afterNavigate(target);
1341
1368
  return;
1342
1369
  }
1343
1370
  if (target.routerLink) {
@@ -1346,50 +1373,93 @@ class RoutesComponent {
1346
1373
  ? target.routerLink[0]
1347
1374
  : target.routerLink;
1348
1375
  if (currentUrl !== targetUrl) {
1349
- this.router.navigate(Array.isArray(target.routerLink) ? target.routerLink : [target.routerLink]);
1376
+ this.router.navigate(Array.isArray(target.routerLink)
1377
+ ? target.routerLink
1378
+ : [target.routerLink]);
1350
1379
  }
1351
- if (this.isMobile)
1352
- this.closeMenu();
1353
- this.expandPath(items, target);
1354
- this._menuItemsSubject.next([...items]);
1380
+ this.afterNavigate(target);
1355
1381
  return;
1356
1382
  }
1357
1383
  }
1358
1384
  target.expanded = !target.expanded;
1359
- this.subSidebarVisible = target.expanded;
1360
- this._menuItemsSubject.next([...items]);
1385
+ this.subSidebarVisible = true;
1386
+ this.refresh();
1387
+ }
1388
+ closeAfterNavigate() {
1389
+ if (this.isMobile) {
1390
+ this.closeMenu();
1391
+ }
1392
+ }
1393
+ getLinkProps(item) {
1394
+ const isExternal = item.isExternal ?? false;
1395
+ return {
1396
+ isExternal,
1397
+ href: isExternal ? (item.url ?? null) : null,
1398
+ routerLink: !isExternal ? (item.routerLink ?? null) : null,
1399
+ addRoute: item.addRoute ?? '',
1400
+ };
1361
1401
  }
1362
- toggleExpand(item) {
1402
+ expandPath(target) {
1403
+ const found = this.expandPathRecursive(this.items, target);
1404
+ this.refresh();
1405
+ return found;
1406
+ }
1407
+ toggleItem(item) {
1363
1408
  item.expanded = !item.expanded;
1409
+ this.refresh();
1364
1410
  }
1365
- closeMenu() {
1366
- this.subSidebarVisible = false;
1367
- this.sidebarVisible = false;
1368
- // this.menuItems$.subscribe(items => {
1369
- // items.forEach(item => item.expanded = false);
1370
- // });
1371
- }
1372
- // @HostListener('document:keydown.escape')
1373
- // onEsc() {
1374
- // this.closeMenu();
1375
- // }
1376
- onNavigate() {
1411
+ setExpandedByLevel(items, level = 1) {
1412
+ for (const item of items) {
1413
+ item.expanded = level === 1;
1414
+ if (item.items?.length) {
1415
+ this.setExpandedByLevel(item.items, level + 1);
1416
+ }
1417
+ }
1418
+ }
1419
+ captureExpandedState(items) {
1420
+ const state = new WeakMap();
1421
+ const traverse = (nodes) => {
1422
+ for (const node of nodes) {
1423
+ state.set(node, node.expanded === true);
1424
+ if (node.items?.length) {
1425
+ traverse(node.items);
1426
+ }
1427
+ }
1428
+ };
1429
+ traverse(items);
1430
+ return state;
1431
+ }
1432
+ restoreExpandedStateBeforeSearch() {
1433
+ if (!this.expandedStateBeforeSearch)
1434
+ return;
1435
+ const traverse = (nodes) => {
1436
+ for (const node of nodes) {
1437
+ node.expanded = this.expandedStateBeforeSearch?.get(node) ?? false;
1438
+ if (node.items?.length) {
1439
+ traverse(node.items);
1440
+ }
1441
+ }
1442
+ };
1443
+ traverse(this.items);
1444
+ this.expandedStateBeforeSearch = null;
1445
+ }
1446
+ afterNavigate(target) {
1377
1447
  if (this.isMobile) {
1378
1448
  this.closeMenu();
1379
1449
  }
1450
+ this.expandPath(target);
1380
1451
  }
1381
1452
  buildItems(routes) {
1382
1453
  return routes
1383
- .filter(r => !r.requiredPolicy || this.hasPermission(r.requiredPolicy))
1384
- .map(r => this.createItem(r));
1454
+ .filter((route) => !route.requiredPolicy || this.hasPermission(route.requiredPolicy))
1455
+ .map((route) => this.createItem(route));
1385
1456
  }
1386
1457
  createItem(route) {
1387
1458
  const item = {
1388
1459
  label: route.name,
1389
1460
  icon: route.iconClass,
1390
- // routerLink: route.path ? [route.path] : undefined,
1391
1461
  title: route.name,
1392
- expanded: false
1462
+ expanded: false,
1393
1463
  };
1394
1464
  if (route.isExternal) {
1395
1465
  item.url = route.path;
@@ -1403,43 +1473,109 @@ class RoutesComponent {
1403
1473
  if (this.routesService.hasChildren(route.name)) {
1404
1474
  item.items = this.buildItems(route.children ?? []);
1405
1475
  }
1406
- else {
1407
- // TODO: check if grant permission
1408
- // if(){
1409
- // item['actionButtons']= [
1410
- // {
1411
- // icon: 'pi pi-plus',
1412
- // tooltip: 'ADD',
1413
- // click: (item:any, event:any) => {
1414
- // this.router.navigate([route.path,'add']);
1415
- // },
1416
- // visible: (item:any) => true
1417
- // }
1418
- // ];
1419
- // }
1420
- }
1421
1476
  return item;
1422
1477
  }
1423
1478
  hasPermission(policy) {
1424
- // implement using permission service
1425
1479
  return true;
1426
1480
  }
1427
- getLinkProps(item) {
1428
- const isExternal = item.isExternal ?? false;
1429
- return {
1430
- isExternal,
1431
- href: isExternal ? item.url ?? null : null,
1432
- routerLink: !isExternal ? item.routerLink ?? null : null,
1433
- addRoute: item.addRoute ?? ''
1434
- };
1481
+ setExpandedRecursive(items, expanded) {
1482
+ for (const item of items) {
1483
+ item.expanded = expanded;
1484
+ if (item.items?.length) {
1485
+ this.setExpandedRecursive(item.items, expanded);
1486
+ }
1487
+ }
1488
+ }
1489
+ expandMatchedParents(items, keyword) {
1490
+ const term = keyword.trim().toLowerCase();
1491
+ let hasMatch = false;
1492
+ for (const item of items) {
1493
+ const label = String(item.label ?? '').toLowerCase();
1494
+ const title = String(item.title ?? '').toLowerCase();
1495
+ const selfMatch = label.includes(term) || title.includes(term);
1496
+ const childMatch = item.items?.length
1497
+ ? this.expandMatchedParents(item.items, keyword)
1498
+ : false;
1499
+ item.expanded = !!childMatch || !!selfMatch;
1500
+ if (selfMatch || childMatch) {
1501
+ hasMatch = true;
1502
+ }
1503
+ }
1504
+ return hasMatch;
1505
+ }
1506
+ expandPathRecursive(items, target) {
1507
+ for (const item of items) {
1508
+ if (item === target)
1509
+ return true;
1510
+ if (item.items?.length) {
1511
+ const found = this.expandPathRecursive(item.items, target);
1512
+ if (found) {
1513
+ item.expanded = true;
1514
+ return true;
1515
+ }
1516
+ }
1517
+ }
1518
+ return false;
1519
+ }
1520
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesMenuService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1521
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesMenuService });
1522
+ }
1523
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesMenuService, decorators: [{
1524
+ type: Injectable
1525
+ }], ctorParameters: () => [] });
1526
+
1527
+ class RoutesSearchComponent {
1528
+ menu = inject(RoutesMenuService);
1529
+ closeMenu() {
1530
+ this.menu.closeMenu();
1531
+ }
1532
+ expandAll() {
1533
+ this.menu.expandAll();
1534
+ }
1535
+ collapseAll() {
1536
+ this.menu.collapseAll();
1537
+ }
1538
+ toggleSearch() {
1539
+ this.menu.toggleSearch();
1540
+ }
1541
+ onSearchTextChange(value) {
1542
+ this.menu.search(value);
1543
+ }
1544
+ clearSearch() {
1545
+ this.menu.clearSearch();
1546
+ }
1547
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesSearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1548
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: RoutesSearchComponent, isStandalone: true, selector: "app-routes-search", ngImport: i0, template: "<div class=\"mb-4 space-y-3\">\r\n <div class=\"flex items-center justify-between gap-3\">\r\n <button\r\n type=\"button\"\r\n (click)=\"closeMenu()\"\r\n class=\"inline-flex h-9 items-center gap-1.5 rounded-full px-2 ps-1.5 pe-3 text-sm font-bold text-slate-500 transition hover:bg-primary-50 hover:text-primary-600 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n >\r\n <i class=\"pi pi-angle-right text-sm\"></i>\r\n <span>{{ 'back' | translate }}</span>\r\n </button>\r\n\r\n <div class=\"inline-flex items-center gap-1\">\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-300/25 bg-slate-50/85 text-slate-500 transition hover:-translate-y-px hover:border-primary-500/25 hover:bg-primary-50 hover:text-primary-600 dark:border-slate-400/15 dark:bg-gray-800/75 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n (click)=\"expandAll()\"\r\n pTooltip=\"\u0641\u062A\u062D \u0627\u0644\u0634\u062C\u0631\u0629 \u0643\u0627\u0645\u0644\u0629\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-plus text-xs\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-300/25 bg-slate-50/85 text-slate-500 transition hover:-translate-y-px hover:border-primary-500/25 hover:bg-primary-50 hover:text-primary-600 dark:border-slate-400/15 dark:bg-gray-800/75 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n (click)=\"collapseAll()\"\r\n pTooltip=\"\u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0634\u062C\u0631\u0629 \u0643\u0627\u0645\u0644\u0629\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-minus text-xs\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-xl border transition hover:-translate-y-px\"\r\n [ngClass]=\"\r\n menu.showTreeSearch\r\n ? 'border-transparent bg-primary-600 text-white shadow-lg shadow-primary-500/20'\r\n : 'border-primary-500/20 bg-primary-500/10 text-primary-600 hover:bg-primary-50 dark:text-primary-300 dark:hover:bg-primary-500/10'\r\n \"\r\n (click)=\"toggleSearch()\"\r\n pTooltip=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-search text-xs\"></i>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n @if (menu.showTreeSearch) {\r\n <div\r\n class=\"animate-slideIn rounded-2xl border border-white/45 bg-white/60 p-2 shadow-lg shadow-slate-900/10 backdrop-blur-xl dark:border-white/10 dark:bg-gray-900/70\"\r\n >\r\n <div class=\"relative flex items-center\">\r\n <i\r\n class=\"pi pi-search pointer-events-none absolute right-3 text-sm text-primary-500 dark:text-primary-300\"\r\n ></i>\r\n\r\n <input\r\n type=\"text\"\r\n [ngModel]=\"menu.searchText\"\r\n (ngModelChange)=\"onSearchTextChange($event)\"\r\n placeholder=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A...\"\r\n class=\"h-10 w-full rounded-2xl border border-slate-300/30 bg-slate-50/85 py-0 pl-9 pr-10 text-sm text-slate-700 outline-none transition placeholder:text-slate-400 focus:border-primary-500/50 focus:bg-white focus:ring-4 focus:ring-primary-500/10 dark:border-slate-400/20 dark:bg-gray-800/80 dark:text-gray-100 dark:placeholder:text-slate-500 dark:focus:bg-gray-900\"\r\n />\r\n\r\n @if (menu.searchText) {\r\n <button\r\n type=\"button\"\r\n class=\"absolute left-2 inline-flex h-7 w-7 items-center justify-center rounded-full text-slate-400 transition hover:bg-primary-50 hover:text-primary-600 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n (click)=\"clearSearch()\"\r\n >\r\n <i class=\"pi pi-times text-xs\"></i>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3$2.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1549
+ }
1550
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesSearchComponent, decorators: [{
1551
+ type: Component,
1552
+ args: [{ selector: 'app-routes-search', standalone: true, imports: [CommonModule, FormsModule, TooltipModule, TranslatePipe], template: "<div class=\"mb-4 space-y-3\">\r\n <div class=\"flex items-center justify-between gap-3\">\r\n <button\r\n type=\"button\"\r\n (click)=\"closeMenu()\"\r\n class=\"inline-flex h-9 items-center gap-1.5 rounded-full px-2 ps-1.5 pe-3 text-sm font-bold text-slate-500 transition hover:bg-primary-50 hover:text-primary-600 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n >\r\n <i class=\"pi pi-angle-right text-sm\"></i>\r\n <span>{{ 'back' | translate }}</span>\r\n </button>\r\n\r\n <div class=\"inline-flex items-center gap-1\">\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-300/25 bg-slate-50/85 text-slate-500 transition hover:-translate-y-px hover:border-primary-500/25 hover:bg-primary-50 hover:text-primary-600 dark:border-slate-400/15 dark:bg-gray-800/75 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n (click)=\"expandAll()\"\r\n pTooltip=\"\u0641\u062A\u062D \u0627\u0644\u0634\u062C\u0631\u0629 \u0643\u0627\u0645\u0644\u0629\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-plus text-xs\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-300/25 bg-slate-50/85 text-slate-500 transition hover:-translate-y-px hover:border-primary-500/25 hover:bg-primary-50 hover:text-primary-600 dark:border-slate-400/15 dark:bg-gray-800/75 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n (click)=\"collapseAll()\"\r\n pTooltip=\"\u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0634\u062C\u0631\u0629 \u0643\u0627\u0645\u0644\u0629\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-minus text-xs\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-xl border transition hover:-translate-y-px\"\r\n [ngClass]=\"\r\n menu.showTreeSearch\r\n ? 'border-transparent bg-primary-600 text-white shadow-lg shadow-primary-500/20'\r\n : 'border-primary-500/20 bg-primary-500/10 text-primary-600 hover:bg-primary-50 dark:text-primary-300 dark:hover:bg-primary-500/10'\r\n \"\r\n (click)=\"toggleSearch()\"\r\n pTooltip=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-search text-xs\"></i>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n @if (menu.showTreeSearch) {\r\n <div\r\n class=\"animate-slideIn rounded-2xl border border-white/45 bg-white/60 p-2 shadow-lg shadow-slate-900/10 backdrop-blur-xl dark:border-white/10 dark:bg-gray-900/70\"\r\n >\r\n <div class=\"relative flex items-center\">\r\n <i\r\n class=\"pi pi-search pointer-events-none absolute right-3 text-sm text-primary-500 dark:text-primary-300\"\r\n ></i>\r\n\r\n <input\r\n type=\"text\"\r\n [ngModel]=\"menu.searchText\"\r\n (ngModelChange)=\"onSearchTextChange($event)\"\r\n placeholder=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A...\"\r\n class=\"h-10 w-full rounded-2xl border border-slate-300/30 bg-slate-50/85 py-0 pl-9 pr-10 text-sm text-slate-700 outline-none transition placeholder:text-slate-400 focus:border-primary-500/50 focus:bg-white focus:ring-4 focus:ring-primary-500/10 dark:border-slate-400/20 dark:bg-gray-800/80 dark:text-gray-100 dark:placeholder:text-slate-500 dark:focus:bg-gray-900\"\r\n />\r\n\r\n @if (menu.searchText) {\r\n <button\r\n type=\"button\"\r\n class=\"absolute left-2 inline-flex h-7 w-7 items-center justify-center rounded-full text-slate-400 transition hover:bg-primary-50 hover:text-primary-600 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n (click)=\"clearSearch()\"\r\n >\r\n <i class=\"pi pi-times text-xs\"></i>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n" }]
1553
+ }] });
1554
+
1555
+ class RoutesComponent {
1556
+ menu = inject(RoutesMenuService);
1557
+ ngOnInit() {
1558
+ this.checkViewport();
1559
+ }
1560
+ onResize() {
1561
+ this.checkViewport();
1562
+ }
1563
+ checkViewport() {
1564
+ this.menu.syncViewport();
1435
1565
  }
1436
1566
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1437
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: RoutesComponent, isStandalone: true, selector: "app-routes", host: { listeners: { "window:resize": "onResize()" } }, ngImport: i0, template: "<aside class=\"flex h-screen bg-white dark:bg-gray-900 transition-all duration-300\"\n [ngClass]=\"{ 'w-0 overflow-hidden': !sidebarVisible && isMobile }\">\n\n <!-- \uD83C\uDFA8 \u0627\u0644\u0634\u0631\u064A\u0637 \u0627\u0644\u062C\u0627\u0646\u0628\u064A \u0627\u0644\u0631\u0626\u064A\u0633\u064A -->\n <div\n class=\"flex flex-col justify-between pt-4 pb-2 w-16 border-l shadow-xl\n bg-gradient-to-b from-primary-500 via-primary-600 to-primary-700\n text-white rounded-r-3xl\">\n\n <!-- \uD83D\uDD1D \u0627\u0644\u0623\u0639\u0644\u0649 -->\n <div class=\"flex flex-col items-center gap-4\">\n <img src=\"/assets/logo.png\" alt=\"Logo\"\n class=\"w-9 h-9 mt-1 rounded-lg shadow-sm\"/>\n\n <!-- \u0632\u0631 \u0627\u0644\u0642\u0627\u0626\u0645\u0629 -->\n <!-- [pTooltip]=\"'TOGGLE_MENU' | translate\" -->\n <button (click)=\"toggleAll()\"\n tooltipPosition=\"right\"\n class=\"text-white hover:scale-110 transition-transform duration-200\">\n <i class=\"pi pi-bars text-lg\"></i>\n </button>\n\n <!-- \u0623\u064A\u0642\u0648\u0646\u0627\u062A -->\n @for (item of secondToLastLevel$ | async; track item) {\n <div class=\"relative flex flex-col items-center\">\n <!-- \u0627\u0644\u0623\u064A\u0642\u0648\u0646\u0629 -->\n <div (click)=\"onMainClick(item)\"\n [pTooltip]=\"item.label | translate\"\n tooltipPosition=\"right\"\n class=\"relative flex items-center justify-center w-10 h-10 rounded-xl cursor-pointer\n hover:bg-white/20 transition-all duration-200\"\n [ngClass]=\"{ 'bg-white/30 scale-105 shadow-inner': item.expanded }\">\n <i [class]=\"item.icon + ' text-xl'\"></i>\n </div>\n </div>\n }\n </div>\n\n <!-- \u2699\uFE0F \u0627\u0644\u0623\u0633\u0641\u0644 -->\n <div class=\"flex flex-col items-center pb-3 text-white/80\">\n <i class=\"pi pi-cog text-lg hover:text-white cursor-pointer transition\"></i>\n </div>\n </div>\n\n <!-- \uD83D\uDCCB \u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0641\u0631\u0639\u064A\u0629 -->\n @if (subSidebarVisible) {\n <div\n class=\"w-[290px] max-w-[290px] min-w-[260px] border-l border-gray-200 dark:border-gray-700 glass-bg animate-slideIn\n shadow-2xl rounded-l-3xl p-4 rtl:text-right overflow-y-auto overflow-x-hidden transition-all duration-300\"\n [ngClass]=\"{ 'absolute top-0 right-16 h-full z-40': isMobile }\">\n <!-- \u0632\u0631 \u0631\u062C\u0648\u0639 -->\n <button (click)=\"closeMenu()\"\n class=\"flex items-center gap-2 mb-4 text-gray-500 dark:text-gray-300 hover:text-primary-600 transition\">\n <i class=\"pi\" [ngClass]=\"isMobile ? 'pi-angle-right' : 'pi-angle-right'\"></i>\n <span>{{ 'back' | translate }}</span>\n </button>\n <!-- Recursive rendering -->\n @if (menuItems$ | async; as menuItems) {\n <ng-container *ngTemplateOutlet=\"renderMenu; context:{ $implicit: menuItems, level: 1 }\"></ng-container>\n}\n\n<ng-template #renderMenu let-items let-level=\"level\">\n @for (item of items; track item) {\n\n @if (!item.items?.length) {\n @let link = getLinkProps(item);\n <div class=\"mb-1\">\n @if (link.isExternal) {\n <a [href]=\"link.href\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n (click)=\"$event.stopPropagation()\"\n class=\"flex items-center gap-2 px-3 py-2 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\">\n <i [class]=\"item.icon + ' text-base'\"></i>\n <span class=\"min-w-0 break-words whitespace-normal\">{{ item.label | translate }}</span>\n <i class=\"pi pi-external-link text-[10px] opacity-50\"></i>\n </a>\n } @else {\n <div class=\"flex items-center gap-2\">\n <a [routerLink]=\"link.routerLink\"\n (click)=\"onNavigate()\"\n routerLinkActive=\"active-link\"\n class=\"flex-1 min-w-0 flex items-center gap-2 px-2 py-1 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\">\n <i [class]=\"item.icon + ' text-base'\"></i>\n <span class=\"min-w-0 break-words whitespace-normal\">\n {{ item.label | translate }}</span>\n </a>\n\n@if (link.addRoute) {\n <a\n [routerLink]=\"[link.addRoute]\"\n (click)=\"$event.stopPropagation(); onNavigate()\"\n class=\"shrink-0 inline-flex items-center justify-center w-7 h-7 rounded-full text-gray-400 hover:text-primary-500 hover:bg-primary-50 dark:text-gray-500 dark:hover:text-primary-400 dark:hover:bg-primary-900/20 transition-all duration-200\"\n [pTooltip]=\"'ADD' | translate\"\n tooltipPosition=\"top\">\n <i class=\"pi pi-plus text-xs\"></i>\n </a>\n}\n </div>\n }\n</div>\n}\n\n @if (item.items?.length) {\n <div class=\"mb-1\">\n\n <div\n (click)=\"onParentClick(item)\"\n [style.font-size.rem]=\"1.1 - (level * 0.05)\"\n [ngClass]=\"{\n 'font-bold': level === 1,\n 'font-semibold': level === 2,\n 'font-medium': level >= 3,\n 'bg-primary-50 text-primary-700 border-r-4 border-primary-500': item.expanded\n }\"\n class=\"flex justify-between items-center py-2 px-3 rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-800 dark:text-gray-200\">\n\n <div class=\"flex items-center gap-2\">\n\n <i [class]=\"item.icon + ' text-base'\"></i>\n\n <span>\n {{ item.label | translate }}\n </span>\n\n </div>\n\n <i\n class=\"pi\"\n [ngClass]=\"item.expanded ? 'pi-chevron-down' : 'pi-chevron-left'\"\n style=\"font-size:0.6rem;\">\n </i>\n\n </div>\n\n @if (item.expanded) {\n <div class=\"mr-4 border-r border-gray-200 dark:border-gray-700 pr-2 mt-1\">\n\n <ng-container\n *ngTemplateOutlet=\"renderMenu; context:{ $implicit: item.items, level: level + 1 }\">\n </ng-container>\n\n </div>\n }\n\n </div>\n }\n\n }\n</ng-template>\n\n </div>\n}\n\n</aside>\n\n<!-- \uD83C\uDF1F \u0627\u0644\u0632\u0631 \u0627\u0644\u0639\u0627\u0626\u0645 \u0627\u0644\u062D\u062F\u064A\u062B -->\n@if (isMobile && !sidebarVisible) {\n <button\n class=\"floating-btn\"\n (click)=\"openMenu()\"\n pTooltip=\"{{ 'MENU.OPEN' | translate }}\"\n tooltipPosition=\"top\">\n <img src=\"/assets/logo.png\" alt=\"App Icon\" />\n </button>\n}\n", styles: ["@charset \"UTF-8\";@keyframes slideIn{0%{transform:translate(60px);opacity:0}to{transform:translate(0);opacity:1}}.animate-slideIn{animation:slideIn .35s cubic-bezier(.25,1,.5,1)}.glass-bg{background:#ffffffbf;backdrop-filter:blur(16px) saturate(180%);-webkit-backdrop-filter:blur(16px) saturate(180%);border:1px solid rgba(255,255,255,.25)}@media(max-width:1024px){aside{position:fixed;top:0;right:0;height:100vh;z-index:50}}.floating-btn{position:fixed;bottom:1.25rem;right:1.25rem;z-index:60;width:60px;height:60px;border-radius:50%;background:linear-gradient(135deg,var(--p-primary-500),var(--p-primary-700));box-shadow:0 4px 18px #00000040;display:flex;align-items:center;justify-content:center;transition:all .3s ease}.floating-btn:hover{transform:scale(1.1) rotate(5deg)}.floating-btn img{width:28px;height:28px;border-radius:10px}.active-link{background-color:var(--p-primary-500)!important;color:#fff!important;font-weight:600;box-shadow:0 4px 6px -1px #0000001a;transform:translate(-3px)}.active-link i{color:#fff!important}.active-item-link{background:linear-gradient(to left,var(--p-primary-50),transparent);color:var(--p-primary-600)!important;position:relative;border-right:3px solid var(--p-primary-500);border-radius:0 12px 12px 0}.active-item-link i{color:var(--p-primary-500);filter:drop-shadow(0 0 5px rgba(var(--p-primary-500-rgb),.4))}.active-item-link span{font-weight:700}.animate-slideIn{animation:slideIn .3s ease-out}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.glass-bg::-webkit-scrollbar{width:4px}.glass-bg::-webkit-scrollbar-thumb{background:#0000001a;border-radius:10px}.active-link{background-color:var(--p-primary-50)!important;color:var(--p-primary-700)!important;border-right:4px solid var(--p-primary-500)!important;border-radius:4px 12px 12px 4px!important}.active-link span{font-weight:800!important}.active-link i{color:var(--p-primary-600)!important;transform:scale(1.1)}aside{-webkit-font-smoothing:antialiased;letter-spacing:-.01em}.dark .active-link{background-color:rgba(var(--p-primary-500-rgb),.15)!important;color:var(--p-primary-300)!important}.submenu{animation:menuOpen .25s ease}@keyframes menuOpen{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}aside{box-shadow:0 10px 40px #00000026}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2$2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3$2.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1567
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: RoutesComponent, isStandalone: true, selector: "app-routes", host: { listeners: { "window:resize": "onResize()" } }, providers: [RoutesMenuService], ngImport: i0, template: "<aside\r\n class=\"flex h-screen bg-white dark:bg-gray-900 transition-all duration-300\"\r\n [ngClass]=\"{ 'w-0 overflow-hidden': !menu.sidebarVisible && menu.isMobile }\"\r\n>\r\n <div\r\n class=\"flex flex-col justify-between pt-4 pb-2 w-16 border-l shadow-xl bg-gradient-to-b from-primary-500 via-primary-600 to-primary-700 text-white rounded-r-3xl\"\r\n >\r\n <div class=\"flex flex-col items-center gap-4\">\r\n <img\r\n src=\"/assets/logo.png\"\r\n alt=\"Logo\"\r\n class=\"w-9 h-9 mt-1 rounded-lg shadow-sm\"\r\n />\r\n\r\n <button\r\n (click)=\"menu.toggleSidebar()\"\r\n tooltipPosition=\"right\"\r\n class=\"text-white hover:scale-110 transition-transform duration-200\"\r\n >\r\n <i class=\"pi pi-bars text-lg\"></i>\r\n </button>\r\n @for (item of menu.secondToLastLevel$ | async; track item) {\r\n <div class=\"relative flex flex-col items-center\">\r\n <div\r\n (click)=\"menu.activateItem(item)\"\r\n [pTooltip]=\"item.label | translate\"\r\n tooltipPosition=\"right\"\r\n class=\"relative flex items-center justify-center w-10 h-10 rounded-xl cursor-pointer hover:bg-white/20 transition-all duration-200\"\r\n [ngClass]=\"{ 'bg-white/30 scale-105 shadow-inner': item.expanded }\"\r\n >\r\n <i [class]=\"item.icon + ' text-xl'\"></i>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n <div class=\"flex flex-col items-center pb-3 text-white/80\">\r\n <i\r\n class=\"pi pi-cog text-lg hover:text-white cursor-pointer transition\"\r\n ></i>\r\n </div>\r\n </div>\r\n @if (menu.subSidebarVisible) {\r\n <div\r\n class=\"w-[290px] max-w-[290px] min-w-[260px] border-l border-gray-200 dark:border-gray-700 glass-bg animate-slideIn shadow-2xl rounded-l-3xl p-4 rtl:text-right overflow-y-auto overflow-x-hidden transition-all duration-300\"\r\n [ngClass]=\"{ 'absolute top-0 right-16 h-full z-40': menu.isMobile }\"\r\n >\r\n <app-routes-search></app-routes-search>\r\n\r\n <!-- Recursive rendering -->\r\n @if (menu.menuItems$ | async; as menuItems) {\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n renderMenu;\r\n context: { $implicit: menuItems, level: 1 }\r\n \"\r\n ></ng-container>\r\n }\r\n\r\n <ng-template #renderMenu let-items let-level=\"level\">\r\n @for (item of items; track item) {\r\n @if (!item.items?.length) {\r\n @let link = menu.getLinkProps(item);\r\n <div class=\"mb-1\">\r\n @if (link.isExternal) {\r\n <a\r\n [href]=\"link.href\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n (click)=\"$event.stopPropagation()\"\r\n class=\"flex items-center gap-2 px-3 py-2 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\"\r\n >\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n <span class=\"min-w-0 break-words whitespace-normal\">{{\r\n item.label | translate\r\n }}</span>\r\n <i class=\"pi pi-external-link text-[10px] opacity-50\"></i>\r\n </a>\r\n } @else {\r\n <div class=\"flex items-center gap-2\">\r\n <a\r\n [routerLink]=\"link.routerLink\"\r\n (click)=\"menu.closeAfterNavigate()\"\r\n routerLinkActive=\"active-link\"\r\n [ngClass]=\"{ 'search-match': menu.isSearchMatch(item) }\"\r\n class=\"flex-1 min-w-0 flex items-center gap-2 px-2 py-1 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\"\r\n >\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n <span class=\"min-w-0 break-words whitespace-normal\">\r\n {{ item.label | translate }}</span\r\n >\r\n </a>\r\n\r\n @if (link.addRoute) {\r\n <a\r\n [routerLink]=\"[link.addRoute]\"\r\n (click)=\"\r\n $event.stopPropagation(); menu.closeAfterNavigate()\r\n \"\r\n class=\"shrink-0 inline-flex items-center justify-center w-7 h-7 rounded-full text-gray-400 hover:text-primary-500 hover:bg-primary-50 dark:text-gray-500 dark:hover:text-primary-400 dark:hover:bg-primary-900/20 transition-all duration-200\"\r\n [pTooltip]=\"'ADD' | translate\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-plus text-xs\"></i>\r\n </a>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (item.items?.length) {\r\n <div class=\"mb-1\">\r\n <div\r\n (click)=\"menu.toggleItem(item)\"\r\n [style.font-size.rem]=\"1.1 - level * 0.05\"\r\n [ngClass]=\"{\r\n 'font-bold': level === 1,\r\n 'font-semibold': level === 2,\r\n 'font-medium': level >= 3,\r\n 'bg-primary-50 text-primary-700 border-r-4 border-primary-500':\r\n item.expanded,\r\n 'search-match': menu.isSearchMatch(item),\r\n }\"\r\n class=\"flex justify-between items-center py-2 px-3 rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-800 dark:text-gray-200\"\r\n >\r\n <div class=\"flex items-center gap-2\">\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n\r\n <span>\r\n {{ item.label | translate }}\r\n </span>\r\n </div>\r\n\r\n <i\r\n class=\"pi\"\r\n [ngClass]=\"\r\n item.expanded ? 'pi-chevron-down' : 'pi-chevron-left'\r\n \"\r\n style=\"font-size: 0.6rem\"\r\n >\r\n </i>\r\n </div>\r\n\r\n @if (item.expanded) {\r\n <div\r\n class=\"mr-4 border-r border-gray-200 dark:border-gray-700 pr-2 mt-1\"\r\n >\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n renderMenu;\r\n context: { $implicit: item.items, level: level + 1 }\r\n \"\r\n >\r\n </ng-container>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n </ng-template>\r\n </div>\r\n }\r\n</aside>\r\n@if (menu.isMobile && !menu.sidebarVisible) {\r\n <button\r\n class=\"floating-btn\"\r\n (click)=\"menu.openMenu()\"\r\n pTooltip=\"{{ 'MENU.OPEN' | translate }}\"\r\n tooltipPosition=\"top\"\r\n >\r\n <img src=\"/assets/logo.png\" alt=\"App Icon\" />\r\n </button>\r\n}\r\n", styles: ["@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.animate-slideIn{animation:slideIn .3s ease-out}.glass-bg{background-color:#ffffffbf;--tw-backdrop-blur: blur(24px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.glass-bg:is(.dark *){background-color:#111827cc}.glass-bg{-webkit-backdrop-filter:blur(16px) saturate(180%);backdrop-filter:blur(16px) saturate(180%)}.glass-bg::-webkit-scrollbar{width:4px}.glass-bg::-webkit-scrollbar-thumb{border-radius:9999px;background-color:#94a3b84d}@media(max-width:1024px){aside{position:fixed;right:0;top:0;z-index:50;height:100vh}}.floating-btn{position:fixed;bottom:1.25rem;right:1.25rem;z-index:60;display:flex;height:60px;width:60px;align-items:center;justify-content:center;border-radius:9999px;--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s;background:linear-gradient(135deg,var(--p-primary-500),var(--p-primary-700))}.floating-btn:hover{transform:scale(1.1) rotate(5deg)}.floating-btn img{height:1.75rem;width:1.75rem;border-radius:.5rem}.active-link{border-radius:.5rem;--tw-bg-opacity: 1;background-color:color-mix(in srgb,var(--p-primary-50) calc(100% * var(--tw-bg-opacity, 1)),transparent);font-weight:600;--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-700) calc(100% * var(--tw-text-opacity, 1)),transparent);--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.active-link:is(.dark *){background-color:color-mix(in srgb,var(--p-primary-500) 15%,transparent);--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-300) calc(100% * var(--tw-text-opacity, 1)),transparent)}.active-link{border-right:4px solid var(--p-primary-500)}.active-link span{font-weight:800}.active-link i{--tw-scale-x: 1.1;--tw-scale-y: 1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-600) calc(100% * var(--tw-text-opacity, 1)),transparent)}.active-link i:is(.dark *){--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-300) calc(100% * var(--tw-text-opacity, 1)),transparent)}.search-match{color:var(--p-primary-700)!important;background:linear-gradient(to left,rgba(var(--p-primary-500-rgb),.16),transparent)!important;border-right:3px solid var(--p-primary-500)!important;box-shadow:inset 0 0 0 1px rgba(var(--p-primary-500-rgb),.08)}.search-match span{font-weight:800}.search-match i{color:var(--p-primary-600)!important}.dark .search-match{color:var(--p-primary-300)!important;background:rgba(var(--p-primary-500-rgb),.18)!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2$2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3$2.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip"] }, { kind: "component", type: RoutesSearchComponent, selector: "app-routes-search" }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1438
1568
  }
1439
1569
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesComponent, decorators: [{
1440
1570
  type: Component,
1441
- args: [{ selector: 'app-routes', standalone: true, imports: [CommonModule, RouterModule, TooltipModule, TranslatePipe, StyleClassModule], template: "<aside class=\"flex h-screen bg-white dark:bg-gray-900 transition-all duration-300\"\n [ngClass]=\"{ 'w-0 overflow-hidden': !sidebarVisible && isMobile }\">\n\n <!-- \uD83C\uDFA8 \u0627\u0644\u0634\u0631\u064A\u0637 \u0627\u0644\u062C\u0627\u0646\u0628\u064A \u0627\u0644\u0631\u0626\u064A\u0633\u064A -->\n <div\n class=\"flex flex-col justify-between pt-4 pb-2 w-16 border-l shadow-xl\n bg-gradient-to-b from-primary-500 via-primary-600 to-primary-700\n text-white rounded-r-3xl\">\n\n <!-- \uD83D\uDD1D \u0627\u0644\u0623\u0639\u0644\u0649 -->\n <div class=\"flex flex-col items-center gap-4\">\n <img src=\"/assets/logo.png\" alt=\"Logo\"\n class=\"w-9 h-9 mt-1 rounded-lg shadow-sm\"/>\n\n <!-- \u0632\u0631 \u0627\u0644\u0642\u0627\u0626\u0645\u0629 -->\n <!-- [pTooltip]=\"'TOGGLE_MENU' | translate\" -->\n <button (click)=\"toggleAll()\"\n tooltipPosition=\"right\"\n class=\"text-white hover:scale-110 transition-transform duration-200\">\n <i class=\"pi pi-bars text-lg\"></i>\n </button>\n\n <!-- \u0623\u064A\u0642\u0648\u0646\u0627\u062A -->\n @for (item of secondToLastLevel$ | async; track item) {\n <div class=\"relative flex flex-col items-center\">\n <!-- \u0627\u0644\u0623\u064A\u0642\u0648\u0646\u0629 -->\n <div (click)=\"onMainClick(item)\"\n [pTooltip]=\"item.label | translate\"\n tooltipPosition=\"right\"\n class=\"relative flex items-center justify-center w-10 h-10 rounded-xl cursor-pointer\n hover:bg-white/20 transition-all duration-200\"\n [ngClass]=\"{ 'bg-white/30 scale-105 shadow-inner': item.expanded }\">\n <i [class]=\"item.icon + ' text-xl'\"></i>\n </div>\n </div>\n }\n </div>\n\n <!-- \u2699\uFE0F \u0627\u0644\u0623\u0633\u0641\u0644 -->\n <div class=\"flex flex-col items-center pb-3 text-white/80\">\n <i class=\"pi pi-cog text-lg hover:text-white cursor-pointer transition\"></i>\n </div>\n </div>\n\n <!-- \uD83D\uDCCB \u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0641\u0631\u0639\u064A\u0629 -->\n @if (subSidebarVisible) {\n <div\n class=\"w-[290px] max-w-[290px] min-w-[260px] border-l border-gray-200 dark:border-gray-700 glass-bg animate-slideIn\n shadow-2xl rounded-l-3xl p-4 rtl:text-right overflow-y-auto overflow-x-hidden transition-all duration-300\"\n [ngClass]=\"{ 'absolute top-0 right-16 h-full z-40': isMobile }\">\n <!-- \u0632\u0631 \u0631\u062C\u0648\u0639 -->\n <button (click)=\"closeMenu()\"\n class=\"flex items-center gap-2 mb-4 text-gray-500 dark:text-gray-300 hover:text-primary-600 transition\">\n <i class=\"pi\" [ngClass]=\"isMobile ? 'pi-angle-right' : 'pi-angle-right'\"></i>\n <span>{{ 'back' | translate }}</span>\n </button>\n <!-- Recursive rendering -->\n @if (menuItems$ | async; as menuItems) {\n <ng-container *ngTemplateOutlet=\"renderMenu; context:{ $implicit: menuItems, level: 1 }\"></ng-container>\n}\n\n<ng-template #renderMenu let-items let-level=\"level\">\n @for (item of items; track item) {\n\n @if (!item.items?.length) {\n @let link = getLinkProps(item);\n <div class=\"mb-1\">\n @if (link.isExternal) {\n <a [href]=\"link.href\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n (click)=\"$event.stopPropagation()\"\n class=\"flex items-center gap-2 px-3 py-2 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\">\n <i [class]=\"item.icon + ' text-base'\"></i>\n <span class=\"min-w-0 break-words whitespace-normal\">{{ item.label | translate }}</span>\n <i class=\"pi pi-external-link text-[10px] opacity-50\"></i>\n </a>\n } @else {\n <div class=\"flex items-center gap-2\">\n <a [routerLink]=\"link.routerLink\"\n (click)=\"onNavigate()\"\n routerLinkActive=\"active-link\"\n class=\"flex-1 min-w-0 flex items-center gap-2 px-2 py-1 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\">\n <i [class]=\"item.icon + ' text-base'\"></i>\n <span class=\"min-w-0 break-words whitespace-normal\">\n {{ item.label | translate }}</span>\n </a>\n\n@if (link.addRoute) {\n <a\n [routerLink]=\"[link.addRoute]\"\n (click)=\"$event.stopPropagation(); onNavigate()\"\n class=\"shrink-0 inline-flex items-center justify-center w-7 h-7 rounded-full text-gray-400 hover:text-primary-500 hover:bg-primary-50 dark:text-gray-500 dark:hover:text-primary-400 dark:hover:bg-primary-900/20 transition-all duration-200\"\n [pTooltip]=\"'ADD' | translate\"\n tooltipPosition=\"top\">\n <i class=\"pi pi-plus text-xs\"></i>\n </a>\n}\n </div>\n }\n</div>\n}\n\n @if (item.items?.length) {\n <div class=\"mb-1\">\n\n <div\n (click)=\"onParentClick(item)\"\n [style.font-size.rem]=\"1.1 - (level * 0.05)\"\n [ngClass]=\"{\n 'font-bold': level === 1,\n 'font-semibold': level === 2,\n 'font-medium': level >= 3,\n 'bg-primary-50 text-primary-700 border-r-4 border-primary-500': item.expanded\n }\"\n class=\"flex justify-between items-center py-2 px-3 rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-800 dark:text-gray-200\">\n\n <div class=\"flex items-center gap-2\">\n\n <i [class]=\"item.icon + ' text-base'\"></i>\n\n <span>\n {{ item.label | translate }}\n </span>\n\n </div>\n\n <i\n class=\"pi\"\n [ngClass]=\"item.expanded ? 'pi-chevron-down' : 'pi-chevron-left'\"\n style=\"font-size:0.6rem;\">\n </i>\n\n </div>\n\n @if (item.expanded) {\n <div class=\"mr-4 border-r border-gray-200 dark:border-gray-700 pr-2 mt-1\">\n\n <ng-container\n *ngTemplateOutlet=\"renderMenu; context:{ $implicit: item.items, level: level + 1 }\">\n </ng-container>\n\n </div>\n }\n\n </div>\n }\n\n }\n</ng-template>\n\n </div>\n}\n\n</aside>\n\n<!-- \uD83C\uDF1F \u0627\u0644\u0632\u0631 \u0627\u0644\u0639\u0627\u0626\u0645 \u0627\u0644\u062D\u062F\u064A\u062B -->\n@if (isMobile && !sidebarVisible) {\n <button\n class=\"floating-btn\"\n (click)=\"openMenu()\"\n pTooltip=\"{{ 'MENU.OPEN' | translate }}\"\n tooltipPosition=\"top\">\n <img src=\"/assets/logo.png\" alt=\"App Icon\" />\n </button>\n}\n", styles: ["@charset \"UTF-8\";@keyframes slideIn{0%{transform:translate(60px);opacity:0}to{transform:translate(0);opacity:1}}.animate-slideIn{animation:slideIn .35s cubic-bezier(.25,1,.5,1)}.glass-bg{background:#ffffffbf;backdrop-filter:blur(16px) saturate(180%);-webkit-backdrop-filter:blur(16px) saturate(180%);border:1px solid rgba(255,255,255,.25)}@media(max-width:1024px){aside{position:fixed;top:0;right:0;height:100vh;z-index:50}}.floating-btn{position:fixed;bottom:1.25rem;right:1.25rem;z-index:60;width:60px;height:60px;border-radius:50%;background:linear-gradient(135deg,var(--p-primary-500),var(--p-primary-700));box-shadow:0 4px 18px #00000040;display:flex;align-items:center;justify-content:center;transition:all .3s ease}.floating-btn:hover{transform:scale(1.1) rotate(5deg)}.floating-btn img{width:28px;height:28px;border-radius:10px}.active-link{background-color:var(--p-primary-500)!important;color:#fff!important;font-weight:600;box-shadow:0 4px 6px -1px #0000001a;transform:translate(-3px)}.active-link i{color:#fff!important}.active-item-link{background:linear-gradient(to left,var(--p-primary-50),transparent);color:var(--p-primary-600)!important;position:relative;border-right:3px solid var(--p-primary-500);border-radius:0 12px 12px 0}.active-item-link i{color:var(--p-primary-500);filter:drop-shadow(0 0 5px rgba(var(--p-primary-500-rgb),.4))}.active-item-link span{font-weight:700}.animate-slideIn{animation:slideIn .3s ease-out}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.glass-bg::-webkit-scrollbar{width:4px}.glass-bg::-webkit-scrollbar-thumb{background:#0000001a;border-radius:10px}.active-link{background-color:var(--p-primary-50)!important;color:var(--p-primary-700)!important;border-right:4px solid var(--p-primary-500)!important;border-radius:4px 12px 12px 4px!important}.active-link span{font-weight:800!important}.active-link i{color:var(--p-primary-600)!important;transform:scale(1.1)}aside{-webkit-font-smoothing:antialiased;letter-spacing:-.01em}.dark .active-link{background-color:rgba(var(--p-primary-500-rgb),.15)!important;color:var(--p-primary-300)!important}.submenu{animation:menuOpen .25s ease}@keyframes menuOpen{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}aside{box-shadow:0 10px 40px #00000026}\n"] }]
1442
- }], ctorParameters: () => [], propDecorators: { onResize: [{
1571
+ args: [{ selector: 'app-routes', standalone: true, imports: [
1572
+ CommonModule,
1573
+ RouterModule,
1574
+ TooltipModule,
1575
+ TranslatePipe,
1576
+ RoutesSearchComponent,
1577
+ ], providers: [RoutesMenuService], template: "<aside\r\n class=\"flex h-screen bg-white dark:bg-gray-900 transition-all duration-300\"\r\n [ngClass]=\"{ 'w-0 overflow-hidden': !menu.sidebarVisible && menu.isMobile }\"\r\n>\r\n <div\r\n class=\"flex flex-col justify-between pt-4 pb-2 w-16 border-l shadow-xl bg-gradient-to-b from-primary-500 via-primary-600 to-primary-700 text-white rounded-r-3xl\"\r\n >\r\n <div class=\"flex flex-col items-center gap-4\">\r\n <img\r\n src=\"/assets/logo.png\"\r\n alt=\"Logo\"\r\n class=\"w-9 h-9 mt-1 rounded-lg shadow-sm\"\r\n />\r\n\r\n <button\r\n (click)=\"menu.toggleSidebar()\"\r\n tooltipPosition=\"right\"\r\n class=\"text-white hover:scale-110 transition-transform duration-200\"\r\n >\r\n <i class=\"pi pi-bars text-lg\"></i>\r\n </button>\r\n @for (item of menu.secondToLastLevel$ | async; track item) {\r\n <div class=\"relative flex flex-col items-center\">\r\n <div\r\n (click)=\"menu.activateItem(item)\"\r\n [pTooltip]=\"item.label | translate\"\r\n tooltipPosition=\"right\"\r\n class=\"relative flex items-center justify-center w-10 h-10 rounded-xl cursor-pointer hover:bg-white/20 transition-all duration-200\"\r\n [ngClass]=\"{ 'bg-white/30 scale-105 shadow-inner': item.expanded }\"\r\n >\r\n <i [class]=\"item.icon + ' text-xl'\"></i>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n <div class=\"flex flex-col items-center pb-3 text-white/80\">\r\n <i\r\n class=\"pi pi-cog text-lg hover:text-white cursor-pointer transition\"\r\n ></i>\r\n </div>\r\n </div>\r\n @if (menu.subSidebarVisible) {\r\n <div\r\n class=\"w-[290px] max-w-[290px] min-w-[260px] border-l border-gray-200 dark:border-gray-700 glass-bg animate-slideIn shadow-2xl rounded-l-3xl p-4 rtl:text-right overflow-y-auto overflow-x-hidden transition-all duration-300\"\r\n [ngClass]=\"{ 'absolute top-0 right-16 h-full z-40': menu.isMobile }\"\r\n >\r\n <app-routes-search></app-routes-search>\r\n\r\n <!-- Recursive rendering -->\r\n @if (menu.menuItems$ | async; as menuItems) {\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n renderMenu;\r\n context: { $implicit: menuItems, level: 1 }\r\n \"\r\n ></ng-container>\r\n }\r\n\r\n <ng-template #renderMenu let-items let-level=\"level\">\r\n @for (item of items; track item) {\r\n @if (!item.items?.length) {\r\n @let link = menu.getLinkProps(item);\r\n <div class=\"mb-1\">\r\n @if (link.isExternal) {\r\n <a\r\n [href]=\"link.href\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n (click)=\"$event.stopPropagation()\"\r\n class=\"flex items-center gap-2 px-3 py-2 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\"\r\n >\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n <span class=\"min-w-0 break-words whitespace-normal\">{{\r\n item.label | translate\r\n }}</span>\r\n <i class=\"pi pi-external-link text-[10px] opacity-50\"></i>\r\n </a>\r\n } @else {\r\n <div class=\"flex items-center gap-2\">\r\n <a\r\n [routerLink]=\"link.routerLink\"\r\n (click)=\"menu.closeAfterNavigate()\"\r\n routerLinkActive=\"active-link\"\r\n [ngClass]=\"{ 'search-match': menu.isSearchMatch(item) }\"\r\n class=\"flex-1 min-w-0 flex items-center gap-2 px-2 py-1 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\"\r\n >\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n <span class=\"min-w-0 break-words whitespace-normal\">\r\n {{ item.label | translate }}</span\r\n >\r\n </a>\r\n\r\n @if (link.addRoute) {\r\n <a\r\n [routerLink]=\"[link.addRoute]\"\r\n (click)=\"\r\n $event.stopPropagation(); menu.closeAfterNavigate()\r\n \"\r\n class=\"shrink-0 inline-flex items-center justify-center w-7 h-7 rounded-full text-gray-400 hover:text-primary-500 hover:bg-primary-50 dark:text-gray-500 dark:hover:text-primary-400 dark:hover:bg-primary-900/20 transition-all duration-200\"\r\n [pTooltip]=\"'ADD' | translate\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-plus text-xs\"></i>\r\n </a>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (item.items?.length) {\r\n <div class=\"mb-1\">\r\n <div\r\n (click)=\"menu.toggleItem(item)\"\r\n [style.font-size.rem]=\"1.1 - level * 0.05\"\r\n [ngClass]=\"{\r\n 'font-bold': level === 1,\r\n 'font-semibold': level === 2,\r\n 'font-medium': level >= 3,\r\n 'bg-primary-50 text-primary-700 border-r-4 border-primary-500':\r\n item.expanded,\r\n 'search-match': menu.isSearchMatch(item),\r\n }\"\r\n class=\"flex justify-between items-center py-2 px-3 rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-800 dark:text-gray-200\"\r\n >\r\n <div class=\"flex items-center gap-2\">\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n\r\n <span>\r\n {{ item.label | translate }}\r\n </span>\r\n </div>\r\n\r\n <i\r\n class=\"pi\"\r\n [ngClass]=\"\r\n item.expanded ? 'pi-chevron-down' : 'pi-chevron-left'\r\n \"\r\n style=\"font-size: 0.6rem\"\r\n >\r\n </i>\r\n </div>\r\n\r\n @if (item.expanded) {\r\n <div\r\n class=\"mr-4 border-r border-gray-200 dark:border-gray-700 pr-2 mt-1\"\r\n >\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n renderMenu;\r\n context: { $implicit: item.items, level: level + 1 }\r\n \"\r\n >\r\n </ng-container>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n </ng-template>\r\n </div>\r\n }\r\n</aside>\r\n@if (menu.isMobile && !menu.sidebarVisible) {\r\n <button\r\n class=\"floating-btn\"\r\n (click)=\"menu.openMenu()\"\r\n pTooltip=\"{{ 'MENU.OPEN' | translate }}\"\r\n tooltipPosition=\"top\"\r\n >\r\n <img src=\"/assets/logo.png\" alt=\"App Icon\" />\r\n </button>\r\n}\r\n", styles: ["@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.animate-slideIn{animation:slideIn .3s ease-out}.glass-bg{background-color:#ffffffbf;--tw-backdrop-blur: blur(24px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.glass-bg:is(.dark *){background-color:#111827cc}.glass-bg{-webkit-backdrop-filter:blur(16px) saturate(180%);backdrop-filter:blur(16px) saturate(180%)}.glass-bg::-webkit-scrollbar{width:4px}.glass-bg::-webkit-scrollbar-thumb{border-radius:9999px;background-color:#94a3b84d}@media(max-width:1024px){aside{position:fixed;right:0;top:0;z-index:50;height:100vh}}.floating-btn{position:fixed;bottom:1.25rem;right:1.25rem;z-index:60;display:flex;height:60px;width:60px;align-items:center;justify-content:center;border-radius:9999px;--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s;background:linear-gradient(135deg,var(--p-primary-500),var(--p-primary-700))}.floating-btn:hover{transform:scale(1.1) rotate(5deg)}.floating-btn img{height:1.75rem;width:1.75rem;border-radius:.5rem}.active-link{border-radius:.5rem;--tw-bg-opacity: 1;background-color:color-mix(in srgb,var(--p-primary-50) calc(100% * var(--tw-bg-opacity, 1)),transparent);font-weight:600;--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-700) calc(100% * var(--tw-text-opacity, 1)),transparent);--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.active-link:is(.dark *){background-color:color-mix(in srgb,var(--p-primary-500) 15%,transparent);--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-300) calc(100% * var(--tw-text-opacity, 1)),transparent)}.active-link{border-right:4px solid var(--p-primary-500)}.active-link span{font-weight:800}.active-link i{--tw-scale-x: 1.1;--tw-scale-y: 1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-600) calc(100% * var(--tw-text-opacity, 1)),transparent)}.active-link i:is(.dark *){--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-300) calc(100% * var(--tw-text-opacity, 1)),transparent)}.search-match{color:var(--p-primary-700)!important;background:linear-gradient(to left,rgba(var(--p-primary-500-rgb),.16),transparent)!important;border-right:3px solid var(--p-primary-500)!important;box-shadow:inset 0 0 0 1px rgba(var(--p-primary-500-rgb),.08)}.search-match span{font-weight:800}.search-match i{color:var(--p-primary-600)!important}.dark .search-match{color:var(--p-primary-300)!important;background:rgba(var(--p-primary-500-rgb),.18)!important}\n"] }]
1578
+ }], propDecorators: { onResize: [{
1443
1579
  type: HostListener,
1444
1580
  args: ['window:resize']
1445
1581
  }] } });
@@ -1447,11 +1583,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
1447
1583
  class AuthWrapperComponent {
1448
1584
  constructor() { }
1449
1585
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AuthWrapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1450
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: AuthWrapperComponent, isStandalone: true, selector: "abp-auth-wrapper", ngImport: i0, template: " <ng-content></ng-content>\n\n\n<!-- <div class=\"row\">\n <div class=\"mx-auto col col-md-5\">\n @if ((service.isMultiTenancyEnabled$ | async) && service.isTenantBoxVisible) {\n <abp-tenant-box\n *abpReplaceableTemplate=\"{ componentKey: service.tenantBoxKey }\"\n ></abp-tenant-box>\n }\n\n <div class=\"abp-account-container\">\n @if (service.enableLocalLogin$ | async) {\n <div class=\"card mt-3 shadow-sm rounded\">\n <div class=\"card-body p-5\">\n <ng-content></ng-content>\n </div>\n </div>\n } @else {\n <div class=\"alert alert-warning\">\n <strong>{{ 'AbpAccount::InvalidLoginRequest' | localize }}</strong>\n {{ 'AbpAccount::ThereAreNoLoginSchemesConfiguredForThisClient' | localize }}\n </div>\n }\n </div>\n </div>\n</div> -->\n" });
1586
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: AuthWrapperComponent, isStandalone: true, selector: "abp-auth-wrapper", ngImport: i0, template: " <ng-content></ng-content>\r\n\r\n\r\n<!-- <div class=\"row\">\r\n <div class=\"mx-auto col col-md-5\">\r\n @if ((service.isMultiTenancyEnabled$ | async) && service.isTenantBoxVisible) {\r\n <abp-tenant-box\r\n *abpReplaceableTemplate=\"{ componentKey: service.tenantBoxKey }\"\r\n ></abp-tenant-box>\r\n }\r\n\r\n <div class=\"abp-account-container\">\r\n @if (service.enableLocalLogin$ | async) {\r\n <div class=\"card mt-3 shadow-sm rounded\">\r\n <div class=\"card-body p-5\">\r\n <ng-content></ng-content>\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"alert alert-warning\">\r\n <strong>{{ 'AbpAccount::InvalidLoginRequest' | localize }}</strong>\r\n {{ 'AbpAccount::ThereAreNoLoginSchemesConfiguredForThisClient' | localize }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n</div> -->\r\n" });
1451
1587
  }
1452
1588
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AuthWrapperComponent, decorators: [{
1453
1589
  type: Component,
1454
- args: [{ standalone: true, imports: [], selector: 'abp-auth-wrapper', template: " <ng-content></ng-content>\n\n\n<!-- <div class=\"row\">\n <div class=\"mx-auto col col-md-5\">\n @if ((service.isMultiTenancyEnabled$ | async) && service.isTenantBoxVisible) {\n <abp-tenant-box\n *abpReplaceableTemplate=\"{ componentKey: service.tenantBoxKey }\"\n ></abp-tenant-box>\n }\n\n <div class=\"abp-account-container\">\n @if (service.enableLocalLogin$ | async) {\n <div class=\"card mt-3 shadow-sm rounded\">\n <div class=\"card-body p-5\">\n <ng-content></ng-content>\n </div>\n </div>\n } @else {\n <div class=\"alert alert-warning\">\n <strong>{{ 'AbpAccount::InvalidLoginRequest' | localize }}</strong>\n {{ 'AbpAccount::ThereAreNoLoginSchemesConfiguredForThisClient' | localize }}\n </div>\n }\n </div>\n </div>\n</div> -->\n" }]
1590
+ args: [{ standalone: true, imports: [], selector: 'abp-auth-wrapper', template: " <ng-content></ng-content>\r\n\r\n\r\n<!-- <div class=\"row\">\r\n <div class=\"mx-auto col col-md-5\">\r\n @if ((service.isMultiTenancyEnabled$ | async) && service.isTenantBoxVisible) {\r\n <abp-tenant-box\r\n *abpReplaceableTemplate=\"{ componentKey: service.tenantBoxKey }\"\r\n ></abp-tenant-box>\r\n }\r\n\r\n <div class=\"abp-account-container\">\r\n @if (service.enableLocalLogin$ | async) {\r\n <div class=\"card mt-3 shadow-sm rounded\">\r\n <div class=\"card-body p-5\">\r\n <ng-content></ng-content>\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"alert alert-warning\">\r\n <strong>{{ 'AbpAccount::InvalidLoginRequest' | localize }}</strong>\r\n {{ 'AbpAccount::ThereAreNoLoginSchemesConfiguredForThisClient' | localize }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n</div> -->\r\n" }]
1455
1591
  }], ctorParameters: () => [] });
1456
1592
 
1457
1593
  class AccountLayoutComponent {
@@ -1466,11 +1602,11 @@ class AccountLayoutComponent {
1466
1602
  // this.service.subscribeWindowSize();
1467
1603
  }
1468
1604
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AccountLayoutComponent, deps: [{ token: LayoutService }], target: i0.ɵɵFactoryTarget.Component });
1469
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: AccountLayoutComponent, isStandalone: true, selector: "abp-layout-account", providers: [LayoutService, SubscriptionService], ngImport: i0, template: " <abp-auth-wrapper\n *abpReplaceableTemplate=\"{\n componentKey: authWrapperKey\n }\"\n >\n <router-outlet #outlet=\"outlet\"></router-outlet>\n </abp-auth-wrapper>\n", dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: AuthWrapperComponent, selector: "abp-auth-wrapper" }, { kind: "directive", type: ReplaceableTemplateDirective, selector: "[abpReplaceableTemplate]", inputs: ["abpReplaceableTemplate"] }] });
1605
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: AccountLayoutComponent, isStandalone: true, selector: "abp-layout-account", providers: [LayoutService, SubscriptionService], ngImport: i0, template: " <abp-auth-wrapper\r\n *abpReplaceableTemplate=\"{\r\n componentKey: authWrapperKey\r\n }\"\r\n >\r\n <router-outlet #outlet=\"outlet\"></router-outlet>\r\n </abp-auth-wrapper>\r\n", dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: AuthWrapperComponent, selector: "abp-auth-wrapper" }, { kind: "directive", type: ReplaceableTemplateDirective, selector: "[abpReplaceableTemplate]", inputs: ["abpReplaceableTemplate"] }] });
1470
1606
  }
1471
1607
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AccountLayoutComponent, decorators: [{
1472
1608
  type: Component,
1473
- args: [{ standalone: true, imports: [RoutesComponent, NavItemsComponent, RouterOutlet, AuthWrapperComponent, ReplaceableTemplateDirective], selector: 'abp-layout-account', providers: [LayoutService, SubscriptionService], template: " <abp-auth-wrapper\n *abpReplaceableTemplate=\"{\n componentKey: authWrapperKey\n }\"\n >\n <router-outlet #outlet=\"outlet\"></router-outlet>\n </abp-auth-wrapper>\n" }]
1609
+ args: [{ standalone: true, imports: [RoutesComponent, NavItemsComponent, RouterOutlet, AuthWrapperComponent, ReplaceableTemplateDirective], selector: 'abp-layout-account', providers: [LayoutService, SubscriptionService], template: " <abp-auth-wrapper\r\n *abpReplaceableTemplate=\"{\r\n componentKey: authWrapperKey\r\n }\"\r\n >\r\n <router-outlet #outlet=\"outlet\"></router-outlet>\r\n </abp-auth-wrapper>\r\n" }]
1474
1610
  }], ctorParameters: () => [{ type: LayoutService }] });
1475
1611
 
1476
1612
  class ApplicationLayoutComponent {
@@ -1646,11 +1782,11 @@ class ApplicationLayoutComponent {
1646
1782
  }
1647
1783
  }
1648
1784
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ApplicationLayoutComponent, deps: [{ token: LayoutService }, { token: i0.Renderer2 }, { token: i2$2.Router }, { token: i0.NgZone }, { token: i3$3.LocalizationService }], target: i0.ɵɵFactoryTarget.Component });
1649
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: ApplicationLayoutComponent, isStandalone: true, selector: "abp-layout-application", providers: [LayoutService, SubscriptionService], ngImport: i0, template: "\n\n<div class=\"flex h-screen overflow-hidden\">\n\n <!-- Sidebar stays fixed -->\n\n <app-routes\n *abpReplaceableTemplate=\"{\n componentKey: service.routesComponentKey,\n inputs: { smallScreen: { value: service.smallScreen } }\n }\"\n class=\"mb-2 md:mb-0\"\n ></app-routes>\n\n <!-- Main content scrolls -->\n <main class=\"flex-1 flex flex-col overflow-hidden\">\n\n <!-- Optional Topbar -->\n <!-- @if (showTopbar) { -->\n\n <app-nav-items\n *abpReplaceableTemplate=\"{\n componentKey: service.navItemsComponentKey\n }\"\n ></app-nav-items>\n\n <!-- p-6 space-y-6 -->\n <div class=\"flex-1 overflow-y-auto\">\n <router-outlet #outlet=\"outlet\"></router-outlet>\n </div>\n\n <!-- <app-footer></app-footer> -->\n\n </main>\n\n </div>\n\n", dependencies: [{ kind: "component", type: RoutesComponent, selector: "app-routes" }, { kind: "component", type: NavItemsComponent, selector: "app-nav-items" }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: ReplaceableTemplateDirective, selector: "[abpReplaceableTemplate]", inputs: ["abpReplaceableTemplate"] }], animations: [ /* slideFromBottom, */ /* appModuleAnimation() */] });
1785
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: ApplicationLayoutComponent, isStandalone: true, selector: "abp-layout-application", providers: [LayoutService, SubscriptionService], ngImport: i0, template: "\r\n\r\n<div class=\"flex h-screen overflow-hidden\">\r\n\r\n <!-- Sidebar stays fixed -->\r\n\r\n <app-routes\r\n *abpReplaceableTemplate=\"{\r\n componentKey: service.routesComponentKey,\r\n inputs: { smallScreen: { value: service.smallScreen } }\r\n }\"\r\n class=\"mb-2 md:mb-0\"\r\n ></app-routes>\r\n\r\n <!-- Main content scrolls -->\r\n <main class=\"flex-1 flex flex-col overflow-hidden\">\r\n\r\n <!-- Optional Topbar -->\r\n <!-- @if (showTopbar) { -->\r\n\r\n <app-nav-items\r\n *abpReplaceableTemplate=\"{\r\n componentKey: service.navItemsComponentKey\r\n }\"\r\n ></app-nav-items>\r\n\r\n <!-- p-6 space-y-6 -->\r\n <div class=\"flex-1 overflow-y-auto\">\r\n <router-outlet #outlet=\"outlet\"></router-outlet>\r\n </div>\r\n\r\n <!-- <app-footer></app-footer> -->\r\n\r\n </main>\r\n\r\n </div>\r\n\r\n", dependencies: [{ kind: "component", type: RoutesComponent, selector: "app-routes" }, { kind: "component", type: NavItemsComponent, selector: "app-nav-items" }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: ReplaceableTemplateDirective, selector: "[abpReplaceableTemplate]", inputs: ["abpReplaceableTemplate"] }], animations: [ /* slideFromBottom, */ /* appModuleAnimation() */] });
1650
1786
  }
1651
1787
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ApplicationLayoutComponent, decorators: [{
1652
1788
  type: Component,
1653
- args: [{ standalone: true, imports: [RoutesComponent, NavItemsComponent, RouterOutlet, ReplaceableTemplateDirective], selector: 'abp-layout-application', animations: [ /* slideFromBottom, */ /* appModuleAnimation() */], providers: [LayoutService, SubscriptionService], template: "\n\n<div class=\"flex h-screen overflow-hidden\">\n\n <!-- Sidebar stays fixed -->\n\n <app-routes\n *abpReplaceableTemplate=\"{\n componentKey: service.routesComponentKey,\n inputs: { smallScreen: { value: service.smallScreen } }\n }\"\n class=\"mb-2 md:mb-0\"\n ></app-routes>\n\n <!-- Main content scrolls -->\n <main class=\"flex-1 flex flex-col overflow-hidden\">\n\n <!-- Optional Topbar -->\n <!-- @if (showTopbar) { -->\n\n <app-nav-items\n *abpReplaceableTemplate=\"{\n componentKey: service.navItemsComponentKey\n }\"\n ></app-nav-items>\n\n <!-- p-6 space-y-6 -->\n <div class=\"flex-1 overflow-y-auto\">\n <router-outlet #outlet=\"outlet\"></router-outlet>\n </div>\n\n <!-- <app-footer></app-footer> -->\n\n </main>\n\n </div>\n\n" }]
1789
+ args: [{ standalone: true, imports: [RoutesComponent, NavItemsComponent, RouterOutlet, ReplaceableTemplateDirective], selector: 'abp-layout-application', animations: [ /* slideFromBottom, */ /* appModuleAnimation() */], providers: [LayoutService, SubscriptionService], template: "\r\n\r\n<div class=\"flex h-screen overflow-hidden\">\r\n\r\n <!-- Sidebar stays fixed -->\r\n\r\n <app-routes\r\n *abpReplaceableTemplate=\"{\r\n componentKey: service.routesComponentKey,\r\n inputs: { smallScreen: { value: service.smallScreen } }\r\n }\"\r\n class=\"mb-2 md:mb-0\"\r\n ></app-routes>\r\n\r\n <!-- Main content scrolls -->\r\n <main class=\"flex-1 flex flex-col overflow-hidden\">\r\n\r\n <!-- Optional Topbar -->\r\n <!-- @if (showTopbar) { -->\r\n\r\n <app-nav-items\r\n *abpReplaceableTemplate=\"{\r\n componentKey: service.navItemsComponentKey\r\n }\"\r\n ></app-nav-items>\r\n\r\n <!-- p-6 space-y-6 -->\r\n <div class=\"flex-1 overflow-y-auto\">\r\n <router-outlet #outlet=\"outlet\"></router-outlet>\r\n </div>\r\n\r\n <!-- <app-footer></app-footer> -->\r\n\r\n </main>\r\n\r\n </div>\r\n\r\n" }]
1654
1790
  }], ctorParameters: () => [{ type: LayoutService }, { type: i0.Renderer2 }, { type: i2$2.Router }, { type: i0.NgZone }, { type: i3$3.LocalizationService }] });
1655
1791
 
1656
1792
  class EmptyLayoutComponent {