@es.framework/ng.ui.core 2.0.58 → 2.0.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,23 @@
1
1
  import * as i0 from '@angular/core';
2
- import { NgModule, inject, EventEmitter, Input, Output, Component } from '@angular/core';
2
+ import { NgModule, inject, EventEmitter, Input, Output, Component, signal, Injectable } from '@angular/core';
3
+ import * as i1 from '@angular/common';
3
4
  import { CommonModule } from '@angular/common';
4
- import { UntypedFormGroup } from '@angular/forms';
5
- import { BaseService } from '@es.framework/ng.core/services';
5
+ import * as i2 from '@angular/forms';
6
+ import { UntypedFormGroup, FormsModule } from '@angular/forms';
7
+ import { BaseService, SwalService } from '@es.framework/ng.core/services';
6
8
  import { GenericDialogComponent } from '@es.framework/ng.ui.core/generic-dialog';
9
+ import * as i7 from '@ngx-translate/core';
10
+ import { TranslateService, TranslateModule } from '@ngx-translate/core';
11
+ import * as i3 from 'primeng/button';
12
+ import { ButtonModule } from 'primeng/button';
13
+ import * as i4 from 'primeng/inputtext';
14
+ import { InputTextModule } from 'primeng/inputtext';
15
+ import * as i5 from 'primeng/tooltip';
16
+ import { TooltipModule } from 'primeng/tooltip';
17
+ import * as i6 from 'primeng/drawer';
18
+ import { DrawerModule } from 'primeng/drawer';
19
+ import { finalize, Subject } from 'rxjs';
20
+ import { LoadingSkeletonComponent } from '@es.framework/ng.ui.core/loading-skeletons';
7
21
 
8
22
  class FilterTemplatesModule {
9
23
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FilterTemplatesModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
@@ -376,11 +390,859 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
376
390
  type: Input
377
391
  }] } });
378
392
 
379
- // export * from './lib/report-templates-drawer.component';
393
+ class SavedReportTemplatesDrawerComponent {
394
+ svc = inject(BaseService);
395
+ swal = inject(SwalService);
396
+ tr = inject(TranslateService);
397
+ apiName = 'filter-templates';
398
+ module = 'crm';
399
+ service = '';
400
+ category = '';
401
+ feature = '';
402
+ templateSelected = new EventEmitter();
403
+ edit = new EventEmitter();
404
+ // ✅ جديد
405
+ add = new EventEmitter();
406
+ // ✅ التحكم من الأب
407
+ visible = false;
408
+ visibleChange = new EventEmitter();
409
+ // ✅ اختياري: خلّها قابلة للتخصيص من الأب
410
+ position = 'left';
411
+ showCloseIcon = true;
412
+ modal = true; // ✅ يغمّق اللي تحت
413
+ dismissable = true; // ✅ الضغط خارج/على الماسك يقفل
414
+ closeOnEscape = true; // ✅ ESC يقفل
415
+ blockScroll = true; // ✅ يمنع سكرول للخلفية
416
+ filterFields = [];
417
+ moduleName = '';
418
+ defaultFeature = '';
419
+ defaultCategory = '';
420
+ defaultPermission = '';
421
+ currentUserId = null;
422
+ templateEditorVisible = false;
423
+ templateEditorMode = 'create';
424
+ templateEditorModel = {};
425
+ templates = [];
426
+ selected = null;
427
+ loading = false;
428
+ search = '';
429
+ favOpen = true;
430
+ sharedOpen = true;
431
+ privateOpen = true;
432
+ ngOnInit() {
433
+ this.refresh();
434
+ }
435
+ refresh() {
436
+ this.loading = true;
437
+ this.svc.apiName = this.apiName;
438
+ this.svc.moduleName = this.module;
439
+ const filters = {
440
+ service: this.service,
441
+ module: this.module,
442
+ category: this.category,
443
+ };
444
+ this.svc.getList({ sorting: 'id DESC', filtering: { filters } })
445
+ .pipe(finalize(() => (this.loading = false)))
446
+ .subscribe({
447
+ next: (res) => {
448
+ this.templates = res?.items ?? res ?? [];
449
+ if (this.selected) {
450
+ this.selected =
451
+ this.templates.find(x => String(x.id) === String(this.selected.id)) ?? null;
452
+ }
453
+ },
454
+ error: () => this.swal.error(this.tr.instant('ERROR_TITLE'), this.tr.instant('ERROR.FAILED_LOAD_DATA')),
455
+ });
456
+ }
457
+ get filteredAll() {
458
+ const q = (this.search || '').trim().toLowerCase();
459
+ if (!q)
460
+ return [...this.templates];
461
+ return this.templates.filter(t => (this.getName(t) || '').toLowerCase().includes(q));
462
+ }
463
+ get favList() {
464
+ return this.filteredAll.filter(x => !!x.isFav);
465
+ }
466
+ get privateList() {
467
+ return this.filteredAll.filter(x => x.shareScope === 'ME');
468
+ }
469
+ get sharedList() {
470
+ return this.filteredAll.filter(x => x.shareScope !== 'ME' && !x.isFav);
471
+ }
472
+ select(t) {
473
+ if (this.isSelected(t)) {
474
+ this.clearSelection();
475
+ return;
476
+ }
477
+ this.selected = t;
478
+ this.templateSelected.emit(t);
479
+ }
480
+ isSelected(t) {
481
+ return this.selected && String(this.selected.id) === String(t.id);
482
+ }
483
+ getName(t) {
484
+ const lang = this.tr.currentLang || 'ar';
485
+ return (lang.startsWith('ar') ? (t.nameAr || t.nameEn) : (t.nameEn || t.nameAr)) || '';
486
+ }
487
+ toggleFav(t) {
488
+ const payload = { ...t, isFav: !t.isFav };
489
+ this.svc.update(String(t.id), payload).subscribe({
490
+ next: (u) => (t.isFav = u?.isFav ?? payload.isFav),
491
+ error: () => this.swal.error(this.tr.instant('ERROR_TITLE'), this.tr.instant('OPERATION_FAILED')),
492
+ });
493
+ }
494
+ confirmDelete(t) {
495
+ this.swal.confirm(this.tr.instant('DELETE_CONFIRM_TITLE'), this.tr.instant('DELETE_CONFIRM_MESSAGE'), this.tr.instant('COMMON.DELETE')).then((r) => {
496
+ if (!r?.isConfirmed)
497
+ return;
498
+ this.deleteTemplate(t);
499
+ });
500
+ }
501
+ deleteTemplate(t) {
502
+ // مهم: نفس إعدادات الاستدعاءات الثانية
503
+ this.svc.apiName = this.apiName;
504
+ this.svc.moduleName = this.module;
505
+ // ✅ استدعاء Endpoint الحذف
506
+ // ملاحظة: اسم الميثود يعتمد على BaseService عندكم:
507
+ // غالباً delete(id) أو remove(id)
508
+ const id = String(t.id);
509
+ this.svc.delete(id).subscribe({
510
+ next: () => {
511
+ // حدّث القائمة محلياً
512
+ this.templates = this.templates.filter(x => String(x.id) !== id);
513
+ // لو المحذوف هو المحدد
514
+ if (this.selected && String(this.selected.id) === id) {
515
+ this.selected = null;
516
+ this.templateSelected.emit(null);
517
+ }
518
+ this.swal.toast(this.tr.instant('RECORD_DELETED_SUCCESSFULLY'));
519
+ },
520
+ error: () => this.swal.error(this.tr.instant('ERROR_TITLE'), this.tr.instant('OPERATION_FAILED')),
521
+ });
522
+ }
523
+ onVisibleChange(v) {
524
+ this.visible = v;
525
+ this.visibleChange.emit(v);
526
+ }
527
+ clearSelection() {
528
+ this.selected = null;
529
+ this.templateSelected.emit(null); // ✅ يبلغ الأب: ما عاد فيه قالب
530
+ }
531
+ openTemplateCreate() {
532
+ this.templateEditorMode = 'create';
533
+ this.templateEditorModel = {
534
+ isActive: true,
535
+ shareScope: 'ALL',
536
+ };
537
+ this.templateEditorVisible = true;
538
+ }
539
+ openTemplateEdit(t) {
540
+ this.templateEditorMode = 'edit';
541
+ this.templateEditorModel = { ...t };
542
+ this.templateEditorVisible = true;
543
+ }
544
+ onTemplateSaved(saved) {
545
+ this.refresh(); // ✅ هو نفسه يحدث القائمة
546
+ this.templateEditorVisible = false;
547
+ }
548
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: SavedReportTemplatesDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
549
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: SavedReportTemplatesDrawerComponent, isStandalone: true, selector: "saved-report-templates-drawer", inputs: { apiName: "apiName", module: "module", service: "service", category: "category", feature: "feature", visible: "visible", position: "position", showCloseIcon: "showCloseIcon", modal: "modal", dismissable: "dismissable", closeOnEscape: "closeOnEscape", blockScroll: "blockScroll", filterFields: "filterFields", moduleName: "moduleName", defaultFeature: "defaultFeature", defaultCategory: "defaultCategory", defaultPermission: "defaultPermission", currentUserId: "currentUserId" }, outputs: { templateSelected: "templateSelected", edit: "edit", add: "add", visibleChange: "visibleChange" }, providers: [BaseService], ngImport: i0, template: `
550
+ <p-drawer
551
+ [visible]="visible"
552
+ (visibleChange)="onVisibleChange($event)"
553
+ [position]="position"
554
+ [modal]="modal"
555
+ [dismissible]="dismissable"
556
+ [closeOnEscape]="closeOnEscape"
557
+ [blockScroll]="blockScroll"
558
+ [showCloseIcon]="showCloseIcon"
559
+ >
560
+ <div class="h-full flex flex-col bg-white">
561
+
562
+ <!-- Header -->
563
+ <div class="sticky top-0 z-10 bg-white border-b border-gray-200 px-4 py-4">
564
+ <div class="flex items-center gap-3">
565
+ <div class="w-9 h-9 rounded-xl flex items-center justify-center bg-gray-100 text-gray-700">
566
+ <i class="pi pi-bookmark"></i>
567
+ </div>
568
+
569
+ <div class="flex-1 min-w-0">
570
+ <div class="text-xs text-gray-500">
571
+ {{ 'TEMPLATES' | translate }}
572
+ </div>
573
+ <div class="text-base font-semibold text-gray-900 truncate">
574
+ {{ selected ? getName(selected) : ('SELECT_TEMPLATE' | translate) }}
575
+ </div>
576
+ </div>
577
+
578
+ <button
579
+ pButton
580
+ type="button"
581
+ class="p-button-rounded p-button-sm p-button-text"
582
+ icon="pi pi-refresh"
583
+ (click)="refresh()"
584
+ [disabled]="loading"
585
+ pTooltip="{{ 'REFRESH' | translate }}"
586
+ tooltipPosition="bottom"
587
+ ></button>
588
+
589
+ <button
590
+ pButton
591
+ type="button"
592
+ class="p-button-rounded p-button-sm p-button-primary"
593
+ icon="pi pi-plus"
594
+ (click)="openTemplateCreate()"
595
+ [disabled]="loading"
596
+ pTooltip="{{ 'COMMON.ADD' | translate }}"
597
+ tooltipPosition="bottom"
598
+ ></button>
599
+ </div>
600
+
601
+ <!-- Search -->
602
+ <div class="mt-4">
603
+ <span class="p-input-icon-left w-full">
604
+ <i class="pi pi-search"></i>
605
+ <input
606
+ pInputText
607
+ class="w-full !rounded-xl"
608
+ [(ngModel)]="search"
609
+ [disabled]="loading"
610
+ [placeholder]="'SEARCH_FOR_A_VIEW' | translate"
611
+ />
612
+ </span>
613
+ </div>
614
+ </div>
615
+
616
+ <!-- Content -->
617
+ <div class="flex-1 overflow-auto px-3 py-3">
618
+
619
+ <!-- ✅ Loading -->
620
+ <div *ngIf="loading" class="py-4">
621
+ <app-loading-skeleton [itemsCount]="4"></app-loading-skeleton>
622
+ </div>
623
+
624
+ <!-- ✅ Empty -->
625
+ <div *ngIf="!loading && filteredAll.length === 0" class="text-sm text-gray-500 text-center py-12">
626
+ <div class="mx-auto mb-3 w-12 h-12 rounded-2xl bg-gray-100 flex items-center justify-center">
627
+ <i class="pi pi-inbox text-gray-500"></i>
628
+ </div>
629
+ {{ 'NO_DATA_FOUND' | translate }}
630
+ </div>
631
+
632
+ <!-- ✅ Content when not loading -->
633
+ <ng-container *ngIf="!loading">
634
+
635
+ <!-- Favorites -->
636
+ <ng-container *ngIf="favList.length > 0">
637
+ <button
638
+ type="button"
639
+ class="w-full flex items-center justify-between px-2 py-2 text-sm font-semibold text-gray-800"
640
+ (click)="favOpen = !favOpen"
641
+ >
642
+ <span class="flex items-center gap-2">
643
+ <i class="pi pi-star-fill text-yellow-500"></i>
644
+ {{ 'FAVOURITES' | translate }}
645
+ <span class="text-xs px-2 py-0.5 rounded-full bg-gray-100 text-gray-600">{{ favList.length }}</span>
646
+ </span>
647
+ <i class="pi text-xs" [ngClass]="favOpen ? 'pi-chevron-up' : 'pi-chevron-down'"></i>
648
+ </button>
649
+
650
+ <div *ngIf="favOpen" class="mt-1 space-y-1">
651
+ <ng-container *ngFor="let t of favList">
652
+ <div
653
+ class="group relative flex items-center gap-3 px-3 py-2 rounded-xl transition cursor-pointer
654
+ hover:bg-gray-50"
655
+ [style.backgroundColor]="isSelected(t) ? 'var(--p-primary-50, rgba(59,130,246,0.10))' : ''"
656
+ (click)="select(t)"
657
+ >
658
+ <span
659
+ class="absolute left-0 top-1 bottom-1 w-1 rounded-full"
660
+ [style.backgroundColor]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : 'transparent'"
661
+ ></span>
662
+
663
+ <i class="text-base shrink-0"
664
+ [ngClass]="t.icon || 'pi pi-check-circle'"
665
+ [style.color]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : ''"></i>
666
+
667
+ <span class="flex-1 truncate"
668
+ [class.font-semibold]="isSelected(t)"
669
+ [style.color]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : ''">
670
+ {{ getName(t) }}
671
+ </span>
672
+
673
+ <!-- Actions -->
674
+ <div class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition"
675
+ (click)="$event.stopPropagation()">
676
+ <button pButton type="button" class="p-button-text p-button-sm"
677
+ [icon]="t.isFav ? 'pi pi-star-fill' : 'pi pi-star'"
678
+ (click)="toggleFav(t)"></button>
679
+
680
+ @if (!isSelected(t)) {
681
+ <button pButton type="button" class="p-button-text p-button-sm"
682
+ icon="pi pi-pencil"
683
+ (click)="openTemplateEdit(t)"></button>
684
+
685
+ <button pButton type="button" class="p-button-text p-button-sm p-button-danger"
686
+ icon="pi pi-trash"
687
+ (click)="confirmDelete(t)"></button>
688
+ }
689
+ </div>
690
+ </div>
691
+ </ng-container>
692
+ </div>
693
+
694
+ <div class="my-3 border-b border-gray-200"></div>
695
+ </ng-container>
696
+
697
+ <!-- Shared -->
698
+ <ng-container *ngIf="sharedList.length > 0">
699
+ <button
700
+ type="button"
701
+ class="w-full flex items-center justify-between px-2 py-2 text-sm font-semibold text-gray-800"
702
+ (click)="sharedOpen = !sharedOpen"
703
+ >
704
+ <span class="flex items-center gap-2">
705
+ <i class="pi pi-users text-gray-600"></i>
706
+ {{ 'SHARED' | translate }}
707
+ <span class="text-xs px-2 py-0.5 rounded-full bg-gray-100 text-gray-600">{{ sharedList.length }}</span>
708
+ </span>
709
+ <i class="pi text-xs" [ngClass]="sharedOpen ? 'pi-chevron-up' : 'pi-chevron-down'"></i>
710
+ </button>
711
+
712
+ <div *ngIf="sharedOpen" class="mt-1 space-y-1">
713
+ <ng-container *ngFor="let t of sharedList">
714
+ <div
715
+ class="group relative flex items-center gap-3 px-3 py-2 rounded-xl transition cursor-pointer hover:bg-gray-50"
716
+ [style.backgroundColor]="isSelected(t) ? 'var(--p-primary-50, rgba(59,130,246,0.10))' : ''"
717
+ (click)="select(t)"
718
+ >
719
+ <span
720
+ class="absolute left-0 top-1 bottom-1 w-1 rounded-full"
721
+ [style.backgroundColor]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : 'transparent'"
722
+ ></span>
723
+
724
+ <i class="text-base shrink-0"
725
+ [ngClass]="t.icon || 'pi pi-check-circle'"
726
+ [style.color]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : ''"></i>
727
+
728
+ <span class="flex-1 truncate"
729
+ [class.font-semibold]="isSelected(t)"
730
+ [style.color]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : ''">
731
+ {{ getName(t) }}
732
+ </span>
733
+
734
+ <div class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition"
735
+ (click)="$event.stopPropagation()">
736
+ <button pButton type="button" class="p-button-text p-button-sm"
737
+ [icon]="t.isFav ? 'pi pi-star-fill' : 'pi pi-star'"
738
+ (click)="toggleFav(t)"></button>
739
+
740
+ @if (!isSelected(t)) {
741
+ <button pButton type="button" class="p-button-text p-button-sm"
742
+ icon="pi pi-pencil"
743
+ (click)="openTemplateEdit(t)"></button>
744
+
745
+ <button pButton type="button" class="p-button-text p-button-sm p-button-danger"
746
+ icon="pi pi-trash"
747
+ (click)="confirmDelete(t)"></button>
748
+ }
749
+ </div>
750
+ </div>
751
+ </ng-container>
752
+ </div>
753
+
754
+ <div class="my-3 border-b border-gray-200"></div>
755
+ </ng-container>
756
+
757
+ <!-- Private -->
758
+ <ng-container *ngIf="privateList.length > 0">
759
+ <button
760
+ type="button"
761
+ class="w-full flex items-center justify-between px-2 py-2 text-sm font-semibold text-gray-800"
762
+ (click)="privateOpen = !privateOpen"
763
+ >
764
+ <span class="flex items-center gap-2">
765
+ <i class="pi pi-lock text-gray-600"></i>
766
+ {{ 'PRIVATE' | translate }}
767
+ <span class="text-xs px-2 py-0.5 rounded-full bg-gray-100 text-gray-600">{{ privateList.length }}</span>
768
+ </span>
769
+ <i class="pi text-xs" [ngClass]="privateOpen ? 'pi-chevron-up' : 'pi-chevron-down'"></i>
770
+ </button>
771
+
772
+ <div *ngIf="privateOpen" class="mt-1 space-y-1">
773
+ <ng-container *ngFor="let t of privateList">
774
+ <div
775
+ class="group relative flex items-center gap-3 px-3 py-2 rounded-xl transition cursor-pointer hover:bg-gray-50"
776
+ [style.backgroundColor]="isSelected(t) ? 'var(--p-primary-50, rgba(59,130,246,0.10))' : ''"
777
+ (click)="select(t)"
778
+ >
779
+ <span
780
+ class="absolute left-0 top-1 bottom-1 w-1 rounded-full"
781
+ [style.backgroundColor]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : 'transparent'"
782
+ ></span>
783
+
784
+ <i class="text-base shrink-0"
785
+ [ngClass]="t.icon || 'pi pi-check-circle'"
786
+ [style.color]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : ''"></i>
787
+
788
+ <span class="flex-1 truncate"
789
+ [class.font-semibold]="isSelected(t)"
790
+ [style.color]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : ''">
791
+ {{ getName(t) }}
792
+ </span>
793
+
794
+ <div class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition"
795
+ (click)="$event.stopPropagation()">
796
+ <button pButton type="button" class="p-button-text p-button-sm"
797
+ [icon]="t.isFav ? 'pi pi-star-fill' : 'pi pi-star'"
798
+ (click)="toggleFav(t)"></button>
799
+
800
+ @if (!isSelected(t)) {
801
+ <button pButton type="button" class="p-button-text p-button-sm"
802
+ icon="pi pi-pencil"
803
+ (click)="openTemplateEdit(t)"></button>
804
+
805
+ <button pButton type="button" class="p-button-text p-button-sm p-button-danger"
806
+ icon="pi pi-trash"
807
+ (click)="confirmDelete(t)"></button>
808
+ }
809
+ </div>
810
+ </div>
811
+ </ng-container>
812
+ </div>
813
+ </ng-container>
814
+
815
+ </ng-container>
816
+ </div>
817
+ </div>
818
+ </p-drawer>
819
+
820
+ <filter-template-editor-dialog
821
+ [(visible)]="templateEditorVisible"
822
+ [mode]="templateEditorMode"
823
+ [model]="templateEditorModel"
824
+ [filterFields]="filterFields"
825
+ [moduleName]="moduleName"
826
+ [apiName]="apiName"
827
+ [defaultFeature]="defaultFeature"
828
+ [defaultCategory]="defaultCategory"
829
+ [defaultPermission]="defaultPermission"
830
+ [currentUserId]="currentUserId"
831
+ (saved)="onTemplateSaved($event)">
832
+ </filter-template-editor-dialog>
833
+
834
+
835
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i3.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5.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: DrawerModule }, { kind: "component", type: i6.Drawer, selector: "p-drawer", inputs: ["appendTo", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "component", type: LoadingSkeletonComponent, selector: "app-loading-skeleton", inputs: ["itemsCount"] }, { kind: "component", type: FilterTemplateEditorDialogComponent, selector: "filter-template-editor-dialog", inputs: ["visible", "mode", "model", "filterFields", "apiName", "moduleName", "idField", "defaultFeature", "defaultPermission", "defaultCategory", "dialogMaxWidth", "currentUserId"], outputs: ["visibleChange", "saved"] }, { kind: "pipe", type: i7.TranslatePipe, name: "translate" }] });
836
+ }
837
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: SavedReportTemplatesDrawerComponent, decorators: [{
838
+ type: Component,
839
+ args: [{
840
+ selector: 'saved-report-templates-drawer',
841
+ standalone: true,
842
+ imports: [
843
+ CommonModule,
844
+ FormsModule,
845
+ TranslateModule,
846
+ ButtonModule,
847
+ InputTextModule,
848
+ TooltipModule,
849
+ DrawerModule,
850
+ LoadingSkeletonComponent,
851
+ FilterTemplateEditorDialogComponent
852
+ ],
853
+ providers: [BaseService],
854
+ template: `
855
+ <p-drawer
856
+ [visible]="visible"
857
+ (visibleChange)="onVisibleChange($event)"
858
+ [position]="position"
859
+ [modal]="modal"
860
+ [dismissible]="dismissable"
861
+ [closeOnEscape]="closeOnEscape"
862
+ [blockScroll]="blockScroll"
863
+ [showCloseIcon]="showCloseIcon"
864
+ >
865
+ <div class="h-full flex flex-col bg-white">
866
+
867
+ <!-- Header -->
868
+ <div class="sticky top-0 z-10 bg-white border-b border-gray-200 px-4 py-4">
869
+ <div class="flex items-center gap-3">
870
+ <div class="w-9 h-9 rounded-xl flex items-center justify-center bg-gray-100 text-gray-700">
871
+ <i class="pi pi-bookmark"></i>
872
+ </div>
873
+
874
+ <div class="flex-1 min-w-0">
875
+ <div class="text-xs text-gray-500">
876
+ {{ 'TEMPLATES' | translate }}
877
+ </div>
878
+ <div class="text-base font-semibold text-gray-900 truncate">
879
+ {{ selected ? getName(selected) : ('SELECT_TEMPLATE' | translate) }}
880
+ </div>
881
+ </div>
882
+
883
+ <button
884
+ pButton
885
+ type="button"
886
+ class="p-button-rounded p-button-sm p-button-text"
887
+ icon="pi pi-refresh"
888
+ (click)="refresh()"
889
+ [disabled]="loading"
890
+ pTooltip="{{ 'REFRESH' | translate }}"
891
+ tooltipPosition="bottom"
892
+ ></button>
893
+
894
+ <button
895
+ pButton
896
+ type="button"
897
+ class="p-button-rounded p-button-sm p-button-primary"
898
+ icon="pi pi-plus"
899
+ (click)="openTemplateCreate()"
900
+ [disabled]="loading"
901
+ pTooltip="{{ 'COMMON.ADD' | translate }}"
902
+ tooltipPosition="bottom"
903
+ ></button>
904
+ </div>
905
+
906
+ <!-- Search -->
907
+ <div class="mt-4">
908
+ <span class="p-input-icon-left w-full">
909
+ <i class="pi pi-search"></i>
910
+ <input
911
+ pInputText
912
+ class="w-full !rounded-xl"
913
+ [(ngModel)]="search"
914
+ [disabled]="loading"
915
+ [placeholder]="'SEARCH_FOR_A_VIEW' | translate"
916
+ />
917
+ </span>
918
+ </div>
919
+ </div>
920
+
921
+ <!-- Content -->
922
+ <div class="flex-1 overflow-auto px-3 py-3">
923
+
924
+ <!-- ✅ Loading -->
925
+ <div *ngIf="loading" class="py-4">
926
+ <app-loading-skeleton [itemsCount]="4"></app-loading-skeleton>
927
+ </div>
928
+
929
+ <!-- ✅ Empty -->
930
+ <div *ngIf="!loading && filteredAll.length === 0" class="text-sm text-gray-500 text-center py-12">
931
+ <div class="mx-auto mb-3 w-12 h-12 rounded-2xl bg-gray-100 flex items-center justify-center">
932
+ <i class="pi pi-inbox text-gray-500"></i>
933
+ </div>
934
+ {{ 'NO_DATA_FOUND' | translate }}
935
+ </div>
936
+
937
+ <!-- ✅ Content when not loading -->
938
+ <ng-container *ngIf="!loading">
939
+
940
+ <!-- Favorites -->
941
+ <ng-container *ngIf="favList.length > 0">
942
+ <button
943
+ type="button"
944
+ class="w-full flex items-center justify-between px-2 py-2 text-sm font-semibold text-gray-800"
945
+ (click)="favOpen = !favOpen"
946
+ >
947
+ <span class="flex items-center gap-2">
948
+ <i class="pi pi-star-fill text-yellow-500"></i>
949
+ {{ 'FAVOURITES' | translate }}
950
+ <span class="text-xs px-2 py-0.5 rounded-full bg-gray-100 text-gray-600">{{ favList.length }}</span>
951
+ </span>
952
+ <i class="pi text-xs" [ngClass]="favOpen ? 'pi-chevron-up' : 'pi-chevron-down'"></i>
953
+ </button>
954
+
955
+ <div *ngIf="favOpen" class="mt-1 space-y-1">
956
+ <ng-container *ngFor="let t of favList">
957
+ <div
958
+ class="group relative flex items-center gap-3 px-3 py-2 rounded-xl transition cursor-pointer
959
+ hover:bg-gray-50"
960
+ [style.backgroundColor]="isSelected(t) ? 'var(--p-primary-50, rgba(59,130,246,0.10))' : ''"
961
+ (click)="select(t)"
962
+ >
963
+ <span
964
+ class="absolute left-0 top-1 bottom-1 w-1 rounded-full"
965
+ [style.backgroundColor]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : 'transparent'"
966
+ ></span>
967
+
968
+ <i class="text-base shrink-0"
969
+ [ngClass]="t.icon || 'pi pi-check-circle'"
970
+ [style.color]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : ''"></i>
971
+
972
+ <span class="flex-1 truncate"
973
+ [class.font-semibold]="isSelected(t)"
974
+ [style.color]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : ''">
975
+ {{ getName(t) }}
976
+ </span>
977
+
978
+ <!-- Actions -->
979
+ <div class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition"
980
+ (click)="$event.stopPropagation()">
981
+ <button pButton type="button" class="p-button-text p-button-sm"
982
+ [icon]="t.isFav ? 'pi pi-star-fill' : 'pi pi-star'"
983
+ (click)="toggleFav(t)"></button>
984
+
985
+ @if (!isSelected(t)) {
986
+ <button pButton type="button" class="p-button-text p-button-sm"
987
+ icon="pi pi-pencil"
988
+ (click)="openTemplateEdit(t)"></button>
989
+
990
+ <button pButton type="button" class="p-button-text p-button-sm p-button-danger"
991
+ icon="pi pi-trash"
992
+ (click)="confirmDelete(t)"></button>
993
+ }
994
+ </div>
995
+ </div>
996
+ </ng-container>
997
+ </div>
998
+
999
+ <div class="my-3 border-b border-gray-200"></div>
1000
+ </ng-container>
1001
+
1002
+ <!-- Shared -->
1003
+ <ng-container *ngIf="sharedList.length > 0">
1004
+ <button
1005
+ type="button"
1006
+ class="w-full flex items-center justify-between px-2 py-2 text-sm font-semibold text-gray-800"
1007
+ (click)="sharedOpen = !sharedOpen"
1008
+ >
1009
+ <span class="flex items-center gap-2">
1010
+ <i class="pi pi-users text-gray-600"></i>
1011
+ {{ 'SHARED' | translate }}
1012
+ <span class="text-xs px-2 py-0.5 rounded-full bg-gray-100 text-gray-600">{{ sharedList.length }}</span>
1013
+ </span>
1014
+ <i class="pi text-xs" [ngClass]="sharedOpen ? 'pi-chevron-up' : 'pi-chevron-down'"></i>
1015
+ </button>
1016
+
1017
+ <div *ngIf="sharedOpen" class="mt-1 space-y-1">
1018
+ <ng-container *ngFor="let t of sharedList">
1019
+ <div
1020
+ class="group relative flex items-center gap-3 px-3 py-2 rounded-xl transition cursor-pointer hover:bg-gray-50"
1021
+ [style.backgroundColor]="isSelected(t) ? 'var(--p-primary-50, rgba(59,130,246,0.10))' : ''"
1022
+ (click)="select(t)"
1023
+ >
1024
+ <span
1025
+ class="absolute left-0 top-1 bottom-1 w-1 rounded-full"
1026
+ [style.backgroundColor]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : 'transparent'"
1027
+ ></span>
1028
+
1029
+ <i class="text-base shrink-0"
1030
+ [ngClass]="t.icon || 'pi pi-check-circle'"
1031
+ [style.color]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : ''"></i>
1032
+
1033
+ <span class="flex-1 truncate"
1034
+ [class.font-semibold]="isSelected(t)"
1035
+ [style.color]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : ''">
1036
+ {{ getName(t) }}
1037
+ </span>
1038
+
1039
+ <div class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition"
1040
+ (click)="$event.stopPropagation()">
1041
+ <button pButton type="button" class="p-button-text p-button-sm"
1042
+ [icon]="t.isFav ? 'pi pi-star-fill' : 'pi pi-star'"
1043
+ (click)="toggleFav(t)"></button>
1044
+
1045
+ @if (!isSelected(t)) {
1046
+ <button pButton type="button" class="p-button-text p-button-sm"
1047
+ icon="pi pi-pencil"
1048
+ (click)="openTemplateEdit(t)"></button>
1049
+
1050
+ <button pButton type="button" class="p-button-text p-button-sm p-button-danger"
1051
+ icon="pi pi-trash"
1052
+ (click)="confirmDelete(t)"></button>
1053
+ }
1054
+ </div>
1055
+ </div>
1056
+ </ng-container>
1057
+ </div>
1058
+
1059
+ <div class="my-3 border-b border-gray-200"></div>
1060
+ </ng-container>
1061
+
1062
+ <!-- Private -->
1063
+ <ng-container *ngIf="privateList.length > 0">
1064
+ <button
1065
+ type="button"
1066
+ class="w-full flex items-center justify-between px-2 py-2 text-sm font-semibold text-gray-800"
1067
+ (click)="privateOpen = !privateOpen"
1068
+ >
1069
+ <span class="flex items-center gap-2">
1070
+ <i class="pi pi-lock text-gray-600"></i>
1071
+ {{ 'PRIVATE' | translate }}
1072
+ <span class="text-xs px-2 py-0.5 rounded-full bg-gray-100 text-gray-600">{{ privateList.length }}</span>
1073
+ </span>
1074
+ <i class="pi text-xs" [ngClass]="privateOpen ? 'pi-chevron-up' : 'pi-chevron-down'"></i>
1075
+ </button>
1076
+
1077
+ <div *ngIf="privateOpen" class="mt-1 space-y-1">
1078
+ <ng-container *ngFor="let t of privateList">
1079
+ <div
1080
+ class="group relative flex items-center gap-3 px-3 py-2 rounded-xl transition cursor-pointer hover:bg-gray-50"
1081
+ [style.backgroundColor]="isSelected(t) ? 'var(--p-primary-50, rgba(59,130,246,0.10))' : ''"
1082
+ (click)="select(t)"
1083
+ >
1084
+ <span
1085
+ class="absolute left-0 top-1 bottom-1 w-1 rounded-full"
1086
+ [style.backgroundColor]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : 'transparent'"
1087
+ ></span>
1088
+
1089
+ <i class="text-base shrink-0"
1090
+ [ngClass]="t.icon || 'pi pi-check-circle'"
1091
+ [style.color]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : ''"></i>
1092
+
1093
+ <span class="flex-1 truncate"
1094
+ [class.font-semibold]="isSelected(t)"
1095
+ [style.color]="isSelected(t) ? 'var(--p-primary-color, #3b82f6)' : ''">
1096
+ {{ getName(t) }}
1097
+ </span>
1098
+
1099
+ <div class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition"
1100
+ (click)="$event.stopPropagation()">
1101
+ <button pButton type="button" class="p-button-text p-button-sm"
1102
+ [icon]="t.isFav ? 'pi pi-star-fill' : 'pi pi-star'"
1103
+ (click)="toggleFav(t)"></button>
1104
+
1105
+ @if (!isSelected(t)) {
1106
+ <button pButton type="button" class="p-button-text p-button-sm"
1107
+ icon="pi pi-pencil"
1108
+ (click)="openTemplateEdit(t)"></button>
1109
+
1110
+ <button pButton type="button" class="p-button-text p-button-sm p-button-danger"
1111
+ icon="pi pi-trash"
1112
+ (click)="confirmDelete(t)"></button>
1113
+ }
1114
+ </div>
1115
+ </div>
1116
+ </ng-container>
1117
+ </div>
1118
+ </ng-container>
1119
+
1120
+ </ng-container>
1121
+ </div>
1122
+ </div>
1123
+ </p-drawer>
1124
+
1125
+ <filter-template-editor-dialog
1126
+ [(visible)]="templateEditorVisible"
1127
+ [mode]="templateEditorMode"
1128
+ [model]="templateEditorModel"
1129
+ [filterFields]="filterFields"
1130
+ [moduleName]="moduleName"
1131
+ [apiName]="apiName"
1132
+ [defaultFeature]="defaultFeature"
1133
+ [defaultCategory]="defaultCategory"
1134
+ [defaultPermission]="defaultPermission"
1135
+ [currentUserId]="currentUserId"
1136
+ (saved)="onTemplateSaved($event)">
1137
+ </filter-template-editor-dialog>
1138
+
1139
+
1140
+ `,
1141
+ }]
1142
+ }], propDecorators: { apiName: [{
1143
+ type: Input
1144
+ }], module: [{
1145
+ type: Input
1146
+ }], service: [{
1147
+ type: Input
1148
+ }], category: [{
1149
+ type: Input
1150
+ }], feature: [{
1151
+ type: Input
1152
+ }], templateSelected: [{
1153
+ type: Output
1154
+ }], edit: [{
1155
+ type: Output
1156
+ }], add: [{
1157
+ type: Output
1158
+ }], visible: [{
1159
+ type: Input
1160
+ }], visibleChange: [{
1161
+ type: Output
1162
+ }], position: [{
1163
+ type: Input
1164
+ }], showCloseIcon: [{
1165
+ type: Input
1166
+ }], modal: [{
1167
+ type: Input
1168
+ }], dismissable: [{
1169
+ type: Input
1170
+ }], closeOnEscape: [{
1171
+ type: Input
1172
+ }], blockScroll: [{
1173
+ type: Input
1174
+ }], filterFields: [{
1175
+ type: Input
1176
+ }], moduleName: [{
1177
+ type: Input
1178
+ }], defaultFeature: [{
1179
+ type: Input
1180
+ }], defaultCategory: [{
1181
+ type: Input
1182
+ }], defaultPermission: [{
1183
+ type: Input
1184
+ }], currentUserId: [{
1185
+ type: Input
1186
+ }] } });
1187
+
1188
+ class CrudContextService {
1189
+ // استخدام Signals لأداء أسرع
1190
+ moduleName = signal('', ...(ngDevMode ? [{ debugName: "moduleName" }] : []));
1191
+ category = signal('', ...(ngDevMode ? [{ debugName: "category" }] : []));
1192
+ feature = signal('', ...(ngDevMode ? [{ debugName: "feature" }] : []));
1193
+ filters = signal([], ...(ngDevMode ? [{ debugName: "filters" }] : []));
1194
+ templateDrawerVisible = signal(false, ...(ngDevMode ? [{ debugName: "templateDrawerVisible" }] : []));
1195
+ // حدث يطلق عند اختيار قالب
1196
+ templateSelected$ = new Subject();
1197
+ openTemplates() { this.templateDrawerVisible.set(true); }
1198
+ closeTemplates() { this.templateDrawerVisible.set(false); }
1199
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CrudContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1200
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CrudContextService });
1201
+ }
1202
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CrudContextService, decorators: [{
1203
+ type: Injectable
1204
+ }] });
1205
+
1206
+ class ReportTemplatesWrapperComponent {
1207
+ context = inject(CrudContextService); // يحصل على نفس النسخة الخاصة بالجدول الأب
1208
+ onSelect(template) {
1209
+ this.context.templateSelected$.next(template);
1210
+ this.context.closeTemplates();
1211
+ }
1212
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ReportTemplatesWrapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1213
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: ReportTemplatesWrapperComponent, isStandalone: true, selector: "app-report-templates-wrapper", ngImport: i0, template: `
1214
+ <saved-report-templates-drawer
1215
+ [visible]="context.templateDrawerVisible()"
1216
+ [filterFields]="context.filters()"
1217
+ [service]="context.moduleName()"
1218
+ [category]="context.category()"
1219
+ [feature]="context.feature()"
1220
+ (templateSelected)="onSelect($event)">
1221
+ </saved-report-templates-drawer>
1222
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: SavedReportTemplatesDrawerComponent, selector: "saved-report-templates-drawer", inputs: ["apiName", "module", "service", "category", "feature", "visible", "position", "showCloseIcon", "modal", "dismissable", "closeOnEscape", "blockScroll", "filterFields", "moduleName", "defaultFeature", "defaultCategory", "defaultPermission", "currentUserId"], outputs: ["templateSelected", "edit", "add", "visibleChange"] }] });
1223
+ }
1224
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ReportTemplatesWrapperComponent, decorators: [{
1225
+ type: Component,
1226
+ args: [{
1227
+ selector: 'app-report-templates-wrapper',
1228
+ standalone: true,
1229
+ imports: [CommonModule, SavedReportTemplatesDrawerComponent],
1230
+ template: `
1231
+ <saved-report-templates-drawer
1232
+ [visible]="context.templateDrawerVisible()"
1233
+ [filterFields]="context.filters()"
1234
+ [service]="context.moduleName()"
1235
+ [category]="context.category()"
1236
+ [feature]="context.feature()"
1237
+ (templateSelected)="onSelect($event)">
1238
+ </saved-report-templates-drawer>
1239
+ `
1240
+ }]
1241
+ }] });
380
1242
 
381
1243
  /**
382
1244
  * Generated bundle index. Do not edit.
383
1245
  */
384
1246
 
385
- export { FilterTemplateEditorDialogComponent, FilterTemplateFormFields, FilterTemplatesModule };
1247
+ export { FilterTemplateEditorDialogComponent, FilterTemplateFormFields, FilterTemplatesModule, ReportTemplatesWrapperComponent, SavedReportTemplatesDrawerComponent };
386
1248
  //# sourceMappingURL=es.framework-ng.ui.core-filter-templates.mjs.map