@es.framework/ng.ui.theme 2.0.56

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.
@@ -0,0 +1,1850 @@
1
+ import * as i0 from '@angular/core';
2
+ import { signal, computed, effect, Injectable, inject, Component, PLATFORM_ID, HostListener, provideAppInitializer, Injector, makeEnvironmentProviders, InjectionToken } from '@angular/core';
3
+ import { Subject, Observable, lastValueFrom, BehaviorSubject, map, filter } from 'rxjs';
4
+ import * as i3$3 from '@es.framework/ng.core/services';
5
+ import { ConfigStateService, SessionStateService, LocalizationService, LocalStorageService, NavItemsService, UserMenuService, RoutesService, SubscriptionService, ReplaceableComponentsService } from '@es.framework/ng.core/services';
6
+ import { eLayoutType } from '@es.framework/ng.core/models';
7
+ import * as i2$2 from '@angular/router';
8
+ import { Router, RouterModule, RouterOutlet, NavigationEnd } from '@angular/router';
9
+ import * as i1$1 from '@angular/common';
10
+ import { isPlatformBrowser, CommonModule } from '@angular/common';
11
+ import { StyleClassModule } from 'primeng/styleclass';
12
+ import * as i3$1 from 'primeng/avatar';
13
+ import { AvatarModule } from 'primeng/avatar';
14
+ import * as i5 from 'primeng/menu';
15
+ import { MenuModule } from 'primeng/menu';
16
+ import * as i2 from 'primeng/button';
17
+ import { ButtonModule } from 'primeng/button';
18
+ import { OverlayModule } from 'primeng/overlay';
19
+ import { VisibleDirective, HasPermissionDirective, ReplaceableTemplateDirective } from '@es.framework/ng.core/directives';
20
+ import { TranslatePipe, ToInjectorPipe } from '@es.framework/ng.core/pipes';
21
+ import { toSignal } from '@angular/core/rxjs-interop';
22
+ import * as i2$1 from '@angular/forms';
23
+ import { FormsModule } from '@angular/forms';
24
+ import { SelectModule } from 'primeng/select';
25
+ import * as i1 from 'primeng/popover';
26
+ import { PopoverModule } from 'primeng/popover';
27
+ import { TranslateService } from '@ngx-translate/core';
28
+ import { updatePreset, updateSurfacePalette } from '@primeng/themes';
29
+ import Aura from '@primeng/themes/aura';
30
+ import Lara from '@primeng/themes/lara';
31
+ import Nora from '@primeng/themes/nora';
32
+ import { PrimeNG } from 'primeng/config';
33
+ import * as i3 from 'primeng/selectbutton';
34
+ import { SelectButtonModule } from 'primeng/selectbutton';
35
+ import { AuthService } from '@es.framework/ng.core/abstracts';
36
+ import * as i4 from 'primeng/api';
37
+ import * as i3$2 from 'primeng/tooltip';
38
+ import { TooltipModule } from 'primeng/tooltip';
39
+ import Swal from 'sweetalert2';
40
+ import { NAVIGATE_TO_MANAGE_PROFILE } from '@es.framework/ng.core/tokens';
41
+
42
+ class LayoutService {
43
+ smallScreen = false;
44
+ logoComponentKey = "Theme.LogoComponent" /* eThemeBasicComponents.Logo */;
45
+ routesComponentKey = "Theme.RoutesComponent" /* eThemeBasicComponents.Routes */;
46
+ navItemsComponentKey = "Theme.NavItemsComponent" /* eThemeBasicComponents.NavItems */;
47
+ _config = {
48
+ preset: 'Aura',
49
+ primary: 'blue',
50
+ surface: null,
51
+ darkTheme: false,
52
+ menuMode: 'static'
53
+ };
54
+ _state = {
55
+ staticMenuDesktopInactive: false,
56
+ overlayMenuActive: false,
57
+ configSidebarVisible: false,
58
+ staticMenuMobileActive: false,
59
+ menuHoverActive: false
60
+ };
61
+ layoutConfig = signal(this._config, ...(ngDevMode ? [{ debugName: "layoutConfig" }] : []));
62
+ layoutState = signal(this._state, ...(ngDevMode ? [{ debugName: "layoutState" }] : []));
63
+ configUpdate = new Subject();
64
+ overlayOpen = new Subject();
65
+ menuSource = new Subject();
66
+ resetSource = new Subject();
67
+ menuSource$ = this.menuSource.asObservable();
68
+ resetSource$ = this.resetSource.asObservable();
69
+ configUpdate$ = this.configUpdate.asObservable();
70
+ overlayOpen$ = this.overlayOpen.asObservable();
71
+ theme = computed(() => (this.layoutConfig()?.darkTheme ? 'light' : 'dark'), ...(ngDevMode ? [{ debugName: "theme" }] : []));
72
+ isSidebarActive = computed(() => this.layoutState().overlayMenuActive || this.layoutState().staticMenuMobileActive, ...(ngDevMode ? [{ debugName: "isSidebarActive" }] : []));
73
+ isDarkTheme = computed(() => this.layoutConfig().darkTheme, ...(ngDevMode ? [{ debugName: "isDarkTheme" }] : []));
74
+ getPrimary = computed(() => this.layoutConfig().primary, ...(ngDevMode ? [{ debugName: "getPrimary" }] : []));
75
+ getSurface = computed(() => this.layoutConfig().surface, ...(ngDevMode ? [{ debugName: "getSurface" }] : []));
76
+ isOverlay = computed(() => this.layoutConfig().menuMode === 'overlay', ...(ngDevMode ? [{ debugName: "isOverlay" }] : []));
77
+ transitionComplete = signal(false, ...(ngDevMode ? [{ debugName: "transitionComplete" }] : []));
78
+ initialized = false;
79
+ constructor() {
80
+ effect(() => {
81
+ const config = this.layoutConfig();
82
+ if (config) {
83
+ this.onConfigUpdate();
84
+ }
85
+ });
86
+ effect(() => {
87
+ const config = this.layoutConfig();
88
+ if (!this.initialized || !config) {
89
+ this.initialized = true;
90
+ return;
91
+ }
92
+ this.handleDarkModeTransition(config);
93
+ });
94
+ }
95
+ handleDarkModeTransition(config) {
96
+ if (document.startViewTransition) {
97
+ this.startViewTransition(config);
98
+ }
99
+ else {
100
+ this.toggleDarkMode(config);
101
+ this.onTransitionEnd();
102
+ }
103
+ }
104
+ startViewTransition(config) {
105
+ const transition = document.startViewTransition(() => {
106
+ this.toggleDarkMode(config);
107
+ });
108
+ transition.ready
109
+ .then(() => {
110
+ this.onTransitionEnd();
111
+ })
112
+ .catch(() => { });
113
+ }
114
+ toggleDarkMode(config) {
115
+ const _config = config || this.layoutConfig();
116
+ if (_config.darkTheme) {
117
+ document.documentElement.classList.add('app-dark');
118
+ }
119
+ else {
120
+ document.documentElement.classList.remove('app-dark');
121
+ }
122
+ }
123
+ onTransitionEnd() {
124
+ this.transitionComplete.set(true);
125
+ setTimeout(() => {
126
+ this.transitionComplete.set(false);
127
+ });
128
+ }
129
+ onMenuToggle() {
130
+ if (this.isOverlay()) {
131
+ this.layoutState.update((prev) => ({ ...prev, overlayMenuActive: !this.layoutState().overlayMenuActive }));
132
+ if (this.layoutState().overlayMenuActive) {
133
+ this.overlayOpen.next(null);
134
+ }
135
+ }
136
+ if (this.isDesktop()) {
137
+ this.layoutState.update((prev) => ({ ...prev, staticMenuDesktopInactive: !this.layoutState().staticMenuDesktopInactive }));
138
+ }
139
+ else {
140
+ this.layoutState.update((prev) => ({ ...prev, staticMenuMobileActive: !this.layoutState().staticMenuMobileActive }));
141
+ if (this.layoutState().staticMenuMobileActive) {
142
+ this.overlayOpen.next(null);
143
+ }
144
+ }
145
+ }
146
+ isDesktop() {
147
+ return window.innerWidth > 991;
148
+ }
149
+ isMobile() {
150
+ return !this.isDesktop();
151
+ }
152
+ onConfigUpdate() {
153
+ this._config = { ...this.layoutConfig() };
154
+ this.configUpdate.next(this.layoutConfig());
155
+ }
156
+ onMenuStateChange(event) {
157
+ this.menuSource.next(event);
158
+ }
159
+ reset() {
160
+ this.resetSource.next(true);
161
+ }
162
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LayoutService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
163
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LayoutService, providedIn: 'root' });
164
+ }
165
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LayoutService, decorators: [{
166
+ type: Injectable,
167
+ args: [{
168
+ providedIn: 'root'
169
+ }]
170
+ }], ctorParameters: () => [] });
171
+
172
+ class LanguagesComponent {
173
+ configState = inject(ConfigStateService);
174
+ sessionState = inject(SessionStateService);
175
+ translate = inject(LocalizationService);
176
+ localStorageService = inject(LocalStorageService);
177
+ in18Translate = inject(TranslateService);
178
+ // Convert observable to signal
179
+ languages$ = this.configState.getDeep$('localization.languages');
180
+ languages = toSignal(this.languages$, { initialValue: [] });
181
+ // selectedLangValue = this.sessionState.getLanguage();
182
+ selectedLang = signal(this.sessionState.getLanguage(), ...(ngDevMode ? [{ debugName: "selectedLang" }] : []));
183
+ constructor() {
184
+ // Update signal when language is changed from external source (optional)
185
+ effect(() => {
186
+ this.selectedLang.set(this.sessionState.getLanguage());
187
+ });
188
+ }
189
+ onSelectLanguage(cultureName) {
190
+ try {
191
+ this.in18Translate.use(cultureName).subscribe(() => {
192
+ });
193
+ this.localStorageService.setItem('language', cultureName);
194
+ this.sessionState.setLanguage(cultureName);
195
+ this.selectedLang.set(cultureName);
196
+ }
197
+ catch (error) {
198
+ }
199
+ this.updateHtmlDirection(cultureName);
200
+ }
201
+ updateHtmlDirection(languageCode) {
202
+ const htmlElement = document.documentElement;
203
+ if (languageCode === 'ar' || languageCode.includes('ar-')) {
204
+ htmlElement.setAttribute('dir', 'rtl');
205
+ htmlElement.classList.add('rtl');
206
+ }
207
+ else {
208
+ htmlElement.setAttribute('dir', 'ltr');
209
+ htmlElement.classList.remove('rtl');
210
+ }
211
+ }
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
+
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
+ }
245
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LanguagesComponent, decorators: [{
246
+ type: Component,
247
+ args: [{
248
+ selector: 'abp-languages',
249
+ standalone: true,
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
+
281
+ `
282
+ }]
283
+ }], ctorParameters: () => [] });
284
+
285
+ const presets = {
286
+ Aura,
287
+ Lara,
288
+ Nora
289
+ };
290
+ class AppConfigurator {
291
+ router = inject(Router);
292
+ config = inject(PrimeNG);
293
+ layoutService = inject(LayoutService);
294
+ platformId = inject(PLATFORM_ID);
295
+ primeng = inject(PrimeNG);
296
+ presets = Object.keys(presets);
297
+ showMenuModeButton = signal(!this.router.url.includes('auth'), ...(ngDevMode ? [{ debugName: "showMenuModeButton" }] : []));
298
+ menuModeOptions = [
299
+ { label: 'Static', value: 'static' },
300
+ { label: 'Overlay', value: 'overlay' }
301
+ ];
302
+ ngOnInit() {
303
+ if (isPlatformBrowser(this.platformId)) {
304
+ const preset = this.layoutService.layoutConfig().preset;
305
+ this.onPresetChange(preset ?? "Aura");
306
+ }
307
+ }
308
+ surfaces = [
309
+ {
310
+ name: 'slate',
311
+ palette: {
312
+ 0: '#ffffff',
313
+ 50: '#f8fafc',
314
+ 100: '#f1f5f9',
315
+ 200: '#e2e8f0',
316
+ 300: '#cbd5e1',
317
+ 400: '#94a3b8',
318
+ 500: '#64748b',
319
+ 600: '#475569',
320
+ 700: '#334155',
321
+ 800: '#1e293b',
322
+ 900: '#0f172a',
323
+ 950: '#020617'
324
+ }
325
+ },
326
+ {
327
+ name: 'gray',
328
+ palette: {
329
+ 0: '#ffffff',
330
+ 50: '#f9fafb',
331
+ 100: '#f3f4f6',
332
+ 200: '#e5e7eb',
333
+ 300: '#d1d5db',
334
+ 400: '#9ca3af',
335
+ 500: '#6b7280',
336
+ 600: '#4b5563',
337
+ 700: '#374151',
338
+ 800: '#1f2937',
339
+ 900: '#111827',
340
+ 950: '#030712'
341
+ }
342
+ },
343
+ {
344
+ name: 'zinc',
345
+ palette: {
346
+ 0: '#ffffff',
347
+ 50: '#fafafa',
348
+ 100: '#f4f4f5',
349
+ 200: '#e4e4e7',
350
+ 300: '#d4d4d8',
351
+ 400: '#a1a1aa',
352
+ 500: '#71717a',
353
+ 600: '#52525b',
354
+ 700: '#3f3f46',
355
+ 800: '#27272a',
356
+ 900: '#18181b',
357
+ 950: '#09090b'
358
+ }
359
+ },
360
+ {
361
+ name: 'neutral',
362
+ palette: {
363
+ 0: '#ffffff',
364
+ 50: '#fafafa',
365
+ 100: '#f5f5f5',
366
+ 200: '#e5e5e5',
367
+ 300: '#d4d4d4',
368
+ 400: '#a3a3a3',
369
+ 500: '#737373',
370
+ 600: '#525252',
371
+ 700: '#404040',
372
+ 800: '#262626',
373
+ 900: '#171717',
374
+ 950: '#0a0a0a'
375
+ }
376
+ },
377
+ {
378
+ name: 'stone',
379
+ palette: {
380
+ 0: '#ffffff',
381
+ 50: '#fafaf9',
382
+ 100: '#f5f5f4',
383
+ 200: '#e7e5e4',
384
+ 300: '#d6d3d1',
385
+ 400: '#a8a29e',
386
+ 500: '#78716c',
387
+ 600: '#57534e',
388
+ 700: '#44403c',
389
+ 800: '#292524',
390
+ 900: '#1c1917',
391
+ 950: '#0c0a09'
392
+ }
393
+ },
394
+ {
395
+ name: 'soho',
396
+ palette: {
397
+ 0: '#ffffff',
398
+ 50: '#ececec',
399
+ 100: '#dedfdf',
400
+ 200: '#c4c4c6',
401
+ 300: '#adaeb0',
402
+ 400: '#97979b',
403
+ 500: '#7f8084',
404
+ 600: '#6a6b70',
405
+ 700: '#55565b',
406
+ 800: '#3f4046',
407
+ 900: '#2c2c34',
408
+ 950: '#16161d'
409
+ }
410
+ },
411
+ {
412
+ name: 'viva',
413
+ palette: {
414
+ 0: '#ffffff',
415
+ 50: '#f3f3f3',
416
+ 100: '#e7e7e8',
417
+ 200: '#cfd0d0',
418
+ 300: '#b7b8b9',
419
+ 400: '#9fa1a1',
420
+ 500: '#87898a',
421
+ 600: '#6e7173',
422
+ 700: '#565a5b',
423
+ 800: '#3e4244',
424
+ 900: '#262b2c',
425
+ 950: '#0e1315'
426
+ }
427
+ },
428
+ {
429
+ name: 'ocean',
430
+ palette: {
431
+ 0: '#ffffff',
432
+ 50: '#fbfcfc',
433
+ 100: '#F7F9F8',
434
+ 200: '#EFF3F2',
435
+ 300: '#DADEDD',
436
+ 400: '#B1B7B6',
437
+ 500: '#828787',
438
+ 600: '#5F7274',
439
+ 700: '#415B61',
440
+ 800: '#29444E',
441
+ 900: '#183240',
442
+ 950: '#0c1920'
443
+ }
444
+ }
445
+ ];
446
+ selectedPrimaryColor = computed(() => {
447
+ return this.layoutService.layoutConfig().primary;
448
+ }, ...(ngDevMode ? [{ debugName: "selectedPrimaryColor" }] : []));
449
+ selectedSurfaceColor = computed(() => this.layoutService.layoutConfig().surface, ...(ngDevMode ? [{ debugName: "selectedSurfaceColor" }] : []));
450
+ selectedPreset = computed(() => this.layoutService.layoutConfig().preset, ...(ngDevMode ? [{ debugName: "selectedPreset" }] : []));
451
+ menuMode = computed(() => this.layoutService.layoutConfig().menuMode, ...(ngDevMode ? [{ debugName: "menuMode" }] : []));
452
+ primaryColors = computed(() => {
453
+ const presetPalette = presets[this.layoutService.layoutConfig().preset].primitive;
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: {} }];
456
+ colors.forEach((color) => {
457
+ palettes.push({
458
+ name: color,
459
+ palette: presetPalette?.[color]
460
+ });
461
+ });
462
+ return palettes;
463
+ }, ...(ngDevMode ? [{ debugName: "primaryColors" }] : []));
464
+ getPresetExt() {
465
+ const color = this.primaryColors().find((c) => c.name === this.selectedPrimaryColor()) || {};
466
+ const preset = this.layoutService.layoutConfig().preset;
467
+ if (color.name === 'noir') {
468
+ return {
469
+ semantic: {
470
+ primary: {
471
+ 50: '{surface.50}',
472
+ 100: '{surface.100}',
473
+ 200: '{surface.200}',
474
+ 300: '{surface.300}',
475
+ 400: '{surface.400}',
476
+ 500: '{surface.500}',
477
+ 600: '{surface.600}',
478
+ 700: '{surface.700}',
479
+ 800: '{surface.800}',
480
+ 900: '{surface.900}',
481
+ 950: '{surface.950}'
482
+ },
483
+ colorScheme: {
484
+ light: {
485
+ primary: {
486
+ color: '{primary.950}',
487
+ contrastColor: '#ffffff',
488
+ hoverColor: '{primary.800}',
489
+ activeColor: '{primary.700}'
490
+ },
491
+ highlight: {
492
+ background: '{primary.950}',
493
+ focusBackground: '{primary.700}',
494
+ color: '#ffffff',
495
+ focusColor: '#ffffff'
496
+ }
497
+ },
498
+ dark: {
499
+ primary: {
500
+ color: '{primary.50}',
501
+ contrastColor: '{primary.950}',
502
+ hoverColor: '{primary.200}',
503
+ activeColor: '{primary.300}'
504
+ },
505
+ highlight: {
506
+ background: '{primary.50}',
507
+ focusBackground: '{primary.300}',
508
+ color: '{primary.950}',
509
+ focusColor: '{primary.950}'
510
+ }
511
+ }
512
+ }
513
+ }
514
+ };
515
+ }
516
+ else {
517
+ if (preset === 'Nora') {
518
+ return {
519
+ semantic: {
520
+ primary: color.palette,
521
+ colorScheme: {
522
+ light: {
523
+ primary: {
524
+ color: '{primary.600}',
525
+ contrastColor: '#ffffff',
526
+ hoverColor: '{primary.700}',
527
+ activeColor: '{primary.800}'
528
+ },
529
+ highlight: {
530
+ background: '{primary.600}',
531
+ focusBackground: '{primary.700}',
532
+ color: '#ffffff',
533
+ focusColor: '#ffffff'
534
+ }
535
+ },
536
+ dark: {
537
+ primary: {
538
+ color: '{primary.500}',
539
+ contrastColor: '{surface.900}',
540
+ hoverColor: '{primary.400}',
541
+ activeColor: '{primary.300}'
542
+ },
543
+ highlight: {
544
+ background: '{primary.500}',
545
+ focusBackground: '{primary.400}',
546
+ color: '{surface.900}',
547
+ focusColor: '{surface.900}'
548
+ }
549
+ }
550
+ }
551
+ }
552
+ };
553
+ }
554
+ else {
555
+ return {
556
+ semantic: {
557
+ primary: color.palette,
558
+ colorScheme: {
559
+ light: {
560
+ primary: {
561
+ color: '{primary.500}',
562
+ contrastColor: '#ffffff',
563
+ hoverColor: '{primary.600}',
564
+ activeColor: '{primary.700}'
565
+ },
566
+ highlight: {
567
+ background: '{primary.50}',
568
+ focusBackground: '{primary.100}',
569
+ color: '{primary.700}',
570
+ focusColor: '{primary.800}'
571
+ }
572
+ },
573
+ dark: {
574
+ primary: {
575
+ color: '{primary.400}',
576
+ contrastColor: '{surface.900}',
577
+ hoverColor: '{primary.300}',
578
+ activeColor: '{primary.200}'
579
+ },
580
+ highlight: {
581
+ background: 'color-mix(in srgb, {primary.400}, transparent 84%)',
582
+ focusBackground: 'color-mix(in srgb, {primary.400}, transparent 76%)',
583
+ color: 'rgba(255,255,255,.87)',
584
+ focusColor: 'rgba(255,255,255,.87)'
585
+ }
586
+ }
587
+ }
588
+ }
589
+ };
590
+ }
591
+ }
592
+ }
593
+ updateColors(event, type, color) {
594
+ if (type === 'primary') {
595
+ this.layoutService.layoutConfig.update(state => ({ ...state, primary: color.name }));
596
+ this.applyPalette('primary', color.palette || {});
597
+ }
598
+ else if (type === 'surface') {
599
+ this.layoutService.layoutConfig.update(state => ({ ...state, surface: color.name }));
600
+ this.applyPalette('surface', color.palette || {});
601
+ }
602
+ event.stopPropagation();
603
+ }
604
+ applyTheme(type, color) {
605
+ if (type === 'primary') {
606
+ updatePreset(this.getPresetExt());
607
+ }
608
+ else if (type === 'surface') {
609
+ updateSurfacePalette(color.palette);
610
+ }
611
+ }
612
+ applyPalette(type, palette = {}) {
613
+ if (!isPlatformBrowser(this.platformId))
614
+ return; // SSR-safe
615
+ const root = document.documentElement;
616
+ Object.entries(palette).forEach(([key, value]) => {
617
+ if (type === 'primary') {
618
+ root.style.setProperty(`--p-primary-${key}`, value);
619
+ }
620
+ else if (type === 'surface') {
621
+ root.style.setProperty(`--surface-${key}`, value);
622
+ }
623
+ });
624
+ }
625
+ onPresetChange(presetName) {
626
+ this.layoutService.layoutConfig.update(state => ({ ...state, preset: presetName }));
627
+ const preset = presets[presetName];
628
+ // اختر اللون الحالي
629
+ const surfacePalette = this.surfaces.find(s => s.name === this.selectedSurfaceColor())?.palette;
630
+ // تطبيق primary + surface
631
+ const primaryPalette = this.primaryColors().find(c => c.name === this.selectedPrimaryColor())?.palette;
632
+ this.applyPalette('primary', primaryPalette || {});
633
+ this.applyPalette('surface', surfacePalette || {});
634
+ }
635
+ onMenuModeChange(event) {
636
+ this.layoutService.layoutConfig.update((prev) => ({ ...prev, menuMode: event }));
637
+ }
638
+ 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>
686
+ `, 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
+ }
688
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AppConfigurator, decorators: [{
689
+ type: Component,
690
+ args: [{
691
+ selector: 'app-configurator',
692
+ standalone: true,
693
+ 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>
741
+ `,
742
+ }]
743
+ }] });
744
+
745
+ class NavItemsComponent {
746
+ translate = inject(LocalizationService);
747
+ authService = inject(AuthService);
748
+ configState = inject(ConfigStateService);
749
+ sessionState = inject(SessionStateService);
750
+ navItems = inject(NavItemsService);
751
+ userMenu = inject(UserMenuService);
752
+ layoutService = inject(LayoutService);
753
+ loading = false;
754
+ currentUser$;
755
+ selectedTenant$;
756
+ userMenuItems = [];
757
+ constructor() {
758
+ this.currentUser$ = this.configState.getOne$('currentUser');
759
+ this.selectedTenant$ = this.sessionState.getTenant$();
760
+ }
761
+ ngOnInit() {
762
+ // Get user menu items from the service and convert to PrimeNG format
763
+ this.userMenu.items$.subscribe(async (items) => {
764
+ this.userMenuItems = await this.convertToPrimeMenuItems(items);
765
+ });
766
+ }
767
+ async convertToPrimeMenuItems(userMenus) {
768
+ const primeItems = [];
769
+ for (const userMenu of userMenus) {
770
+ // Check visibility if defined
771
+ let isVisible = true;
772
+ if (userMenu.visible) {
773
+ const visibilityResult = userMenu.visible(userMenu);
774
+ if (visibilityResult instanceof Promise) {
775
+ isVisible = await visibilityResult;
776
+ }
777
+ else if (visibilityResult instanceof Observable) {
778
+ isVisible = await lastValueFrom(visibilityResult);
779
+ }
780
+ else {
781
+ isVisible = !!visibilityResult;
782
+ }
783
+ }
784
+ if (!isVisible)
785
+ continue;
786
+ // Create PrimeNG menu item
787
+ const primeItem = {
788
+ id: userMenu.id?.toString(),
789
+ label: userMenu.textTemplate?.text || '',
790
+ icon: userMenu.textTemplate?.icon || '',
791
+ // routerLink: userMenu.component ? undefined : ['#'],
792
+ routerLink: null,
793
+ command: userMenu.action ? (event) => {
794
+ if (userMenu.action) {
795
+ userMenu.action();
796
+ // if (this.menu) {
797
+ // this.menu.hide();
798
+ // }
799
+ }
800
+ } : undefined,
801
+ visible: isVisible,
802
+ data: {
803
+ $implicit: userMenu // Pass the original userMenu as data
804
+ }
805
+ };
806
+ // If it has a component, set it up
807
+ if (userMenu.component) {
808
+ // primeItem.data = {
809
+ // ...primeItem.data,
810
+ // component: userMenu.component
811
+ // };
812
+ }
813
+ // Handle badge
814
+ if (userMenu.badge) {
815
+ primeItem.badge = userMenu.badge.count?.toString() || '';
816
+ primeItem.badgeStyleClass = `p-badge p-badge-${userMenu.badge.color || 'danger'}`;
817
+ }
818
+ primeItems.push(primeItem);
819
+ }
820
+ return primeItems;
821
+ }
822
+ toggleDarkMode() {
823
+ this.layoutService.layoutConfig.update((state) => ({
824
+ ...state,
825
+ darkTheme: !state.darkTheme
826
+ }));
827
+ }
828
+ get userName() {
829
+ // This will be updated when currentUser$ emits
830
+ let name = '?';
831
+ this.currentUser$.subscribe(user => {
832
+ if (user?.name) {
833
+ const names = user.name.split(' ');
834
+ if (names.length >= 2) {
835
+ name = `${names[0][0]}`;
836
+ }
837
+ else if (names[0]) {
838
+ name = names[0][0];
839
+ }
840
+ }
841
+ });
842
+ return name;
843
+ }
844
+ get userStatus() {
845
+ let status = this.translate.instant('UNKNOWN');
846
+ this.currentUser$.subscribe(user => {
847
+ if (!user)
848
+ return;
849
+ this.translate.instant('ACTIVE');
850
+ });
851
+ return status;
852
+ }
853
+ onProfileSettings() {
854
+ // Navigate to profile settings
855
+ console.log('Profile settings clicked');
856
+ }
857
+ onChangePassword() {
858
+ // Navigate to change password
859
+ console.log('Change password clicked');
860
+ }
861
+ logout() {
862
+ this.authService.logout().subscribe();
863
+ }
864
+ get smallScreen() {
865
+ return window.innerWidth < 992;
866
+ }
867
+ navigateToLogin() {
868
+ this.authService.navigateToLogin();
869
+ }
870
+ 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>
1032
+ `, 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
+ }
1034
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: NavItemsComponent, decorators: [{
1035
+ type: Component,
1036
+ args: [{
1037
+ selector: 'app-nav-items',
1038
+ standalone: true,
1039
+ imports: [
1040
+ CommonModule,
1041
+ RouterModule,
1042
+ StyleClassModule,
1043
+ AppConfigurator,
1044
+ LanguagesComponent,
1045
+ AvatarModule,
1046
+ MenuModule,
1047
+ ButtonModule,
1048
+ OverlayModule,
1049
+ TranslatePipe,
1050
+ VisibleDirective,
1051
+ HasPermissionDirective,
1052
+ ToInjectorPipe,
1053
+ PopoverModule
1054
+ ],
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>
1216
+ `
1217
+ }]
1218
+ }], ctorParameters: () => [] });
1219
+
1220
+ class RoutesComponent {
1221
+ subSidebarVisible = false;
1222
+ sidebarVisible = true;
1223
+ isMobile = false;
1224
+ router = inject(Router);
1225
+ localization = inject(LocalizationService);
1226
+ layoutService = inject(LayoutService);
1227
+ routesService = inject(RoutesService);
1228
+ configState = inject(ConfigStateService);
1229
+ secondToLastLevel$;
1230
+ _menuItemsSubject = new BehaviorSubject([]);
1231
+ // menuItems$!: Observable<MenuItem[]>;
1232
+ 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) {
1256
+ 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
+ }
1270
+ }
1271
+ };
1272
+ traverse(items);
1273
+ return result;
1274
+ }
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;
1298
+ }
1299
+ else {
1300
+ this.sidebarVisible = true;
1301
+ this.subSidebarVisible = true;
1302
+ }
1303
+ }
1304
+ toggleAll() {
1305
+ if (this.isMobile) {
1306
+ this.sidebarVisible = !this.sidebarVisible;
1307
+ this.subSidebarVisible = this.sidebarVisible;
1308
+ }
1309
+ else {
1310
+ this.subSidebarVisible = !this.subSidebarVisible;
1311
+ }
1312
+ }
1313
+ openMenu() {
1314
+ this.sidebarVisible = true;
1315
+ this.subSidebarVisible = true;
1316
+ }
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
+ }
1328
+ }
1329
+ return false;
1330
+ }
1331
+ onMainClick(target) {
1332
+ const items = this._menuItemsSubject.value;
1333
+ const isExternal = target.isExternal ?? false;
1334
+ if (!target.items?.length) {
1335
+ if (isExternal && target.url) {
1336
+ window.open(target.url, '_blank', 'noopener,noreferrer');
1337
+ if (this.isMobile)
1338
+ this.closeMenu();
1339
+ this.expandPath(items, target);
1340
+ this._menuItemsSubject.next([...items]);
1341
+ return;
1342
+ }
1343
+ if (target.routerLink) {
1344
+ const currentUrl = this.router.url;
1345
+ const targetUrl = Array.isArray(target.routerLink)
1346
+ ? target.routerLink[0]
1347
+ : target.routerLink;
1348
+ if (currentUrl !== targetUrl) {
1349
+ this.router.navigate(Array.isArray(target.routerLink) ? target.routerLink : [target.routerLink]);
1350
+ }
1351
+ if (this.isMobile)
1352
+ this.closeMenu();
1353
+ this.expandPath(items, target);
1354
+ this._menuItemsSubject.next([...items]);
1355
+ return;
1356
+ }
1357
+ }
1358
+ target.expanded = !target.expanded;
1359
+ this.subSidebarVisible = target.expanded;
1360
+ this._menuItemsSubject.next([...items]);
1361
+ }
1362
+ toggleExpand(item) {
1363
+ item.expanded = !item.expanded;
1364
+ }
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() {
1377
+ if (this.isMobile) {
1378
+ this.closeMenu();
1379
+ }
1380
+ }
1381
+ buildItems(routes) {
1382
+ return routes
1383
+ .filter(r => !r.requiredPolicy || this.hasPermission(r.requiredPolicy))
1384
+ .map(r => this.createItem(r));
1385
+ }
1386
+ createItem(route) {
1387
+ const item = {
1388
+ label: route.name,
1389
+ icon: route.iconClass,
1390
+ // routerLink: route.path ? [route.path] : undefined,
1391
+ title: route.name,
1392
+ expanded: false
1393
+ };
1394
+ if (route.isExternal) {
1395
+ item.url = route.path;
1396
+ item.target = '_blank';
1397
+ }
1398
+ else if (route.path) {
1399
+ item.routerLink = [route.path];
1400
+ }
1401
+ item.isExternal = route.isExternal ?? false;
1402
+ item.addRoute = route.addRoute ?? '';
1403
+ if (this.routesService.hasChildren(route.name)) {
1404
+ item.items = this.buildItems(route.children ?? []);
1405
+ }
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
+ return item;
1422
+ }
1423
+ hasPermission(policy) {
1424
+ // implement using permission service
1425
+ return true;
1426
+ }
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
+ };
1435
+ }
1436
+ 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-65 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 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>{{ 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 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>{{ 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}.render-menu-container span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.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" }] });
1438
+ }
1439
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesComponent, decorators: [{
1440
+ 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-65 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 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>{{ 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 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>{{ 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}.render-menu-container span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.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: [{
1443
+ type: HostListener,
1444
+ args: ['window:resize']
1445
+ }] } });
1446
+
1447
+ class AuthWrapperComponent {
1448
+ constructor() { }
1449
+ 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" });
1451
+ }
1452
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AuthWrapperComponent, decorators: [{
1453
+ 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" }]
1455
+ }], ctorParameters: () => [] });
1456
+
1457
+ class AccountLayoutComponent {
1458
+ service;
1459
+ // required for dynamic component
1460
+ static type = eLayoutType.account;
1461
+ authWrapperKey = 'Account.AuthWrapperComponent';
1462
+ constructor(service) {
1463
+ this.service = service;
1464
+ }
1465
+ ngAfterViewInit() {
1466
+ // this.service.subscribeWindowSize();
1467
+ }
1468
+ 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"] }] });
1470
+ }
1471
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AccountLayoutComponent, decorators: [{
1472
+ 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" }]
1474
+ }], ctorParameters: () => [{ type: LayoutService }] });
1475
+
1476
+ class ApplicationLayoutComponent {
1477
+ layoutService;
1478
+ renderer;
1479
+ router;
1480
+ _zone;
1481
+ translate;
1482
+ localization = inject(LocalizationService);
1483
+ service = inject(LayoutService);
1484
+ platformId = inject(PLATFORM_ID);
1485
+ // required for dynamic component
1486
+ static type = eLayoutType.application;
1487
+ overlayMenuOpenSubscription;
1488
+ // @Input() showTopbar= true;
1489
+ menuOutsideClickListener;
1490
+ sessionState = inject(SessionStateService);
1491
+ constructor(layoutService, renderer, router, _zone,
1492
+ // private _signalRService:SignalRService,
1493
+ translate) {
1494
+ this.layoutService = layoutService;
1495
+ this.renderer = renderer;
1496
+ this.router = router;
1497
+ this._zone = _zone;
1498
+ this.translate = translate;
1499
+ var isBrowser = isPlatformBrowser(this.platformId);
1500
+ if (isBrowser) {
1501
+ var currentLanguage = 'ar';
1502
+ const savedLanguage = localStorage.getItem('language');
1503
+ var lang = this.sessionState.getLanguage();
1504
+ if (savedLanguage) {
1505
+ currentLanguage = savedLanguage;
1506
+ }
1507
+ if (lang) {
1508
+ currentLanguage = lang;
1509
+ }
1510
+ try {
1511
+ // this.translate.use(currentLanguage).subscribe(() => {
1512
+ // this.sessionState.setLanguage(currentLanguage);
1513
+ // });
1514
+ this.sessionState.setLanguage(currentLanguage);
1515
+ }
1516
+ catch (error) {
1517
+ }
1518
+ this.updateHtmlDirection(currentLanguage);
1519
+ const saved = localStorage.getItem('darkTheme');
1520
+ this.layoutService.layoutConfig.update((state) => ({ ...state, darkTheme: saved === 'true' }));
1521
+ }
1522
+ this.overlayMenuOpenSubscription = this.layoutService.overlayOpen$.subscribe(() => {
1523
+ if (!this.menuOutsideClickListener) {
1524
+ this.menuOutsideClickListener = this.renderer.listen('document', 'click', (event) => {
1525
+ if (this.isOutsideClicked(event)) {
1526
+ this.hideMenu();
1527
+ }
1528
+ });
1529
+ }
1530
+ if (this.layoutService.layoutState().staticMenuMobileActive) {
1531
+ this.blockBodyScroll();
1532
+ }
1533
+ });
1534
+ this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => {
1535
+ this.hideMenu();
1536
+ });
1537
+ }
1538
+ updateHtmlDirection(languageCode) {
1539
+ const htmlElement = document.documentElement;
1540
+ if (languageCode === 'ar' || languageCode.includes('ar-')) {
1541
+ htmlElement.setAttribute('dir', 'rtl');
1542
+ htmlElement.classList.add('rtl');
1543
+ }
1544
+ else {
1545
+ //TODO
1546
+ htmlElement.setAttribute('dir', 'ltr');
1547
+ htmlElement.classList.remove('rtl');
1548
+ }
1549
+ }
1550
+ isOutsideClicked(event) {
1551
+ if (typeof document === 'undefined')
1552
+ return;
1553
+ const sidebarEl = document.querySelector('.layout-sidebar');
1554
+ const topbarEl = document.querySelector('.layout-menu-button');
1555
+ const eventTarget = event.target;
1556
+ return !(sidebarEl?.isSameNode(eventTarget) || sidebarEl?.contains(eventTarget) || topbarEl?.isSameNode(eventTarget) || topbarEl?.contains(eventTarget));
1557
+ }
1558
+ hideMenu() {
1559
+ this.layoutService.layoutState.update((prev) => ({ ...prev, overlayMenuActive: false, staticMenuMobileActive: false, menuHoverActive: false }));
1560
+ if (this.menuOutsideClickListener) {
1561
+ this.menuOutsideClickListener();
1562
+ this.menuOutsideClickListener = null;
1563
+ }
1564
+ this.unblockBodyScroll();
1565
+ }
1566
+ blockBodyScroll() {
1567
+ if (typeof document === 'undefined')
1568
+ return;
1569
+ if (document.body.classList) {
1570
+ document.body.classList.add('blocked-scroll');
1571
+ }
1572
+ else {
1573
+ document.body.className += ' blocked-scroll';
1574
+ }
1575
+ }
1576
+ unblockBodyScroll() {
1577
+ if (typeof document === 'undefined')
1578
+ return;
1579
+ if (document.body.classList) {
1580
+ document.body.classList.remove('blocked-scroll');
1581
+ }
1582
+ else {
1583
+ document.body.className = document.body.className.replace(new RegExp('(^|\\b)' + 'blocked-scroll'.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
1584
+ }
1585
+ }
1586
+ get containerClass() {
1587
+ return {
1588
+ 'layout-overlay': this.layoutService.layoutConfig().menuMode === 'overlay',
1589
+ 'layout-static': this.layoutService.layoutConfig().menuMode === 'static',
1590
+ 'layout-static-inactive': this.layoutService.layoutState().staticMenuDesktopInactive && this.layoutService.layoutConfig().menuMode === 'static',
1591
+ 'layout-overlay-active': this.layoutService.layoutState().overlayMenuActive,
1592
+ 'layout-mobile-active': this.layoutService.layoutState().staticMenuMobileActive
1593
+ };
1594
+ }
1595
+ ngOnInit() {
1596
+ }
1597
+ ngOnDestroy() {
1598
+ if (this.overlayMenuOpenSubscription) {
1599
+ this.overlayMenuOpenSubscription.unsubscribe();
1600
+ }
1601
+ if (this.menuOutsideClickListener) {
1602
+ this.menuOutsideClickListener();
1603
+ }
1604
+ }
1605
+ ngAfterViewInit() {
1606
+ // this.service.subscribeWindowSize();
1607
+ try {
1608
+ var mainColor = "hsl(195,64.7%,26.2%";
1609
+ document.body.style.setProperty('--main-theme-color', mainColor);
1610
+ // document.documentElement.style.setProperty('--main-theme-color', this.apptheme.mainColor);
1611
+ // Find the <meta> tag with the name "theme-color"
1612
+ var metaTag = document.querySelector('meta[name="theme-color"]');
1613
+ // Check if the <meta> tag exists
1614
+ if (metaTag) {
1615
+ // Update the value of the content attribute
1616
+ metaTag.setAttribute('content', mainColor);
1617
+ }
1618
+ var metaTag1 = document.querySelector('meta[name="apple-mobile-web-status-bar"]');
1619
+ // Check if the <meta> tag exists
1620
+ if (metaTag1) {
1621
+ // Update the value of the content attribute
1622
+ metaTag1.setAttribute('content', mainColor);
1623
+ }
1624
+ }
1625
+ catch (error) {
1626
+ }
1627
+ try {
1628
+ Swal.close();
1629
+ }
1630
+ catch (error) {
1631
+ }
1632
+ try {
1633
+ // new StyleLoaderService()
1634
+ // .load(
1635
+ // "https://site-assets.fontawesome.com/releases/v6.7.2/css/all.css",
1636
+ // "https://cdn.jsdelivr.net/npm/primeicons@7.0.0/primeicons.min.css",
1637
+ // // "/assets/filepond.min.css",
1638
+ // // "/assets/bootstrap-datepicker.min.css",
1639
+ // // "/assets/activereports/styles/ar-js-viewer.css",
1640
+ // // "/assets/activereports/styles/ar-js-ui.css",
1641
+ // )
1642
+ // .then(() => {
1643
+ // });
1644
+ }
1645
+ catch (error) {
1646
+ }
1647
+ }
1648
+ 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() */] });
1650
+ }
1651
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ApplicationLayoutComponent, decorators: [{
1652
+ 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" }]
1654
+ }], ctorParameters: () => [{ type: LayoutService }, { type: i0.Renderer2 }, { type: i2$2.Router }, { type: i0.NgZone }, { type: i3$3.LocalizationService }] });
1655
+
1656
+ class EmptyLayoutComponent {
1657
+ static type = eLayoutType.empty;
1658
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmptyLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1659
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: EmptyLayoutComponent, isStandalone: true, selector: "abp-layout-empty", ngImport: i0, template: ` <router-outlet></router-outlet> `, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$2.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] });
1660
+ }
1661
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmptyLayoutComponent, decorators: [{
1662
+ type: Component,
1663
+ args: [{
1664
+ standalone: true,
1665
+ imports: [RouterModule],
1666
+ selector: 'abp-layout-empty',
1667
+ template: ` <router-outlet></router-outlet> `,
1668
+ }]
1669
+ }] });
1670
+
1671
+ // export * from './validation-error/validation-error.component';
1672
+
1673
+ const BASIC_THEME_NAV_ITEM_PROVIDERS = [
1674
+ provideAppInitializer(() => {
1675
+ configureNavItems();
1676
+ }),
1677
+ ];
1678
+ function configureNavItems() {
1679
+ const navItems = inject(NavItemsService);
1680
+ navItems.addItems([
1681
+ // {
1682
+ // id: eThemeBasicComponents.Languages,
1683
+ // order: 100,
1684
+ // component: LanguagesComponent,
1685
+ // },
1686
+ // {
1687
+ // id: eThemeBasicComponents.CurrentUser,
1688
+ // order: 100,
1689
+ // component: CurrentUserComponent,
1690
+ // },
1691
+ ]);
1692
+ }
1693
+
1694
+ const BASIC_THEME_STYLES_PROVIDERS = [
1695
+ provideAppInitializer(() => {
1696
+ const initializerFn = configureStyles(
1697
+ // inject(DomInsertionService),
1698
+ inject(ReplaceableComponentsService));
1699
+ return initializerFn();
1700
+ }),
1701
+ ];
1702
+ function configureStyles(
1703
+ // domInsertion: DomInsertionService,
1704
+ replaceableComponents) {
1705
+ return () => {
1706
+ // domInsertion.insertContent(CONTENT_STRATEGY.AppendStyleToHead(styles));
1707
+ initLayouts(replaceableComponents);
1708
+ };
1709
+ }
1710
+ function initLayouts(replaceableComponents) {
1711
+ replaceableComponents.add({
1712
+ key: "Theme.ApplicationLayoutComponent" /* eThemeBasicComponents.ApplicationLayout */,
1713
+ component: ApplicationLayoutComponent,
1714
+ });
1715
+ replaceableComponents.add({
1716
+ key: "Theme.AccountLayoutComponent" /* eThemeBasicComponents.AccountLayout */,
1717
+ component: AccountLayoutComponent,
1718
+ });
1719
+ replaceableComponents.add({
1720
+ key: "Theme.EmptyLayoutComponent" /* eThemeBasicComponents.EmptyLayout */,
1721
+ component: EmptyLayoutComponent,
1722
+ });
1723
+ }
1724
+
1725
+ const BASIC_THEME_USER_MENU_PROVIDERS = [
1726
+ provideAppInitializer(() => {
1727
+ const initializerFn = configureUserMenu(inject(Injector));
1728
+ return initializerFn();
1729
+ }),
1730
+ ];
1731
+ function configureUserMenu(injector) {
1732
+ const sessionService = injector.get(SessionStateService);
1733
+ const userMenu = injector.get(UserMenuService);
1734
+ const authService = injector.get(AuthService);
1735
+ const navigateToManageProfile = injector.get(NAVIGATE_TO_MANAGE_PROFILE);
1736
+ return () => {
1737
+ userMenu.addItems([
1738
+ // // Only show when impersonated
1739
+ // ...(sessionService.impersonatorUserId
1740
+ // ? [{
1741
+ // id: eUserMenuItems.BackToMyAccount,
1742
+ // order: 0,
1743
+ // textTemplate: { text: 'BackToMyAccount', icon: 'pi pi-user-undo' },
1744
+ // action: () => {
1745
+ // // this.backToMyAccount()
1746
+ // },
1747
+ // }]
1748
+ // : [])
1749
+ // ,
1750
+ // {
1751
+ // id: eUserMenuItems.ChangePassword,
1752
+ // order: 1,
1753
+ // textTemplate: {
1754
+ // text: 'ChangePassword',
1755
+ // icon: 'pi pi-lock',
1756
+ // },
1757
+ // action: () => {
1758
+ // // this.changePassword()
1759
+ // },
1760
+ // },
1761
+ // {
1762
+ // id: eUserMenuItems.LoginAttempts,
1763
+ // order: 2,
1764
+ // textTemplate: {
1765
+ // text: 'LoginAttempts',
1766
+ // icon: 'pi pi-history',
1767
+ // },
1768
+ // action: () => {
1769
+ // // this.showLoginAttempts()
1770
+ // },
1771
+ // },
1772
+ // {
1773
+ // id: eUserMenuItems.MySettings,
1774
+ // order: 3,
1775
+ // textTemplate: {
1776
+ // text: 'MySettings',
1777
+ // icon: 'pi pi-cog',
1778
+ // },
1779
+ // // action: () => this.changeMySettings(),
1780
+ // },
1781
+ // // your existing high-order items
1782
+ // {
1783
+ // id: eUserMenuItems.MyAccount,
1784
+ // order: 100,
1785
+ // textTemplate: {
1786
+ // text: 'Account::MyAccount',
1787
+ // icon: 'pi pi-user-cog',
1788
+ // },
1789
+ // action: () => navigateToManageProfile(),
1790
+ // },
1791
+ {
1792
+ id: "UserMenu.Logout" /* eUserMenuItems.Logout */,
1793
+ order: 101,
1794
+ visible: (item) => true,
1795
+ textTemplate: {
1796
+ // text: 'تسجيل الخروج',
1797
+ text: 'Logout',
1798
+ icon: 'pi pi-sign-out',
1799
+ },
1800
+ action: () => {
1801
+ authService.logout().subscribe();
1802
+ try {
1803
+ location.href = '/account/login';
1804
+ }
1805
+ catch (error) {
1806
+ }
1807
+ },
1808
+ },
1809
+ ]
1810
+ // filter out any nulls (e.g. when not impersonated)
1811
+ );
1812
+ };
1813
+ }
1814
+
1815
+ function provideThemeBasicConfig() {
1816
+ return makeEnvironmentProviders([
1817
+ // BrowserAnimationsModule
1818
+ BASIC_THEME_NAV_ITEM_PROVIDERS,
1819
+ BASIC_THEME_USER_MENU_PROVIDERS,
1820
+ BASIC_THEME_STYLES_PROVIDERS,
1821
+ // {
1822
+ // provide: VALIDATION_ERROR_TEMPLATE,
1823
+ // useValue: ValidationErrorComponent,
1824
+ // },
1825
+ // {
1826
+ // provide: VALIDATION_TARGET_SELECTOR,
1827
+ // useValue: '.form-group',
1828
+ // },
1829
+ // {
1830
+ // provide: VALIDATION_INVALID_CLASSES,
1831
+ // useValue: 'is-invalid',
1832
+ // },
1833
+ // LazyStyleHandler,
1834
+ // provideAppInitializer(() => {
1835
+ // inject(LazyStyleHandler);
1836
+ // }),
1837
+ ]);
1838
+ }
1839
+
1840
+ const LAZY_STYLES = new InjectionToken('LAZY_STYLES');
1841
+
1842
+ // export * from './constants';
1843
+ // export * from './handlers';
1844
+
1845
+ /**
1846
+ * Generated bundle index. Do not edit.
1847
+ */
1848
+
1849
+ export { AccountLayoutComponent, ApplicationLayoutComponent, AuthWrapperComponent, BASIC_THEME_NAV_ITEM_PROVIDERS, BASIC_THEME_STYLES_PROVIDERS, BASIC_THEME_USER_MENU_PROVIDERS, EmptyLayoutComponent, LAZY_STYLES, LayoutService, NavItemsComponent, RoutesComponent, configureNavItems, configureStyles, configureUserMenu, provideThemeBasicConfig };
1850
+ //# sourceMappingURL=es.framework-ng.ui.theme.mjs.map