@es.framework/ng.ui.theme 2.0.60 → 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.
@@ -4,12 +4,12 @@ import { Subject, Observable, lastValueFrom, BehaviorSubject, map, filter } from
4
4
  import * as i3$3 from '@es.framework/ng.core/services';
5
5
  import { ConfigStateService, SessionStateService, LocalizationService, LocalStorageService, NavItemsService, UserMenuService, RoutesService, SubscriptionService, ReplaceableComponentsService } from '@es.framework/ng.core/services';
6
6
  import { eLayoutType } from '@es.framework/ng.core/models';
7
- import * as i2$1 from '@angular/router';
7
+ import * as i2$2 from '@angular/router';
8
8
  import { Router, RouterModule, RouterOutlet, NavigationEnd } from '@angular/router';
9
9
  import * as i1$1 from '@angular/common';
10
10
  import { isPlatformBrowser, CommonModule } from '@angular/common';
11
11
  import { StyleClassModule } from 'primeng/styleclass';
12
- import * as i3$2 from 'primeng/avatar';
12
+ import * as i3$1 from 'primeng/avatar';
13
13
  import { AvatarModule } from 'primeng/avatar';
14
14
  import * as i5 from 'primeng/menu';
15
15
  import { MenuModule } from 'primeng/menu';
@@ -18,8 +18,8 @@ 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';
22
- import * as i3 from '@angular/forms';
21
+ import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
22
+ import * as i2$1 from '@angular/forms';
23
23
  import { FormsModule } from '@angular/forms';
24
24
  import { SelectModule } from 'primeng/select';
25
25
  import * as i1 from 'primeng/popover';
@@ -30,11 +30,11 @@ import Aura from '@primeng/themes/aura';
30
30
  import Lara from '@primeng/themes/lara';
31
31
  import Nora from '@primeng/themes/nora';
32
32
  import { PrimeNG } from 'primeng/config';
33
- import * as i3$1 from 'primeng/selectbutton';
33
+ import * as i3 from 'primeng/selectbutton';
34
34
  import { SelectButtonModule } from 'primeng/selectbutton';
35
35
  import { AuthService } from '@es.framework/ng.core/abstracts';
36
36
  import * as i4 from 'primeng/api';
37
- import * as i4$1 from 'primeng/tooltip';
37
+ import * as i3$2 from 'primeng/tooltip';
38
38
  import { TooltipModule } from 'primeng/tooltip';
39
39
  import Swal from 'sweetalert2';
40
40
  import { NAVIGATE_TO_MANAGE_PROFILE } from '@es.framework/ng.core/tokens';
@@ -721,7 +721,7 @@ class AppConfigurator {
721
721
  </div>
722
722
  }
723
723
  </div>
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: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i3$1.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"] }] });
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"] }] });
725
725
  }
726
726
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AppConfigurator, decorators: [{
727
727
  type: Component,
@@ -1067,7 +1067,7 @@ class NavItemsComponent {
1067
1067
  </button> -->
1068
1068
  </div>
1069
1069
  </header>
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$1.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$2.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" }] });
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" }] });
1071
1071
  }
1072
1072
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: NavItemsComponent, decorators: [{
1073
1073
  type: Component,
@@ -1255,91 +1255,74 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
1255
1255
  }]
1256
1256
  }], ctorParameters: () => [] });
1257
1257
 
1258
- class RoutesComponent {
1259
- subSidebarVisible = false;
1260
- sidebarVisible = true;
1261
- isMobile = false;
1262
- searchText = ''; // ✅ البحث
1258
+ class RoutesMenuService {
1263
1259
  router = inject(Router);
1264
- localization = inject(LocalizationService);
1265
- layoutService = inject(LayoutService);
1266
1260
  routesService = inject(RoutesService);
1267
- configState = inject(ConfigStateService);
1268
- secondToLastLevel$;
1261
+ layoutService = inject(LayoutService);
1262
+ sidebarVisible = true;
1263
+ subSidebarVisible = false;
1264
+ isMobile = false;
1265
+ showTreeSearch = false;
1266
+ searchText = '';
1269
1267
  _menuItemsSubject = new BehaviorSubject([]);
1268
+ expandedStateBeforeSearch = null;
1270
1269
  menuItems$ = this._menuItemsSubject.asObservable();
1271
- constructor() {
1272
- this.routesService.visible$.pipe(map(routes => {
1273
- const items = this.buildItems(routes);
1274
- // ✅ فتح الشجرة كاملة عند الدخول للنظام
1275
- // this.setExpandedRecursive(items, true);
1276
- return items;
1277
- })).subscribe(items => this._menuItemsSubject.next(items));
1278
- }
1279
- ngOnInit() {
1280
- this.checkViewport();
1281
- this.secondToLastLevel$ = this.menuItems$.pipe(map(items => {
1282
- const result = [];
1283
- for (const item of items) {
1284
- if (item.items && item.items.length > 0) {
1285
- result.push(...item.items);
1286
- }
1287
- }
1288
- return result;
1289
- }));
1290
- // ✅ إظهار الشجرة الفرعية مفتوحة في الديسكتوب عند الدخول
1291
- if (!this.isMobile) {
1292
- this.sidebarVisible = true;
1293
- this.subSidebarVisible = true;
1294
- }
1295
- }
1296
- setExpandedRecursive(items, expanded) {
1270
+ secondToLastLevel$ = this.menuItems$.pipe(map((items) => {
1271
+ const result = [];
1297
1272
  for (const item of items) {
1298
- item.expanded = expanded;
1299
1273
  if (item.items?.length) {
1300
- this.setExpandedRecursive(item.items, expanded);
1274
+ result.push(...item.items);
1301
1275
  }
1302
1276
  }
1277
+ return result;
1278
+ }));
1279
+ constructor() {
1280
+ this.routesService.visible$
1281
+ .pipe(map((routes) => this.buildItems(routes)), takeUntilDestroyed())
1282
+ .subscribe((items) => this.setItems(items));
1283
+ }
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]);
1303
1293
  }
1304
1294
  expandAll() {
1305
- const items = this._menuItemsSubject.value;
1306
- this.setExpandedRecursive(items, true);
1295
+ this.setExpandedRecursive(this.items, true);
1307
1296
  this.subSidebarVisible = true;
1308
- this._menuItemsSubject.next([...items]);
1297
+ this.refresh();
1309
1298
  }
1310
1299
  collapseAll() {
1311
- const items = this._menuItemsSubject.value;
1312
- this.setExpandedRecursive(items, false);
1313
- this._menuItemsSubject.next([...items]);
1300
+ this.setExpandedRecursive(this.items, false);
1301
+ this.refresh();
1314
1302
  }
1315
- onSearchChange() {
1316
- const items = this._menuItemsSubject.value;
1303
+ search(keyword) {
1304
+ if (!this.expandedStateBeforeSearch) {
1305
+ this.expandedStateBeforeSearch = this.captureExpandedState(this.items);
1306
+ }
1307
+ this.searchText = keyword;
1317
1308
  if (!this.searchText.trim()) {
1318
- // ✅ عند مسح البحث ترجع مغلقة مثل البداية
1319
- this.setExpandedRecursive(items, false);
1320
- this._menuItemsSubject.next([...items]);
1309
+ this.restoreExpandedStateBeforeSearch();
1310
+ this.refresh();
1321
1311
  return;
1322
1312
  }
1323
- // ✅ أثناء البحث يفتح فقط المسارات المطابقة
1324
- this.expandMatchedParents(items, this.searchText);
1325
- this._menuItemsSubject.next([...items]);
1313
+ this.expandMatchedParents(this.items, this.searchText);
1314
+ this.refresh();
1326
1315
  }
1327
- expandMatchedParents(items, keyword) {
1328
- const term = keyword.trim().toLowerCase();
1329
- let hasMatch = false;
1330
- for (const item of items) {
1331
- const label = String(item.label ?? '').toLowerCase();
1332
- const title = String(item.title ?? '').toLowerCase();
1333
- const selfMatch = label.includes(term) || title.includes(term);
1334
- const childMatch = item.items?.length
1335
- ? this.expandMatchedParents(item.items, keyword)
1336
- : false;
1337
- item.expanded = !!childMatch || !!selfMatch;
1338
- if (selfMatch || childMatch) {
1339
- hasMatch = true;
1340
- }
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();
1341
1325
  }
1342
- return hasMatch;
1343
1326
  }
1344
1327
  isSearchMatch(item) {
1345
1328
  const term = this.searchText.trim().toLowerCase();
@@ -1349,86 +1332,39 @@ class RoutesComponent {
1349
1332
  const title = String(item.title ?? '').toLowerCase();
1350
1333
  return label.includes(term) || title.includes(term);
1351
1334
  }
1352
- clearSearch() {
1353
- this.searchText = '';
1354
- const items = this._menuItemsSubject.value;
1355
- // ✅ لا تفتح الكل عند مسح البحث
1356
- this.setExpandedRecursive(items, false);
1357
- this._menuItemsSubject.next([...items]);
1358
- }
1359
- getSecondToLastLevel(items) {
1360
- const result = [];
1361
- const traverse = (nodes) => {
1362
- for (const node of nodes) {
1363
- if (node.items && node.items.length > 0) {
1364
- const hasGrandChildren = node.items.some(c => c.items && c.items.length > 0);
1365
- if (!hasGrandChildren) {
1366
- result.push(node);
1367
- }
1368
- else {
1369
- traverse(node.items);
1370
- }
1371
- }
1372
- }
1373
- };
1374
- traverse(items);
1375
- return result;
1376
- }
1377
- onParentClick(item) {
1378
- item.expanded = !item.expanded;
1335
+ closeMenu() {
1336
+ this.subSidebarVisible = false;
1337
+ this.sidebarVisible = false;
1379
1338
  }
1380
- onResize() {
1381
- this.checkViewport();
1339
+ openMenu() {
1340
+ this.sidebarVisible = true;
1341
+ this.subSidebarVisible = true;
1382
1342
  }
1383
- checkViewport() {
1343
+ syncViewport() {
1384
1344
  this.isMobile = window.innerWidth < 1024;
1345
+ this.layoutService.smallScreen = this.isMobile;
1385
1346
  if (this.isMobile) {
1386
1347
  this.sidebarVisible = false;
1387
1348
  this.subSidebarVisible = false;
1349
+ return;
1388
1350
  }
1389
- else {
1390
- this.sidebarVisible = true;
1391
- // ✅ خلي الشجرة ظاهرة في الديسكتوب
1392
- this.subSidebarVisible = true;
1393
- }
1351
+ this.sidebarVisible = true;
1352
+ this.subSidebarVisible = true;
1394
1353
  }
1395
- toggleAll() {
1354
+ toggleSidebar() {
1396
1355
  if (this.isMobile) {
1397
1356
  this.sidebarVisible = !this.sidebarVisible;
1398
1357
  this.subSidebarVisible = this.sidebarVisible;
1358
+ return;
1399
1359
  }
1400
- else {
1401
- this.subSidebarVisible = !this.subSidebarVisible;
1402
- }
1403
- }
1404
- openMenu() {
1405
- this.sidebarVisible = true;
1406
- this.subSidebarVisible = true;
1407
- }
1408
- expandPath(items, target) {
1409
- for (const item of items) {
1410
- if (item === target)
1411
- return true;
1412
- if (item.items && item.items.length > 0) {
1413
- const found = this.expandPath(item.items, target);
1414
- if (found) {
1415
- item.expanded = true;
1416
- return true;
1417
- }
1418
- }
1419
- }
1420
- return false;
1360
+ this.subSidebarVisible = !this.subSidebarVisible;
1421
1361
  }
1422
- onMainClick(target) {
1423
- const items = this._menuItemsSubject.value;
1362
+ activateItem(target) {
1424
1363
  const isExternal = target.isExternal ?? false;
1425
1364
  if (!target.items?.length) {
1426
1365
  if (isExternal && target.url) {
1427
1366
  window.open(target.url, '_blank', 'noopener,noreferrer');
1428
- if (this.isMobile)
1429
- this.closeMenu();
1430
- this.expandPath(items, target);
1431
- this._menuItemsSubject.next([...items]);
1367
+ this.afterNavigate(target);
1432
1368
  return;
1433
1369
  }
1434
1370
  if (target.routerLink) {
@@ -1437,42 +1373,93 @@ class RoutesComponent {
1437
1373
  ? target.routerLink[0]
1438
1374
  : target.routerLink;
1439
1375
  if (currentUrl !== targetUrl) {
1440
- 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]);
1441
1379
  }
1442
- if (this.isMobile)
1443
- this.closeMenu();
1444
- this.expandPath(items, target);
1445
- this._menuItemsSubject.next([...items]);
1380
+ this.afterNavigate(target);
1446
1381
  return;
1447
1382
  }
1448
1383
  }
1449
1384
  target.expanded = !target.expanded;
1450
- this.subSidebarVisible = target.expanded;
1451
- this._menuItemsSubject.next([...items]);
1385
+ this.subSidebarVisible = true;
1386
+ this.refresh();
1387
+ }
1388
+ closeAfterNavigate() {
1389
+ if (this.isMobile) {
1390
+ this.closeMenu();
1391
+ }
1452
1392
  }
1453
- toggleExpand(item) {
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
+ };
1401
+ }
1402
+ expandPath(target) {
1403
+ const found = this.expandPathRecursive(this.items, target);
1404
+ this.refresh();
1405
+ return found;
1406
+ }
1407
+ toggleItem(item) {
1454
1408
  item.expanded = !item.expanded;
1409
+ this.refresh();
1455
1410
  }
1456
- closeMenu() {
1457
- this.subSidebarVisible = false;
1458
- this.sidebarVisible = false;
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
+ }
1459
1418
  }
1460
- onNavigate() {
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) {
1461
1447
  if (this.isMobile) {
1462
1448
  this.closeMenu();
1463
1449
  }
1450
+ this.expandPath(target);
1464
1451
  }
1465
1452
  buildItems(routes) {
1466
1453
  return routes
1467
- .filter(r => !r.requiredPolicy || this.hasPermission(r.requiredPolicy))
1468
- .map(r => this.createItem(r));
1454
+ .filter((route) => !route.requiredPolicy || this.hasPermission(route.requiredPolicy))
1455
+ .map((route) => this.createItem(route));
1469
1456
  }
1470
1457
  createItem(route) {
1471
1458
  const item = {
1472
1459
  label: route.name,
1473
1460
  icon: route.iconClass,
1474
1461
  title: route.name,
1475
- expanded: false // ✅ بدل false، افتح كل عنصر افتراضياً
1462
+ expanded: false,
1476
1463
  };
1477
1464
  if (route.isExternal) {
1478
1465
  item.url = route.path;
@@ -1491,38 +1478,104 @@ class RoutesComponent {
1491
1478
  hasPermission(policy) {
1492
1479
  return true;
1493
1480
  }
1494
- getLinkProps(item) {
1495
- const isExternal = item.isExternal ?? false;
1496
- return {
1497
- isExternal,
1498
- href: isExternal ? item.url ?? null : null,
1499
- routerLink: !isExternal ? item.routerLink ?? null : null,
1500
- addRoute: item.addRoute ?? ''
1501
- };
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
+ }
1502
1488
  }
1503
- showTreeSearch = false;
1504
- toggleTreeSearch() {
1505
- this.showTreeSearch = !this.showTreeSearch;
1506
- if (!this.showTreeSearch) {
1507
- this.clearSearch();
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
+ }
1508
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();
1509
1565
  }
1510
1566
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1511
- 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\"\r\n [ngClass]=\"{ 'w-0 overflow-hidden': !sidebarVisible && isMobile }\">\r\n\r\n <!-- \uD83C\uDFA8 \u0627\u0644\u0634\u0631\u064A\u0637 \u0627\u0644\u062C\u0627\u0646\u0628\u064A \u0627\u0644\u0631\u0626\u064A\u0633\u064A -->\r\n <div\r\n class=\"flex flex-col justify-between pt-4 pb-2 w-16 border-l shadow-xl\r\n bg-gradient-to-b from-primary-500 via-primary-600 to-primary-700\r\n text-white rounded-r-3xl\">\r\n\r\n <!-- \uD83D\uDD1D \u0627\u0644\u0623\u0639\u0644\u0649 -->\r\n <div class=\"flex flex-col items-center gap-4\">\r\n <img src=\"/assets/logo.png\" alt=\"Logo\"\r\n class=\"w-9 h-9 mt-1 rounded-lg shadow-sm\"/>\r\n\r\n <!-- \u0632\u0631 \u0627\u0644\u0642\u0627\u0626\u0645\u0629 -->\r\n <!-- [pTooltip]=\"'TOGGLE_MENU' | translate\" -->\r\n <button (click)=\"toggleAll()\"\r\n tooltipPosition=\"right\"\r\n class=\"text-white hover:scale-110 transition-transform duration-200\">\r\n <i class=\"pi pi-bars text-lg\"></i>\r\n </button>\r\n\r\n <!-- \u0623\u064A\u0642\u0648\u0646\u0627\u062A -->\r\n @for (item of secondToLastLevel$ | async; track item) {\r\n <div class=\"relative flex flex-col items-center\">\r\n <!-- \u0627\u0644\u0623\u064A\u0642\u0648\u0646\u0629 -->\r\n <div (click)=\"onMainClick(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\r\n hover:bg-white/20 transition-all duration-200\"\r\n [ngClass]=\"{ 'bg-white/30 scale-105 shadow-inner': item.expanded }\">\r\n <i [class]=\"item.icon + ' text-xl'\"></i>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- \u2699\uFE0F \u0627\u0644\u0623\u0633\u0641\u0644 -->\r\n <div class=\"flex flex-col items-center pb-3 text-white/80\">\r\n <i class=\"pi pi-cog text-lg hover:text-white cursor-pointer transition\"></i>\r\n </div>\r\n </div>\r\n\r\n <!-- \uD83D\uDCCB \u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0641\u0631\u0639\u064A\u0629 -->\r\n @if (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\r\n 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': isMobile }\">\r\n <!-- \u0632\u0631 \u0631\u062C\u0648\u0639 -->\r\n <!-- \uD83D\uDD1D \u0631\u0623\u0633 \u0627\u0644\u0634\u062C\u0631\u0629 -->\r\n<!-- \uD83D\uDD1D \u0631\u0623\u0633 \u0627\u0644\u0634\u062C\u0631\u0629 -->\r\n<div class=\"tree-header mb-1\">\r\n\r\n <button\r\n type=\"button\"\r\n (click)=\"closeMenu()\"\r\n class=\"tree-back-btn\">\r\n <i class=\"pi pi-angle-right\"></i>\r\n <span>{{ 'back' | translate }}</span>\r\n </button>\r\n\r\n <div class=\"tree-header-actions\">\r\n\r\n <button\r\n type=\"button\"\r\n class=\"tree-mini-btn\"\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 <i class=\"pi pi-plus\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"tree-mini-btn\"\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 <i class=\"pi pi-minus\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"tree-search-toggle\"\r\n [ngClass]=\"{ 'active': showTreeSearch }\"\r\n (click)=\"toggleTreeSearch()\"\r\n pTooltip=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A\"\r\n tooltipPosition=\"top\">\r\n <i class=\"pi pi-search\"></i>\r\n </button>\r\n\r\n </div>\r\n\r\n</div>\r\n\r\n@if (showTreeSearch) {\r\n <div class=\"tree-search-panel mb-4 animate-slideIn\">\r\n <div class=\"tree-search\">\r\n <i class=\"pi pi-search\"></i>\r\n\r\n <input\r\n type=\"text\"\r\n [(ngModel)]=\"searchText\"\r\n (input)=\"onSearchChange()\"\r\n placeholder=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A...\"\r\n class=\"tree-search-input\" />\r\n\r\n @if (searchText) {\r\n <button\r\n type=\"button\"\r\n class=\"tree-search-clear\"\r\n (click)=\"clearSearch()\">\r\n <i class=\"pi pi-times\"></i>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n}\r\n\r\n\r\n <!-- Recursive rendering -->\r\n @if (menuItems$ | async; as menuItems) {\r\n <ng-container *ngTemplateOutlet=\"renderMenu; context:{ $implicit: menuItems, level: 1 }\"></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\r\n @if (!item.items?.length) {\r\n @let link = getLinkProps(item);\r\n <div class=\"mb-1\">\r\n @if (link.isExternal) {\r\n <a [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 <i [class]=\"item.icon + ' text-base'\"></i>\r\n <span class=\"min-w-0 break-words whitespace-normal\">{{ item.label | translate }}</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 [routerLink]=\"link.routerLink\"\r\n (click)=\"onNavigate()\"\r\n routerLinkActive=\"active-link\"\r\n [ngClass]=\"{ 'search-match': 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 <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 </a>\r\n\r\n@if (link.addRoute) {\r\n <a\r\n [routerLink]=\"[link.addRoute]\"\r\n (click)=\"$event.stopPropagation(); onNavigate()\"\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 <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\r\n <div\r\n (click)=\"onParentClick(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': item.expanded,\r\n 'search-match': 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\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n\r\n <span>\r\n {{ item.label | translate }}\r\n </span>\r\n\r\n </div>\r\n\r\n <i\r\n class=\"pi\"\r\n [ngClass]=\"item.expanded ? 'pi-chevron-down' : 'pi-chevron-left'\"\r\n style=\"font-size:0.6rem;\">\r\n </i>\r\n\r\n </div>\r\n\r\n @if (item.expanded) {\r\n <div 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=\"renderMenu; context:{ $implicit: item.items, level: level + 1 }\">\r\n </ng-container>\r\n\r\n </div>\r\n }\r\n\r\n </div>\r\n }\r\n\r\n }\r\n</ng-template>\r\n\r\n </div>\r\n}\r\n\r\n</aside>\r\n\r\n<!-- \uD83C\uDF1F \u0627\u0644\u0632\u0631 \u0627\u0644\u0639\u0627\u0626\u0645 \u0627\u0644\u062D\u062F\u064A\u062B -->\r\n@if (isMobile && !sidebarVisible) {\r\n <button\r\n class=\"floating-btn\"\r\n (click)=\"openMenu()\"\r\n pTooltip=\"{{ 'MENU.OPEN' | translate }}\"\r\n tooltipPosition=\"top\">\r\n <img src=\"/assets/logo.png\" alt=\"App Icon\" />\r\n </button>\r\n}\r\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}.tree-back-btn{height:2.25rem;padding:0 .35rem 0 .75rem;display:inline-flex;align-items:center;gap:.35rem;color:#64748b;font-weight:700;border-radius:999px;transition:all .2s ease}.tree-header{display:flex;align-items:center;justify-content:space-between;gap:.75rem}.tree-back-btn{height:2.35rem;padding:0 .45rem 0 .85rem;display:inline-flex;align-items:center;gap:.4rem;color:#64748b;font-size:.86rem;font-weight:700;border-radius:999px;transition:all .22s ease}.tree-back-btn:hover{color:var(--p-primary-600);background:var(--p-primary-50)}.tree-back-btn i{font-size:.85rem}.tree-header-actions{display:inline-flex;align-items:center;gap:.3rem}.tree-mini-btn,.tree-search-toggle{width:2rem;height:2rem;border-radius:.8rem;display:inline-flex;align-items:center;justify-content:center;color:#64748b;background:#f8fafcd9;border:1px solid rgba(148,163,184,.24);transition:all .2s ease}.tree-mini-btn i,.tree-search-toggle i{font-size:.72rem}.tree-mini-btn:hover,.tree-search-toggle:hover{color:var(--p-primary-600);background:var(--p-primary-50);border-color:rgba(var(--p-primary-500-rgb),.25);transform:translateY(-1px)}.tree-search-toggle{color:var(--p-primary-600);background:rgba(var(--p-primary-500-rgb),.08);border-color:rgba(var(--p-primary-500-rgb),.18)}.tree-search-toggle.active{color:#fff;background:linear-gradient(135deg,var(--p-primary-500),var(--p-primary-600));border-color:transparent;box-shadow:0 8px 18px rgba(var(--p-primary-500-rgb),.22)}.tree-search-panel{padding:.65rem;border-radius:1.15rem;background:#ffffff94;border:1px solid rgba(255,255,255,.45);box-shadow:0 10px 30px #0f172a14;backdrop-filter:blur(14px) saturate(160%);-webkit-backdrop-filter:blur(14px) saturate(160%)}.tree-search{position:relative;display:flex;align-items:center}.tree-search i.pi-search{position:absolute;right:.85rem;color:var(--p-primary-500);font-size:.85rem;z-index:1}.tree-search-input{width:100%;height:2.45rem;padding:0 2.35rem 0 2.2rem;border-radius:999px;border:1px solid rgba(148,163,184,.35);background:#ffffffd1;color:#334155;font-size:.86rem;outline:none;transition:all .25s ease}.tree-search-input:focus{border-color:var(--p-primary-400);box-shadow:0 0 0 4px rgba(var(--p-primary-500-rgb),.12);background:#fff}.tree-search-clear{position:absolute;left:.55rem;width:1.75rem;height:1.75rem;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;color:#64748b;background:#f1f5f9e6;transition:all .2s ease}.tree-search-clear:hover{color:var(--p-primary-600);background:var(--p-primary-50);transform:scale(1.05)}.tree-search-icon{position:absolute;right:.85rem;color:#94a3b8;font-size:.85rem;pointer-events:none}.tree-search-input{width:100%;height:2.45rem;padding:0 2.35rem 0 2.2rem;border-radius:.95rem;border:1px solid rgba(148,163,184,.28);background:#f8fafcd1;color:#334155;font-size:.86rem;outline:none;transition:all .22s ease}.tree-search-input:focus{border-color:rgba(var(--p-primary-500-rgb),.45);background:#fff;box-shadow:0 0 0 3px rgba(var(--p-primary-500-rgb),.1)}.tree-search-input::placeholder{color:#94a3b8}.tree-search-clear{position:absolute;left:.55rem;width:1.65rem;height:1.65rem;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;color:#94a3b8;background:transparent;transition:all .2s ease}.tree-search-clear:hover{color:var(--p-primary-600);background:var(--p-primary-50)}.tree-search-clear i{font-size:.7rem}.dark .tree-back-btn{color:#cbd5e1}.dark .tree-back-btn:hover{color:var(--p-primary-300);background:rgba(var(--p-primary-500-rgb),.12)}.dark .tree-header-actions{background:#1f2937b8;border-color:#94a3b824}.dark .tree-icon-btn{color:#cbd5e1}.dark .tree-icon-btn:hover{color:#fff;background:var(--p-primary-600)}.dark .tree-search-icon{color:#64748b}.dark .tree-search-input{background:#1f2937bd;border-color:#94a3b829;color:#e5e7eb}.dark .tree-search-input:focus{background:#111827eb;border-color:rgba(var(--p-primary-500-rgb),.48)}.dark .tree-search-input::placeholder{color:#64748b}.dark .tree-search-clear{color:#94a3b8}.dark .tree-search-clear:hover{color:var(--p-primary-300);background:rgba(var(--p-primary-500-rgb),.12)}.search-match{background:linear-gradient(to left,rgba(var(--p-primary-500-rgb),.16),transparent)!important;color:var(--p-primary-700)!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!important}.search-match i{color:var(--p-primary-600)!important}.dark .tree-toolbar{background:#111827b8;border-color:#ffffff14}.dark .tree-search-input{background:#1f2937cc;border-color:#94a3b82e;color:#e5e7eb}.dark .tree-search-input:focus{background:#111827f2}.dark .tree-search-clear{background:#374151e6;color:#cbd5e1}.dark .tree-action-btn{background:rgba(var(--p-primary-500-rgb),.12);color:var(--p-primary-300);border-color:rgba(var(--p-primary-500-rgb),.22)}.dark .tree-action-btn:hover{background:var(--p-primary-600);color:#fff}.dark .search-match{background:rgba(var(--p-primary-500-rgb),.18)!important;color:var(--p-primary-300)!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$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2$1.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.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: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: // ✅ أضف هذا
1512
- TooltipModule }, { kind: "directive", type: i4$1.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: "ngmodule", type: ButtonModule }, { 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" }] });
1513
1568
  }
1514
1569
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesComponent, decorators: [{
1515
1570
  type: Component,
1516
1571
  args: [{ selector: 'app-routes', standalone: true, imports: [
1517
1572
  CommonModule,
1518
1573
  RouterModule,
1519
- FormsModule, // ✅ أضف هذا
1520
1574
  TooltipModule,
1521
1575
  TranslatePipe,
1522
- StyleClassModule,
1523
- ButtonModule
1524
- ], template: "<aside class=\"flex h-screen bg-white dark:bg-gray-900 transition-all duration-300\"\r\n [ngClass]=\"{ 'w-0 overflow-hidden': !sidebarVisible && isMobile }\">\r\n\r\n <!-- \uD83C\uDFA8 \u0627\u0644\u0634\u0631\u064A\u0637 \u0627\u0644\u062C\u0627\u0646\u0628\u064A \u0627\u0644\u0631\u0626\u064A\u0633\u064A -->\r\n <div\r\n class=\"flex flex-col justify-between pt-4 pb-2 w-16 border-l shadow-xl\r\n bg-gradient-to-b from-primary-500 via-primary-600 to-primary-700\r\n text-white rounded-r-3xl\">\r\n\r\n <!-- \uD83D\uDD1D \u0627\u0644\u0623\u0639\u0644\u0649 -->\r\n <div class=\"flex flex-col items-center gap-4\">\r\n <img src=\"/assets/logo.png\" alt=\"Logo\"\r\n class=\"w-9 h-9 mt-1 rounded-lg shadow-sm\"/>\r\n\r\n <!-- \u0632\u0631 \u0627\u0644\u0642\u0627\u0626\u0645\u0629 -->\r\n <!-- [pTooltip]=\"'TOGGLE_MENU' | translate\" -->\r\n <button (click)=\"toggleAll()\"\r\n tooltipPosition=\"right\"\r\n class=\"text-white hover:scale-110 transition-transform duration-200\">\r\n <i class=\"pi pi-bars text-lg\"></i>\r\n </button>\r\n\r\n <!-- \u0623\u064A\u0642\u0648\u0646\u0627\u062A -->\r\n @for (item of secondToLastLevel$ | async; track item) {\r\n <div class=\"relative flex flex-col items-center\">\r\n <!-- \u0627\u0644\u0623\u064A\u0642\u0648\u0646\u0629 -->\r\n <div (click)=\"onMainClick(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\r\n hover:bg-white/20 transition-all duration-200\"\r\n [ngClass]=\"{ 'bg-white/30 scale-105 shadow-inner': item.expanded }\">\r\n <i [class]=\"item.icon + ' text-xl'\"></i>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- \u2699\uFE0F \u0627\u0644\u0623\u0633\u0641\u0644 -->\r\n <div class=\"flex flex-col items-center pb-3 text-white/80\">\r\n <i class=\"pi pi-cog text-lg hover:text-white cursor-pointer transition\"></i>\r\n </div>\r\n </div>\r\n\r\n <!-- \uD83D\uDCCB \u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0641\u0631\u0639\u064A\u0629 -->\r\n @if (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\r\n 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': isMobile }\">\r\n <!-- \u0632\u0631 \u0631\u062C\u0648\u0639 -->\r\n <!-- \uD83D\uDD1D \u0631\u0623\u0633 \u0627\u0644\u0634\u062C\u0631\u0629 -->\r\n<!-- \uD83D\uDD1D \u0631\u0623\u0633 \u0627\u0644\u0634\u062C\u0631\u0629 -->\r\n<div class=\"tree-header mb-1\">\r\n\r\n <button\r\n type=\"button\"\r\n (click)=\"closeMenu()\"\r\n class=\"tree-back-btn\">\r\n <i class=\"pi pi-angle-right\"></i>\r\n <span>{{ 'back' | translate }}</span>\r\n </button>\r\n\r\n <div class=\"tree-header-actions\">\r\n\r\n <button\r\n type=\"button\"\r\n class=\"tree-mini-btn\"\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 <i class=\"pi pi-plus\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"tree-mini-btn\"\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 <i class=\"pi pi-minus\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"tree-search-toggle\"\r\n [ngClass]=\"{ 'active': showTreeSearch }\"\r\n (click)=\"toggleTreeSearch()\"\r\n pTooltip=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A\"\r\n tooltipPosition=\"top\">\r\n <i class=\"pi pi-search\"></i>\r\n </button>\r\n\r\n </div>\r\n\r\n</div>\r\n\r\n@if (showTreeSearch) {\r\n <div class=\"tree-search-panel mb-4 animate-slideIn\">\r\n <div class=\"tree-search\">\r\n <i class=\"pi pi-search\"></i>\r\n\r\n <input\r\n type=\"text\"\r\n [(ngModel)]=\"searchText\"\r\n (input)=\"onSearchChange()\"\r\n placeholder=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A...\"\r\n class=\"tree-search-input\" />\r\n\r\n @if (searchText) {\r\n <button\r\n type=\"button\"\r\n class=\"tree-search-clear\"\r\n (click)=\"clearSearch()\">\r\n <i class=\"pi pi-times\"></i>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n}\r\n\r\n\r\n <!-- Recursive rendering -->\r\n @if (menuItems$ | async; as menuItems) {\r\n <ng-container *ngTemplateOutlet=\"renderMenu; context:{ $implicit: menuItems, level: 1 }\"></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\r\n @if (!item.items?.length) {\r\n @let link = getLinkProps(item);\r\n <div class=\"mb-1\">\r\n @if (link.isExternal) {\r\n <a [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 <i [class]=\"item.icon + ' text-base'\"></i>\r\n <span class=\"min-w-0 break-words whitespace-normal\">{{ item.label | translate }}</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 [routerLink]=\"link.routerLink\"\r\n (click)=\"onNavigate()\"\r\n routerLinkActive=\"active-link\"\r\n [ngClass]=\"{ 'search-match': 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 <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 </a>\r\n\r\n@if (link.addRoute) {\r\n <a\r\n [routerLink]=\"[link.addRoute]\"\r\n (click)=\"$event.stopPropagation(); onNavigate()\"\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 <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\r\n <div\r\n (click)=\"onParentClick(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': item.expanded,\r\n 'search-match': 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\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n\r\n <span>\r\n {{ item.label | translate }}\r\n </span>\r\n\r\n </div>\r\n\r\n <i\r\n class=\"pi\"\r\n [ngClass]=\"item.expanded ? 'pi-chevron-down' : 'pi-chevron-left'\"\r\n style=\"font-size:0.6rem;\">\r\n </i>\r\n\r\n </div>\r\n\r\n @if (item.expanded) {\r\n <div 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=\"renderMenu; context:{ $implicit: item.items, level: level + 1 }\">\r\n </ng-container>\r\n\r\n </div>\r\n }\r\n\r\n </div>\r\n }\r\n\r\n }\r\n</ng-template>\r\n\r\n </div>\r\n}\r\n\r\n</aside>\r\n\r\n<!-- \uD83C\uDF1F \u0627\u0644\u0632\u0631 \u0627\u0644\u0639\u0627\u0626\u0645 \u0627\u0644\u062D\u062F\u064A\u062B -->\r\n@if (isMobile && !sidebarVisible) {\r\n <button\r\n class=\"floating-btn\"\r\n (click)=\"openMenu()\"\r\n pTooltip=\"{{ 'MENU.OPEN' | translate }}\"\r\n tooltipPosition=\"top\">\r\n <img src=\"/assets/logo.png\" alt=\"App Icon\" />\r\n </button>\r\n}\r\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}.tree-back-btn{height:2.25rem;padding:0 .35rem 0 .75rem;display:inline-flex;align-items:center;gap:.35rem;color:#64748b;font-weight:700;border-radius:999px;transition:all .2s ease}.tree-header{display:flex;align-items:center;justify-content:space-between;gap:.75rem}.tree-back-btn{height:2.35rem;padding:0 .45rem 0 .85rem;display:inline-flex;align-items:center;gap:.4rem;color:#64748b;font-size:.86rem;font-weight:700;border-radius:999px;transition:all .22s ease}.tree-back-btn:hover{color:var(--p-primary-600);background:var(--p-primary-50)}.tree-back-btn i{font-size:.85rem}.tree-header-actions{display:inline-flex;align-items:center;gap:.3rem}.tree-mini-btn,.tree-search-toggle{width:2rem;height:2rem;border-radius:.8rem;display:inline-flex;align-items:center;justify-content:center;color:#64748b;background:#f8fafcd9;border:1px solid rgba(148,163,184,.24);transition:all .2s ease}.tree-mini-btn i,.tree-search-toggle i{font-size:.72rem}.tree-mini-btn:hover,.tree-search-toggle:hover{color:var(--p-primary-600);background:var(--p-primary-50);border-color:rgba(var(--p-primary-500-rgb),.25);transform:translateY(-1px)}.tree-search-toggle{color:var(--p-primary-600);background:rgba(var(--p-primary-500-rgb),.08);border-color:rgba(var(--p-primary-500-rgb),.18)}.tree-search-toggle.active{color:#fff;background:linear-gradient(135deg,var(--p-primary-500),var(--p-primary-600));border-color:transparent;box-shadow:0 8px 18px rgba(var(--p-primary-500-rgb),.22)}.tree-search-panel{padding:.65rem;border-radius:1.15rem;background:#ffffff94;border:1px solid rgba(255,255,255,.45);box-shadow:0 10px 30px #0f172a14;backdrop-filter:blur(14px) saturate(160%);-webkit-backdrop-filter:blur(14px) saturate(160%)}.tree-search{position:relative;display:flex;align-items:center}.tree-search i.pi-search{position:absolute;right:.85rem;color:var(--p-primary-500);font-size:.85rem;z-index:1}.tree-search-input{width:100%;height:2.45rem;padding:0 2.35rem 0 2.2rem;border-radius:999px;border:1px solid rgba(148,163,184,.35);background:#ffffffd1;color:#334155;font-size:.86rem;outline:none;transition:all .25s ease}.tree-search-input:focus{border-color:var(--p-primary-400);box-shadow:0 0 0 4px rgba(var(--p-primary-500-rgb),.12);background:#fff}.tree-search-clear{position:absolute;left:.55rem;width:1.75rem;height:1.75rem;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;color:#64748b;background:#f1f5f9e6;transition:all .2s ease}.tree-search-clear:hover{color:var(--p-primary-600);background:var(--p-primary-50);transform:scale(1.05)}.tree-search-icon{position:absolute;right:.85rem;color:#94a3b8;font-size:.85rem;pointer-events:none}.tree-search-input{width:100%;height:2.45rem;padding:0 2.35rem 0 2.2rem;border-radius:.95rem;border:1px solid rgba(148,163,184,.28);background:#f8fafcd1;color:#334155;font-size:.86rem;outline:none;transition:all .22s ease}.tree-search-input:focus{border-color:rgba(var(--p-primary-500-rgb),.45);background:#fff;box-shadow:0 0 0 3px rgba(var(--p-primary-500-rgb),.1)}.tree-search-input::placeholder{color:#94a3b8}.tree-search-clear{position:absolute;left:.55rem;width:1.65rem;height:1.65rem;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;color:#94a3b8;background:transparent;transition:all .2s ease}.tree-search-clear:hover{color:var(--p-primary-600);background:var(--p-primary-50)}.tree-search-clear i{font-size:.7rem}.dark .tree-back-btn{color:#cbd5e1}.dark .tree-back-btn:hover{color:var(--p-primary-300);background:rgba(var(--p-primary-500-rgb),.12)}.dark .tree-header-actions{background:#1f2937b8;border-color:#94a3b824}.dark .tree-icon-btn{color:#cbd5e1}.dark .tree-icon-btn:hover{color:#fff;background:var(--p-primary-600)}.dark .tree-search-icon{color:#64748b}.dark .tree-search-input{background:#1f2937bd;border-color:#94a3b829;color:#e5e7eb}.dark .tree-search-input:focus{background:#111827eb;border-color:rgba(var(--p-primary-500-rgb),.48)}.dark .tree-search-input::placeholder{color:#64748b}.dark .tree-search-clear{color:#94a3b8}.dark .tree-search-clear:hover{color:var(--p-primary-300);background:rgba(var(--p-primary-500-rgb),.12)}.search-match{background:linear-gradient(to left,rgba(var(--p-primary-500-rgb),.16),transparent)!important;color:var(--p-primary-700)!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!important}.search-match i{color:var(--p-primary-600)!important}.dark .tree-toolbar{background:#111827b8;border-color:#ffffff14}.dark .tree-search-input{background:#1f2937cc;border-color:#94a3b82e;color:#e5e7eb}.dark .tree-search-input:focus{background:#111827f2}.dark .tree-search-clear{background:#374151e6;color:#cbd5e1}.dark .tree-action-btn{background:rgba(var(--p-primary-500-rgb),.12);color:var(--p-primary-300);border-color:rgba(var(--p-primary-500-rgb),.22)}.dark .tree-action-btn:hover{background:var(--p-primary-600);color:#fff}.dark .search-match{background:rgba(var(--p-primary-500-rgb),.18)!important;color:var(--p-primary-300)!important}\n"] }]
1525
- }], ctorParameters: () => [], propDecorators: { onResize: [{
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: [{
1526
1579
  type: HostListener,
1527
1580
  args: ['window:resize']
1528
1581
  }] } });
@@ -1728,18 +1781,18 @@ class ApplicationLayoutComponent {
1728
1781
  catch (error) {
1729
1782
  }
1730
1783
  }
1731
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ApplicationLayoutComponent, deps: [{ token: LayoutService }, { token: i0.Renderer2 }, { token: i2$1.Router }, { token: i0.NgZone }, { token: i3$3.LocalizationService }], target: i0.ɵɵFactoryTarget.Component });
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 });
1732
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() */] });
1733
1786
  }
1734
1787
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ApplicationLayoutComponent, decorators: [{
1735
1788
  type: Component,
1736
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" }]
1737
- }], ctorParameters: () => [{ type: LayoutService }, { type: i0.Renderer2 }, { type: i2$1.Router }, { type: i0.NgZone }, { type: i3$3.LocalizationService }] });
1790
+ }], ctorParameters: () => [{ type: LayoutService }, { type: i0.Renderer2 }, { type: i2$2.Router }, { type: i0.NgZone }, { type: i3$3.LocalizationService }] });
1738
1791
 
1739
1792
  class EmptyLayoutComponent {
1740
1793
  static type = eLayoutType.empty;
1741
1794
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmptyLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1742
- 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$1.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] });
1795
+ 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"] }] });
1743
1796
  }
1744
1797
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmptyLayoutComponent, decorators: [{
1745
1798
  type: Component,