@unifylib/ui-lib 1.0.3

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.
Files changed (129) hide show
  1. package/README.md +0 -0
  2. package/ng-package.json +7 -0
  3. package/package.json +12 -0
  4. package/src/lib/base-model/SearchStrConfig.ts +12 -0
  5. package/src/lib/base-model/api-response.ts +23 -0
  6. package/src/lib/base-model/audit-log-entry.ts +7 -0
  7. package/src/lib/base-model/button-action-settings.ts +25 -0
  8. package/src/lib/base-model/column-def.model.ts +34 -0
  9. package/src/lib/base-model/do-action-request.ts +11 -0
  10. package/src/lib/base-model/field-action.ts +7 -0
  11. package/src/lib/base-model/field-filter.model.ts +14 -0
  12. package/src/lib/base-model/field-info.ts +98 -0
  13. package/src/lib/base-model/field-predicate.model.ts +7 -0
  14. package/src/lib/base-model/filter-request.ts +27 -0
  15. package/src/lib/base-model/filter.model.ts +49 -0
  16. package/src/lib/base-model/get-items-list.ts +24 -0
  17. package/src/lib/base-model/index.ts +11 -0
  18. package/src/lib/base-model/lookupItem.ts +21 -0
  19. package/src/lib/base-model/null-snackmessage.ts +9 -0
  20. package/src/lib/base-model/page-info.ts +51 -0
  21. package/src/lib/base-model/report-request.model.ts +33 -0
  22. package/src/lib/base-model/response-envelop.model.ts +15 -0
  23. package/src/lib/base-model/snack-message.model.ts +14 -0
  24. package/src/lib/base-model/snackmessage-interface.ts +7 -0
  25. package/src/lib/base-model/table-column.interface.ts +29 -0
  26. package/src/lib/base-model/table-page-user-action.interface.ts +33 -0
  27. package/src/lib/base-model/workflow/workflow-steps.model.ts +9 -0
  28. package/src/lib/base-model/workflow/workflow.model.ts +52 -0
  29. package/src/lib/components/action-confirmation/action-confirmation.component.css +34 -0
  30. package/src/lib/components/action-confirmation/action-confirmation.component.html +18 -0
  31. package/src/lib/components/action-confirmation/action-confirmation.component.spec.ts +23 -0
  32. package/src/lib/components/action-confirmation/action-confirmation.component.ts +58 -0
  33. package/src/lib/components/activity-report-form/activity-report-form.component.html +109 -0
  34. package/src/lib/components/activity-report-form/activity-report-form.component.scss +0 -0
  35. package/src/lib/components/activity-report-form/activity-report-form.component.spec.ts +25 -0
  36. package/src/lib/components/activity-report-form/activity-report-form.component.ts +605 -0
  37. package/src/lib/components/audit-log-details-dialog/audit-log-details-dialog.component.css +51 -0
  38. package/src/lib/components/audit-log-details-dialog/audit-log-details-dialog.component.html +23 -0
  39. package/src/lib/components/audit-log-details-dialog/audit-log-details-dialog.component.spec.ts +23 -0
  40. package/src/lib/components/audit-log-details-dialog/audit-log-details-dialog.component.ts +69 -0
  41. package/src/lib/components/audit-log-list/audit-log.component.html +23 -0
  42. package/src/lib/components/audit-log-list/audit-log.component.scss +0 -0
  43. package/src/lib/components/audit-log-list/audit-log.component.spec.ts +25 -0
  44. package/src/lib/components/audit-log-list/audit-log.component.ts +116 -0
  45. package/src/lib/components/auto-complete/auto-complete.component.css +14 -0
  46. package/src/lib/components/auto-complete/auto-complete.component.html +29 -0
  47. package/src/lib/components/auto-complete/auto-complete.component.spec.ts +23 -0
  48. package/src/lib/components/auto-complete/auto-complete.component.ts +330 -0
  49. package/src/lib/components/base-form/base-form.component.html +58 -0
  50. package/src/lib/components/base-form/base-form.component.scss +0 -0
  51. package/src/lib/components/base-form/base-form.component.spec.ts +25 -0
  52. package/src/lib/components/base-form/base-form.component.ts +305 -0
  53. package/src/lib/components/base-form-canvas/base-form-canvas.component.css +22 -0
  54. package/src/lib/components/base-form-canvas/base-form-canvas.component.html +1006 -0
  55. package/src/lib/components/base-form-canvas/base-form-canvas.component.spec.ts +23 -0
  56. package/src/lib/components/base-form-canvas/base-form-canvas.component.ts +573 -0
  57. package/src/lib/components/base-input-dialog/base-input-dialog.component.css +0 -0
  58. package/src/lib/components/base-input-dialog/base-input-dialog.component.html +42 -0
  59. package/src/lib/components/base-input-dialog/base-input-dialog.component.spec.ts +23 -0
  60. package/src/lib/components/base-input-dialog/base-input-dialog.component.ts +78 -0
  61. package/src/lib/components/base-table/base-table.component.html +242 -0
  62. package/src/lib/components/base-table/base-table.component.scss +31 -0
  63. package/src/lib/components/base-table/base-table.component.spec.ts +25 -0
  64. package/src/lib/components/base-table/base-table.component.ts +568 -0
  65. package/src/lib/components/button-actions/button-actions.component.html +28 -0
  66. package/src/lib/components/button-actions/button-actions.component.scss +6 -0
  67. package/src/lib/components/button-actions/button-actions.component.spec.ts +23 -0
  68. package/src/lib/components/button-actions/button-actions.component.ts +72 -0
  69. package/src/lib/components/editable-base-table/editable-base-table.component.html +372 -0
  70. package/src/lib/components/editable-base-table/editable-base-table.component.scss +44 -0
  71. package/src/lib/components/editable-base-table/editable-base-table.component.spec.ts +25 -0
  72. package/src/lib/components/editable-base-table/editable-base-table.component.ts +570 -0
  73. package/src/lib/components/equation-builder/equation-builder.component.css +0 -0
  74. package/src/lib/components/equation-builder/equation-builder.component.html +31 -0
  75. package/src/lib/components/equation-builder/equation-builder.component.spec.ts +23 -0
  76. package/src/lib/components/equation-builder/equation-builder.component.ts +121 -0
  77. package/src/lib/components/multi-auto-complete/multi-auto-complete.component.css +11 -0
  78. package/src/lib/components/multi-auto-complete/multi-auto-complete.component.html +38 -0
  79. package/src/lib/components/multi-auto-complete/multi-auto-complete.component.spec.ts +23 -0
  80. package/src/lib/components/multi-auto-complete/multi-auto-complete.component.ts +317 -0
  81. package/src/lib/components/paginator/paginator.component.css +25 -0
  82. package/src/lib/components/paginator/paginator.component.html +34 -0
  83. package/src/lib/components/paginator/paginator.component.ts +94 -0
  84. package/src/lib/components/rejection-comment/action-comment.component.css +33 -0
  85. package/src/lib/components/rejection-comment/action-comment.component.html +46 -0
  86. package/src/lib/components/rejection-comment/action-comment.component.spec.ts +23 -0
  87. package/src/lib/components/rejection-comment/action-comment.component.ts +86 -0
  88. package/src/lib/components/report-details-dialog/report-details-dialog.component.css +17 -0
  89. package/src/lib/components/report-details-dialog/report-details-dialog.component.html +16 -0
  90. package/src/lib/components/report-details-dialog/report-details-dialog.component.spec.ts +23 -0
  91. package/src/lib/components/report-details-dialog/report-details-dialog.component.ts +113 -0
  92. package/src/lib/components/report-form/report-form.component.html +94 -0
  93. package/src/lib/components/report-form/report-form.component.scss +0 -0
  94. package/src/lib/components/report-form/report-form.component.spec.ts +25 -0
  95. package/src/lib/components/report-form/report-form.component.ts +588 -0
  96. package/src/lib/components/search-bar/search-bar.component.html +62 -0
  97. package/src/lib/components/search-bar/search-bar.component.scss +8 -0
  98. package/src/lib/components/search-bar/search-bar.component.spec.ts +25 -0
  99. package/src/lib/components/search-bar/search-bar.component.ts +70 -0
  100. package/src/lib/components/shared/attachment-uploader/attachment-uploader.component.css +54 -0
  101. package/src/lib/components/shared/attachment-uploader/attachment-uploader.component.html +22 -0
  102. package/src/lib/components/shared/attachment-uploader/attachment-uploader.component.spec.ts +23 -0
  103. package/src/lib/components/shared/attachment-uploader/attachment-uploader.component.ts +45 -0
  104. package/src/lib/components/shared-list/shared-list.component.css +0 -0
  105. package/src/lib/components/shared-list/shared-list.component.html +17 -0
  106. package/src/lib/components/shared-list/shared-list.component.spec.ts +23 -0
  107. package/src/lib/components/shared-list/shared-list.component.ts +53 -0
  108. package/src/lib/components/title-bar/title-bar.component.css +0 -0
  109. package/src/lib/components/title-bar/title-bar.component.css.map +1 -0
  110. package/src/lib/components/title-bar/title-bar.component.html +31 -0
  111. package/src/lib/components/title-bar/title-bar.component.scss +23 -0
  112. package/src/lib/components/title-bar/title-bar.component.spec.ts +23 -0
  113. package/src/lib/components/title-bar/title-bar.component.ts +119 -0
  114. package/src/lib/services/backend-service.ts +286 -0
  115. package/src/lib/services/index.ts +3 -0
  116. package/src/lib/services/top-panel.ts +17 -0
  117. package/src/lib/services/trigger-form.service.ts +11 -0
  118. package/src/lib/share-module/shared-module.ts +10 -0
  119. package/src/lib/styles/invoiceq-theme.scss +252 -0
  120. package/src/lib/styles/styles.scss +1723 -0
  121. package/src/lib/utils/base-utils.ts +102 -0
  122. package/src/lib/validators/date-range-validator.ts +31 -0
  123. package/src/lib/validators/index.ts +3 -0
  124. package/src/lib/validators/match-list.validator.ts +10 -0
  125. package/src/lib/validators/multi-email-validator.ts +15 -0
  126. package/src/public-api.ts +21 -0
  127. package/tsconfig.lib.json +15 -0
  128. package/tsconfig.lib.prod.json +11 -0
  129. package/tsconfig.spec.json +15 -0
@@ -0,0 +1,121 @@
1
+ import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
2
+ import { FormGroup } from "@angular/forms";
3
+ import { AutoCompleteComponent } from "../auto-complete/auto-complete.component";
4
+ import { MatFormField, MatOption, MatSelect } from "@angular/material/select";
5
+ import {NgForOf, NgIf, NgSwitch, NgSwitchCase} from "@angular/common";
6
+ import { FlexLayoutModule } from "@angular/flex-layout";
7
+ import {MatIcon} from "@angular/material/icon";
8
+ import {MatIconButton} from "@angular/material/button";
9
+
10
+ @Component({
11
+ selector: 'lib-equation-builder',
12
+ standalone: true,
13
+ imports: [
14
+ AutoCompleteComponent,
15
+ MatSelect,
16
+ MatOption,
17
+ MatFormField,
18
+ NgSwitch,
19
+ NgForOf,
20
+ NgSwitchCase,
21
+ FlexLayoutModule,
22
+ MatIcon,
23
+ MatIconButton,
24
+ NgIf
25
+ ],
26
+ templateUrl: './equation-builder.component.html',
27
+ styleUrl: './equation-builder.component.css'
28
+ })
29
+ export class EquationBuilderComponent implements OnInit {
30
+ @Input() form!: FormGroup;
31
+ @Input() field!: any;
32
+ @Input() isPending!: boolean;
33
+ @Output() valueChanged = new EventEmitter<any>();
34
+
35
+ operators = [
36
+ { label: '+', value: '+' },
37
+ { label: '-', value: '-' },
38
+ { label: '=', value: '=' }
39
+ ];
40
+
41
+ items: any[] = [];
42
+ @Input() readOnly!: boolean;
43
+
44
+ ngOnInit() {
45
+ const fieldValues: any[] = this.form.get('fieldValue')?.value;
46
+
47
+ if (Array.isArray(fieldValues) && fieldValues.length > 0) {
48
+ this.items = fieldValues.map(val => {
49
+ if (this.operators.some(op => op.value === val)) {
50
+ return { type: 'dropdown', value: val };
51
+ } else {
52
+ return {
53
+ type: 'lookup',
54
+ value: this.getLookupByCode(val)
55
+ };
56
+ }
57
+ });
58
+ } else {
59
+ this.items = [{ type: 'lookup', value: null }];
60
+ }
61
+
62
+ }
63
+
64
+ getLookupByCode(code: string): any {
65
+ return { code: code, id: 1, englishName: code, arabicName: code }
66
+ }
67
+
68
+ onLookupChange(index: number, value: any) {
69
+ this.items[index].value = value;
70
+
71
+ if (index === this.items.length - 1) {
72
+ this.items.push({ type: 'dropdown', value: null});
73
+ }
74
+
75
+ this.emitChange();
76
+ }
77
+
78
+ onDropdownChange(index: number, value: any) {
79
+ this.items[index].value = value;
80
+
81
+ if (index !== this.items.length - 1) {
82
+ this.emitChange();
83
+ return;
84
+ }
85
+
86
+ if (value === '=') {
87
+ this.items = this.items.slice(0, index + 1);
88
+ } else if (value != null && value !== '') {
89
+ const lastItem = this.items[this.items.length - 1];
90
+ if (lastItem.type !== 'lookup') {
91
+ this.items.push({ type: 'lookup', value: null });
92
+ }
93
+ }
94
+
95
+ this.emitChange();
96
+ }
97
+
98
+ emitChange() {
99
+ const simplifiedItems = this.items.map(item => {
100
+ if (item?.type === 'lookup') {
101
+ return item.value?.code ?? null;
102
+ } else if (item?.type === 'dropdown') {
103
+ return item.value ?? null;
104
+ }
105
+ return null;
106
+ });
107
+ const isValid = simplifiedItems[simplifiedItems.length - 1] === '=';
108
+
109
+ this.valueChanged.emit({
110
+ value: simplifiedItems.filter(val => val !== '='),
111
+ valid: isValid
112
+ });
113
+ }
114
+
115
+ onClearAll(): void {
116
+ this.items = [{ type: 'lookup', value: null }];
117
+ this.emitChange();
118
+ }
119
+
120
+
121
+ }
@@ -0,0 +1,11 @@
1
+ ::ng-deep .main-multi-auto {
2
+
3
+ .matField {
4
+ margin-bottom: 24px !important;
5
+ .mat-form-field-wrapper {
6
+ padding: 0 !important;
7
+ margin-bottom: 0 !important;
8
+ }
9
+ }
10
+ }
11
+
@@ -0,0 +1,38 @@
1
+ <ng-container *ngIf="this.field" class="main-multi-auto">
2
+ <div class="matField" (click)="this.filterLookupItems('', autocompleteTrigger)">
3
+ <mat-form-field fxFlex="100" appearance="outline" floatLabel="always">
4
+ <mat-label>{{ getLabel() }}</mat-label>
5
+ <mat-chip-listbox #chipList [selectable]="true">
6
+ <mat-chip
7
+ *ngFor="let item of selectedItems"
8
+ [removable]="true"
9
+ (removed)="remove(item)">
10
+ {{ itemNameByLag(item) }}
11
+ <mat-icon matChipRemove>cancel</mat-icon>
12
+ </mat-chip>
13
+ <input class="flex-auto" type="text"
14
+ [id]="getId()" #itemInput
15
+ [formControl]="multiControl"
16
+ [matAutocomplete]="auto" matInput
17
+ [readonly]="readonly"
18
+ [required]="isRequired"
19
+ (keydown)="onKeydown($event, autocompleteTrigger)"
20
+ [disabled]="field.readonly"
21
+ #autocompleteTrigger="matAutocompleteTrigger"
22
+ >
23
+ </mat-chip-listbox>
24
+
25
+ <mat-icon class="arrow_drop_down_autocomplete">arrow_drop_down</mat-icon>
26
+ <mat-autocomplete #auto="matAutocomplete" autoActiveFirstOption [displayWith]="displayFn"
27
+ (optionSelected)="selected($event.option.value)">
28
+ <ng-container *ngIf="!field.readonly">
29
+ <mat-option *ngFor="let item of lookupItems$ | async" [value]="item"
30
+ [disabled]="field.readonly || isSelected(item)"
31
+ (click)="$event.stopPropagation(); autocompleteTrigger.openPanel()">
32
+ {{ itemNameByLag(item) }}
33
+ </mat-option>
34
+ </ng-container>
35
+ </mat-autocomplete>
36
+ </mat-form-field>
37
+ </div>
38
+ </ng-container>
@@ -0,0 +1,23 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { MultiAutoCompleteComponent } from './multi-auto-complete.component';
4
+
5
+ describe('MultiAutoCompleteComponent', () => {
6
+ let component: MultiAutoCompleteComponent;
7
+ let fixture: ComponentFixture<MultiAutoCompleteComponent>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ imports: [MultiAutoCompleteComponent]
12
+ })
13
+ .compileComponents();
14
+
15
+ fixture = TestBed.createComponent(MultiAutoCompleteComponent);
16
+ component = fixture.componentInstance;
17
+ fixture.detectChanges();
18
+ });
19
+
20
+ it('should create', () => {
21
+ expect(component).toBeTruthy();
22
+ });
23
+ });
@@ -0,0 +1,317 @@
1
+ import {Component, ElementRef, EventEmitter, Input, Output, SimpleChanges, ViewChild} from '@angular/core';
2
+ import {ALL_ITEM, LookupItem} from "../../base-model";
3
+ import {MatAutocomplete, MatAutocompleteTrigger, MatOption} from "@angular/material/autocomplete";
4
+ import {Observable, of} from "rxjs";
5
+ import {FieldInfo} from "../../base-model";
6
+ import {FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms";
7
+ import {BackendService} from "../../services";
8
+ import {TranslateService} from "@ngx-translate/core";
9
+ import {GetItemsList} from "../../base-model/get-items-list";
10
+ import {MatFormField} from "@angular/material/form-field";
11
+ import {FlexLayoutModule} from "@angular/flex-layout";
12
+ import {MatChip, MatChipGrid, MatChipInput, MatChipListbox} from "@angular/material/chips";
13
+ import {AsyncPipe, NgForOf, NgIf} from "@angular/common";
14
+ import {MatIcon} from "@angular/material/icon";
15
+ import {MatInput} from "@angular/material/input";
16
+ import { MatLabel } from '@angular/material/form-field';
17
+ import {buildItemsList} from "../../utils/base-utils";
18
+ import {FilterRequest} from "../../base-model";
19
+ import {Filter} from "../../base-model";
20
+ import {RequireMatch} from "../../validators";
21
+ import { MatChipsModule } from '@angular/material/chips';
22
+ import { MatIconModule } from '@angular/material/icon';
23
+
24
+
25
+ @Component({
26
+ selector: 'app-multi-auto-complete',
27
+ standalone: true,
28
+ imports: [
29
+ MatFormField,
30
+ FlexLayoutModule,
31
+ MatChip,
32
+ MatIconModule,
33
+ MatChipsModule,
34
+ NgForOf,
35
+ MatIcon,
36
+ ReactiveFormsModule,
37
+ MatAutocompleteTrigger,
38
+ MatChipInput,
39
+ MatInput,
40
+ MatAutocomplete,
41
+ MatOption,
42
+ AsyncPipe,
43
+ NgIf,
44
+ MatChipGrid,
45
+ MatChipListbox,
46
+ MatLabel
47
+ ],
48
+ templateUrl: './multi-auto-complete.component.html',
49
+ styleUrl: './multi-auto-complete.component.css'
50
+ })
51
+ export class MultiAutoCompleteComponent {
52
+ // @ts-ignore
53
+ @ViewChild('itemInput') itemInput: ElementRef<HTMLInputElement>;
54
+ @ViewChild('autocompleteTrigger') matACTrigger: MatAutocompleteTrigger;
55
+
56
+ lookupItems$: Observable<LookupItem[]> | undefined;
57
+ private lookupItems: LookupItem[] = [];
58
+
59
+ selectedItems: LookupItem[] = [];
60
+ cascadedIdz: number[] = [];
61
+ isRequired: boolean= false;
62
+ private timer: any;
63
+
64
+ @Input()// @ts-ignore
65
+ field: FieldInfo;
66
+ //
67
+ @Input()// @ts-ignore
68
+ form: FormGroup;
69
+
70
+ @Input()// @ts-ignore
71
+ defaultValue: any;
72
+
73
+ @Input()
74
+ pathParam: any;
75
+
76
+ @Input()
77
+ readonly : boolean=false;
78
+
79
+ @Output()
80
+ selectedValue: EventEmitter<LookupItem[]> = new EventEmitter();
81
+
82
+ question = 'Would you like to add ?"';
83
+
84
+ multiControl = new FormControl();
85
+
86
+ constructor(private backendService: BackendService, private translateService: TranslateService) {
87
+ }
88
+
89
+ ngOnInit(): void {
90
+ // this.form = this.rootFormGroup.control;
91
+ this.selectedItems = [];
92
+ this.multiControl = new FormControl(this.form?.controls[this.field?.property]);
93
+
94
+ this.multiControl.enable();
95
+ this.updateFieldAttributes();
96
+
97
+ if (!this.field.acceptNewItem){
98
+ // @ts-ignore
99
+ this.multiControl.setValidators([RequireMatch]);
100
+ }
101
+
102
+ this.multiControl.setValue(this.form?.controls[this.field?.property]?.value);
103
+
104
+ this.multiControl.valueChanges.subscribe( v => {
105
+ if (typeof v === 'string') {
106
+ clearTimeout(this.timer);
107
+ this.timer = setTimeout(() => {
108
+ this.filterLookupItems(v, null);}, 700);
109
+ }
110
+ }
111
+ );
112
+
113
+ // this.multiControl.valueChanges.subscribe(v => {
114
+ // if (typeof v === 'string') this.filterLookupItems(v, null);
115
+ // })
116
+ // this.filterAutoCompleteData(this.field.property);
117
+
118
+ this.getLookupData();
119
+ if (this.field.cascadedBy) {
120
+ this.form?.get(this.field.cascadedBy)?.valueChanges.subscribe(v => {
121
+ this.removeChildItemsIfCascadedByRemoved(v);
122
+ });
123
+ }
124
+
125
+ }
126
+
127
+ private updateFieldAttributes() {
128
+ if (this.field.readonly) {
129
+ this.multiControl.disable();
130
+ this.isRequired = false;
131
+ } else if (this.field.required) {
132
+ this.form?.controls[this.field?.property].setValidators([Validators.required]);
133
+ this.form?.controls[this.field?.property].updateValueAndValidity();
134
+ this.multiControl.setValidators([Validators.required]);
135
+ this.isRequired = true;
136
+ }
137
+ this.multiControl.updateValueAndValidity();
138
+ }
139
+
140
+ private removeChildItemsIfCascadedByRemoved(v) {
141
+ this.cascadedIdz = [];
142
+ v.forEach(a => {
143
+ this.cascadedIdz.push(a.id);
144
+ });
145
+ let toDeleteIdz = [];
146
+ this.selectedItems?.forEach(x => {
147
+ const del = this.cascadedIdz.findIndex(y => y === x[this.field.cascadedBy]?.id);
148
+ if (del < 0) {
149
+ toDeleteIdz.push(x['id']);
150
+ }
151
+ });
152
+
153
+ if (toDeleteIdz) {
154
+ this.selectedItems = this.selectedItems.filter(s => !toDeleteIdz.includes(s.id));
155
+ this.form.get(this.field.property).patchValue(this.selectedItems, {emitEvent: true});
156
+ }
157
+ }
158
+
159
+ ngOnChanges({defaultValue, field}: SimpleChanges): void {
160
+ this.patchLookupValue(defaultValue?.currentValue);
161
+ if (field?.currentValue) {
162
+ this.field = field.currentValue;
163
+ this.updateFieldAttributes();
164
+ }
165
+ }
166
+
167
+ private getLookupData() {
168
+ const listItems: GetItemsList = buildItemsList('', this.field.lookupApiPath);
169
+
170
+ const filterRequest: FilterRequest = new FilterRequest();
171
+ filterRequest.filters = [];
172
+ this.cascadedIdz = [];
173
+ if (this.field.cascadedBy) {
174
+ this.form.get(this.field.cascadedBy)?.value?.forEach(a => {
175
+ this.cascadedIdz.push(a.id);
176
+ })
177
+ }
178
+ if (this.field.cascadedBy && this.cascadedIdz?.length > 0) {
179
+ filterRequest.filters.push(new Filter({
180
+ key: 'id', fieldName: this.field.cascadedBy, label: '',
181
+ filterType: 'FILED_FILTER', operator: 'IN',
182
+ valueObject: this.cascadedIdz
183
+ }));
184
+ }
185
+
186
+ filterRequest.columns = ['id','code','englishName','arabicName'];
187
+ this.backendService.getLookupItemsByMultiFilter(listItems, filterRequest).subscribe((resp => {
188
+ if (resp.valid) {
189
+ this.lookupItems = this.field.includeAllOption ? [ALL_ITEM].concat(resp.body) : resp.body;
190
+ if (!this.form.get('id')?.value) {
191
+ this.patchLookupValue(this.lookupItems?.find(l => l.defaultValue));
192
+ }
193
+ this.patchLookupValue(this.multiControl.value);
194
+
195
+ }
196
+
197
+ }));
198
+ }
199
+
200
+
201
+ selected(value: any): void {
202
+ if (value.id !== -1 && this.selectedItems.findIndex(i => i.id === value.id) === -1) {
203
+ this.selectedItems?.push(value);
204
+ this.itemInput.nativeElement.value = '';
205
+ this.form.get(this.field.property)?.setValue(this.selectedItems);
206
+ // @ts-ignore
207
+ this.selectedValue.emit(this.selectedItems);
208
+ this.multiControl.setValue(null);
209
+ }
210
+ }
211
+
212
+ displayFn(selected: any): string {
213
+ return selected ? selected.englishName : undefined;
214
+
215
+ }
216
+ isSelected(item: any): boolean {
217
+ return this.selectedItems?.some(selected => selected.id === item.id); // Use your unique identifier
218
+ }
219
+
220
+ _allowSelection(option: LookupItem): { [className: string]: boolean } {
221
+ return {
222
+ 'prevent-selection': option.id === -1,
223
+ }
224
+ }
225
+
226
+ itemNameByLag(item: any): string {
227
+ // return item.arabicName;
228
+ return this.translateService.getDefaultLang() === 'en' ? item.englishName : item.arabicName
229
+ }
230
+
231
+ private readonly _columns = ['id','code','englishName','arabicName'];
232
+
233
+ filterLookupItems(name: any, trigger: MatAutocompleteTrigger) {
234
+ if (trigger) {
235
+ trigger.openPanel();
236
+ }
237
+ if (name === undefined) {
238
+ name = '';
239
+ }
240
+
241
+ const listItems: GetItemsList = buildItemsList(name, this.field.lookupApiPath);
242
+
243
+ const filterRequest: FilterRequest = new FilterRequest();
244
+ filterRequest.filters = this.field.lookupFilterList || [];
245
+ filterRequest.searchStr = name;
246
+ this.cascadedIdz = [];
247
+ if (this.field.cascadedBy) {
248
+ this.form.get(this.field.cascadedBy)?.value?.forEach(a => {
249
+ this.cascadedIdz.push(a.id);
250
+ })
251
+ }
252
+ if (this.field.cascadedBy && this.cascadedIdz?.length > 0) {
253
+ filterRequest.filters.push(new Filter({
254
+ key: 'id',joinObjectName: this.field.cascadedBy, fieldName: this.field.cascadedBy, label: '',
255
+ filterType: 'FILED_FILTER', operator: 'IN',
256
+ valueObject: this.cascadedIdz
257
+ }));
258
+ }
259
+
260
+ filterRequest.columns = [...this._columns, ...(this.field.extraLookupsColumns ?? [])];
261
+
262
+ this.backendService.getLookupItemsByMultiFilter(listItems, filterRequest).subscribe(resp => {
263
+ // console.log('result -->', resp);
264
+ let items: LookupItem[] = [];
265
+ if (resp.valid) {
266
+ // resp.body.forEach(r => items.push(r));
267
+ items = resp.body.filter(item =>
268
+ // (item.englishName.toLowerCase().includes(name.toLowerCase()) ||
269
+ // item.arabicName.toLowerCase().includes(name.toLowerCase())) &&
270
+ this.selectedItems?.findIndex( i=> i.id ===item.id ) ===-1 );
271
+ if (!items?.length) {
272
+ this.field.acceptNewItem ?
273
+ items?.push(new LookupItem({id: -2, englishName: this.question + name, arabicName: this.question + name})):
274
+ items?.push(new LookupItem({id: -1, englishName: 'No match ...', arabicName: 'غير موجود ... '})) ;
275
+
276
+ }
277
+ } else {
278
+ this.field.acceptNewItem ?
279
+ items?.push(new LookupItem({id: -2, englishName: this.question + name, arabicName: this.question + name})):
280
+ items?.push(new LookupItem({id: -1, englishName: 'No match ...', arabicName: 'غير موجود ... '})) ;
281
+ }
282
+ this.lookupItems$ = of(items);
283
+ });
284
+ return;
285
+ }
286
+
287
+ private patchLookupValue(defaultValue: any) {
288
+ if (defaultValue) {
289
+ // this.form?.get(this.field.property)?.patchValue(defaultValue);
290
+ this.selectedItems = defaultValue;
291
+ this.multiControl.setValue(defaultValue);
292
+ this.multiControl.patchValue(defaultValue, {emitEvent: true});
293
+ }
294
+ }
295
+
296
+ remove(item: LookupItem) {
297
+ const index = this.selectedItems.indexOf(item);
298
+ if (index >= 0) {
299
+ this.selectedItems.splice(index, 1);
300
+ this.form.get(this.field.property).patchValue(this.selectedItems, {emitEvent: true});
301
+ }
302
+ }
303
+
304
+ getLabel() {
305
+ return this.translateService.instant(this.field.label);
306
+ }
307
+ onKeydown(event: KeyboardEvent, autocompleteTrigger: MatAutocompleteTrigger) {
308
+ if (event.key === 'Enter') {
309
+ this.filterLookupItems('', autocompleteTrigger);
310
+ autocompleteTrigger.openPanel();
311
+ event.preventDefault();
312
+ }
313
+ }
314
+ getId() {
315
+ return this.field.property;
316
+ }
317
+ }
@@ -0,0 +1,25 @@
1
+ .custom-paginator {
2
+ display: flex;
3
+ align-items: center;
4
+ gap: 8px;
5
+ }
6
+
7
+ button {
8
+ min-width: 32px;
9
+ }
10
+
11
+ .current-page {
12
+ font-weight: bold;
13
+ background-color: #2196f3;
14
+ color: white;
15
+ border-radius: 4px;
16
+ }
17
+
18
+ .ellipsis {
19
+ padding: 0 8px;
20
+ font-size: 14px;
21
+ color: gray;
22
+ background: none;
23
+ border: none;
24
+ cursor: pointer;
25
+ }
@@ -0,0 +1,34 @@
1
+ <div class="custom-paginator" [dir]="currentDirection">
2
+ <!-- Previous Button -->
3
+ <button mat-icon-button (click)="previousPage()" [disabled]="currentPage === 1" *ngIf="currentDirection == 'ltr'">
4
+ <mat-icon>chevron_left</mat-icon>
5
+ </button>
6
+ <button mat-icon-button (click)="previousPage()" [disabled]="currentPage === 1" *ngIf="currentDirection == 'rtl'">
7
+ <mat-icon>chevron_right</mat-icon>
8
+ </button>
9
+
10
+ <!-- Page Numbers and Ellipses -->
11
+ <ng-container *ngFor="let page of viewablePages">
12
+ <ng-container *ngIf="page === '...'">
13
+ <span class="ellipsis" (click)="goToPage(page)">{{ page }}</span>
14
+ </ng-container>
15
+ <ng-container *ngIf="page !== '...'">
16
+ <button
17
+ mat-button
18
+ [disabled]="page === currentPage"
19
+ (click)="goToPage(page)"
20
+ [class.current-page]="page === currentPage"
21
+ >
22
+ {{ page }}
23
+ </button>
24
+ </ng-container>
25
+ </ng-container>
26
+
27
+ <!-- Next Button -->
28
+ <button mat-icon-button (click)="nextPage()" [disabled]="currentPage === totalPages" *ngIf="currentDirection == 'ltr'">
29
+ <mat-icon>chevron_right</mat-icon>
30
+ </button>
31
+ <button mat-icon-button (click)="nextPage()" [disabled]="currentPage === totalPages" *ngIf="currentDirection == 'rtl'">
32
+ <mat-icon>chevron_left</mat-icon>
33
+ </button>
34
+ </div>
@@ -0,0 +1,94 @@
1
+ import {Component, Input, Output, EventEmitter, OnChanges, OnInit} from '@angular/core';
2
+ import { MatIconModule } from '@angular/material/icon';
3
+ import { MatButtonModule } from '@angular/material/button';
4
+ import { NgIf, NgForOf } from '@angular/common';
5
+ import {Directionality} from "@angular/cdk/bidi";
6
+
7
+ @Component({
8
+ selector: 'app-paginator',
9
+ templateUrl: './paginator.component.html',
10
+ styleUrls: ['./paginator.component.css'],
11
+ standalone: true,
12
+ imports: [MatIconModule, MatButtonModule, NgIf, NgForOf],
13
+ })
14
+ export class PaginatorComponent implements OnInit , OnChanges {
15
+ maxSize = 3;
16
+
17
+ @Input() currentPage = 1;
18
+ @Input() totalPages = 1;
19
+ @Output() pageChange = new EventEmitter<number>();
20
+ currentDirection: 'ltr' | 'rtl';
21
+ constructor(private directionality: Directionality) {
22
+ }
23
+ viewablePages: (number | string)[] = [];
24
+ ngOnInit() {
25
+ this.currentDirection = this.directionality.value;
26
+ this.directionality.change.subscribe((value) => {
27
+ this.currentDirection = value;
28
+ })
29
+ }
30
+
31
+ ngOnChanges() {
32
+ this.calculatePages();
33
+ }
34
+
35
+ calculatePages() {
36
+ const pages: (number | string)[] = [];
37
+ const halfSize = Math.floor(this.maxSize / 2);
38
+
39
+ pages.push(1);
40
+
41
+ let start = Math.max(this.currentPage - halfSize, 2);
42
+ let end = Math.min(this.currentPage + halfSize, this.totalPages - 1);
43
+
44
+ if (this.currentPage <= halfSize) {
45
+ end = Math.min(this.maxSize, this.totalPages - 1);
46
+ } else if (this.currentPage + halfSize >= this.totalPages) {
47
+ start = Math.max(this.totalPages - this.maxSize + 2, 2);
48
+ }
49
+
50
+ if (start > 2) {
51
+ pages.push('...');
52
+ }
53
+
54
+ for (let i = start; i <= end; i++) {
55
+ pages.push(i);
56
+ }
57
+
58
+ if (end < this.totalPages - 1) {
59
+ pages.push('...');
60
+ }
61
+
62
+ if (this.totalPages > 1) {
63
+ pages.push(this.totalPages);
64
+ }
65
+
66
+ this.viewablePages = pages;
67
+ }
68
+
69
+ goToPage(page: number | string) {
70
+ if (page === '...') {
71
+ if (this.viewablePages[1] === '...') {
72
+ this.currentPage = Math.max(this.currentPage - this.maxSize, 1);
73
+ } else {
74
+ this.currentPage = Math.min(this.currentPage + this.maxSize, this.totalPages);
75
+ }
76
+ } else if (typeof page === 'number' && page !== this.currentPage) {
77
+ this.currentPage = page;
78
+ }
79
+
80
+ this.pageChange.emit(this.currentPage);
81
+ this.calculatePages();
82
+ }
83
+ nextPage() {
84
+ if (this.currentPage < this.totalPages) {
85
+ this.goToPage(this.currentPage + 1);
86
+ }
87
+ }
88
+
89
+ previousPage() {
90
+ if (this.currentPage > 1) {
91
+ this.goToPage(this.currentPage - 1);
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,33 @@
1
+ .m-0{
2
+ margin: 0 !important;
3
+ }
4
+ .w-100{
5
+ width: 100%;
6
+ }
7
+ .form-style{
8
+ min-height: 12vh !important;
9
+ height: auto !important;
10
+ }
11
+ .text-red{
12
+ color: red !important;
13
+ }
14
+ .modal-footer{
15
+ margin-top: 5vh;
16
+ }
17
+ .overflow-hidden{
18
+ overflow: hidden;
19
+ }
20
+ .pad-top{
21
+ margin-top: -7px;
22
+ display: block;
23
+ }
24
+ .action-comment {
25
+ display: flex;
26
+ justify-content: space-between;
27
+ }
28
+ .material-icons {
29
+ color: #E22222;padding: 10px;cursor: pointer;
30
+ }
31
+ textarea#warn::placeholder {
32
+ color: red;
33
+ }