@unifylib/ui-lib 1.0.3 → 1.1.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 (155) hide show
  1. package/ng-package.json +7 -7
  2. package/package.json +14 -12
  3. package/src/lib/base-model/SearchStrConfig.ts +12 -12
  4. package/src/lib/base-model/api-response.ts +23 -23
  5. package/src/lib/base-model/audit-log-entry.ts +7 -7
  6. package/src/lib/base-model/button-action-settings.ts +29 -25
  7. package/src/lib/base-model/column-def.model.ts +34 -34
  8. package/src/lib/base-model/do-action-request.ts +11 -11
  9. package/src/lib/base-model/feature-item.ts +9 -0
  10. package/src/lib/base-model/field-action.ts +7 -7
  11. package/src/lib/base-model/field-filter.model.ts +7 -14
  12. package/src/lib/base-model/field-info.ts +102 -98
  13. package/src/lib/base-model/field-predicate.model.ts +7 -7
  14. package/src/lib/base-model/filter-request.ts +27 -27
  15. package/src/lib/base-model/filter.model.ts +49 -49
  16. package/src/lib/base-model/get-items-list.ts +24 -24
  17. package/src/lib/base-model/index.ts +11 -11
  18. package/src/lib/base-model/items-total.model.ts +12 -0
  19. package/src/lib/base-model/line-item.model.ts +18 -0
  20. package/src/lib/base-model/lookupItem.ts +21 -21
  21. package/src/lib/base-model/null-snackmessage.ts +9 -9
  22. package/src/lib/base-model/page-info.ts +54 -51
  23. package/src/lib/base-model/report-request.model.ts +33 -33
  24. package/src/lib/base-model/response-envelop.model.ts +15 -15
  25. package/src/lib/base-model/snack-message.model.ts +14 -14
  26. package/src/lib/base-model/snackmessage-interface.ts +7 -7
  27. package/src/lib/base-model/table-column.interface.ts +29 -29
  28. package/src/lib/base-model/table-page-user-action.interface.ts +33 -33
  29. package/src/lib/base-model/workflow/workflow-steps.model.ts +9 -9
  30. package/src/lib/base-model/workflow/workflow.model.ts +52 -52
  31. package/src/lib/components/action-comment/action-comment.component.css +52 -0
  32. package/src/lib/components/action-comment/action-comment.component.html +47 -0
  33. package/src/lib/components/{rejection-comment → action-comment}/action-comment.component.spec.ts +23 -23
  34. package/src/lib/components/{rejection-comment → action-comment}/action-comment.component.ts +102 -86
  35. package/src/lib/components/action-confirmation/action-confirmation.component.css +46 -34
  36. package/src/lib/components/action-confirmation/action-confirmation.component.html +32 -18
  37. package/src/lib/components/action-confirmation/action-confirmation.component.spec.ts +23 -23
  38. package/src/lib/components/action-confirmation/action-confirmation.component.ts +58 -58
  39. package/src/lib/components/activity-report-form/activity-report-form.component.html +110 -109
  40. package/src/lib/components/activity-report-form/activity-report-form.component.scss +69 -0
  41. package/src/lib/components/activity-report-form/activity-report-form.component.spec.ts +25 -25
  42. package/src/lib/components/activity-report-form/activity-report-form.component.ts +616 -605
  43. package/src/lib/components/advanced-filter/field-filter/field-filter.component.html +8 -0
  44. package/src/lib/components/advanced-filter/field-filter/field-filter.component.scss +0 -0
  45. package/src/lib/components/advanced-filter/field-filter/field-filter.component.spec.ts +25 -0
  46. package/src/lib/components/advanced-filter/field-filter/field-filter.component.ts +55 -0
  47. package/src/lib/components/advanced-filter/filter-builder/filter-builder.component.html +36 -0
  48. package/src/lib/components/advanced-filter/filter-builder/filter-builder.component.scss +130 -0
  49. package/src/lib/components/advanced-filter/filter-builder/filter-builder.component.spec.ts +25 -0
  50. package/src/lib/components/advanced-filter/filter-builder/filter-builder.component.ts +186 -0
  51. package/src/lib/components/audit-log-details-dialog/audit-log-details-dialog.component.css +51 -51
  52. package/src/lib/components/audit-log-details-dialog/audit-log-details-dialog.component.html +23 -23
  53. package/src/lib/components/audit-log-details-dialog/audit-log-details-dialog.component.spec.ts +23 -23
  54. package/src/lib/components/audit-log-details-dialog/audit-log-details-dialog.component.ts +69 -69
  55. package/src/lib/components/audit-log-list/audit-log.component.html +26 -23
  56. package/src/lib/components/audit-log-list/audit-log.component.scss +50 -0
  57. package/src/lib/components/audit-log-list/audit-log.component.spec.ts +25 -25
  58. package/src/lib/components/audit-log-list/audit-log.component.ts +114 -116
  59. package/src/lib/components/auto-complete/auto-complete.component.css +55 -14
  60. package/src/lib/components/auto-complete/auto-complete.component.html +45 -29
  61. package/src/lib/components/auto-complete/auto-complete.component.spec.ts +23 -23
  62. package/src/lib/components/auto-complete/auto-complete.component.ts +331 -330
  63. package/src/lib/components/base-form/base-form.component.html +59 -58
  64. package/src/lib/components/base-form/base-form.component.scss +68 -0
  65. package/src/lib/components/base-form/base-form.component.spec.ts +25 -25
  66. package/src/lib/components/base-form/base-form.component.ts +323 -305
  67. package/src/lib/components/base-form-canvas/base-form-canvas.component.css +196 -22
  68. package/src/lib/components/base-form-canvas/base-form-canvas.component.html +1095 -1006
  69. package/src/lib/components/base-form-canvas/base-form-canvas.component.spec.ts +23 -23
  70. package/src/lib/components/base-form-canvas/base-form-canvas.component.ts +680 -573
  71. package/src/lib/components/base-input-dialog/base-input-dialog.component.css +67 -0
  72. package/src/lib/components/base-input-dialog/base-input-dialog.component.html +47 -42
  73. package/src/lib/components/base-input-dialog/base-input-dialog.component.spec.ts +23 -23
  74. package/src/lib/components/base-input-dialog/base-input-dialog.component.ts +77 -78
  75. package/src/lib/components/base-table/base-table.component.html +268 -242
  76. package/src/lib/components/base-table/base-table.component.scss +140 -31
  77. package/src/lib/components/base-table/base-table.component.spec.ts +25 -25
  78. package/src/lib/components/base-table/base-table.component.ts +621 -568
  79. package/src/lib/components/button-actions/button-actions.component.html +27 -28
  80. package/src/lib/components/button-actions/button-actions.component.scss +101 -6
  81. package/src/lib/components/button-actions/button-actions.component.spec.ts +23 -23
  82. package/src/lib/components/button-actions/button-actions.component.ts +70 -72
  83. package/src/lib/components/editable-base-table/editable-base-table.component.html +337 -372
  84. package/src/lib/components/editable-base-table/editable-base-table.component.scss +126 -44
  85. package/src/lib/components/editable-base-table/editable-base-table.component.spec.ts +25 -25
  86. package/src/lib/components/editable-base-table/editable-base-table.component.ts +579 -570
  87. package/src/lib/components/equation-builder/equation-builder.component.css +39 -0
  88. package/src/lib/components/equation-builder/equation-builder.component.html +31 -31
  89. package/src/lib/components/equation-builder/equation-builder.component.spec.ts +23 -23
  90. package/src/lib/components/equation-builder/equation-builder.component.ts +119 -121
  91. package/src/lib/components/item-line-editor/item-line-editor.component.html +102 -0
  92. package/src/lib/components/item-line-editor/item-line-editor.component.scss +152 -0
  93. package/src/lib/components/item-line-editor/item-line-editor.component.spec.ts +23 -0
  94. package/src/lib/components/item-line-editor/item-line-editor.component.ts +306 -0
  95. package/src/lib/components/multi-auto-complete/multi-auto-complete.component.css +19 -11
  96. package/src/lib/components/multi-auto-complete/multi-auto-complete.component.html +38 -38
  97. package/src/lib/components/multi-auto-complete/multi-auto-complete.component.spec.ts +23 -23
  98. package/src/lib/components/multi-auto-complete/multi-auto-complete.component.ts +315 -317
  99. package/src/lib/components/paginator/paginator.component.css +65 -25
  100. package/src/lib/components/paginator/paginator.component.html +30 -34
  101. package/src/lib/components/paginator/paginator.component.ts +87 -94
  102. package/src/lib/components/report-details-dialog/report-details-dialog.component.css +17 -17
  103. package/src/lib/components/report-details-dialog/report-details-dialog.component.html +16 -16
  104. package/src/lib/components/report-details-dialog/report-details-dialog.component.spec.ts +23 -23
  105. package/src/lib/components/report-details-dialog/report-details-dialog.component.ts +111 -113
  106. package/src/lib/components/report-form/report-form.component.html +92 -94
  107. package/src/lib/components/report-form/report-form.component.scss +51 -0
  108. package/src/lib/components/report-form/report-form.component.spec.ts +25 -25
  109. package/src/lib/components/report-form/report-form.component.ts +599 -588
  110. package/src/lib/components/search-bar/search-bar.component.html +51 -62
  111. package/src/lib/components/search-bar/search-bar.component.scss +63 -8
  112. package/src/lib/components/search-bar/search-bar.component.spec.ts +25 -25
  113. package/src/lib/components/search-bar/search-bar.component.ts +68 -70
  114. package/src/lib/components/section-form-canvas/section-form-canvas.component.html +43 -0
  115. package/src/lib/components/section-form-canvas/section-form-canvas.component.scss +81 -0
  116. package/src/lib/components/section-form-canvas/section-form-canvas.component.spec.ts +23 -0
  117. package/src/lib/components/section-form-canvas/section-form-canvas.component.ts +67 -0
  118. package/src/lib/components/shared/action-button/action-button.component.html +12 -0
  119. package/src/lib/components/shared/action-button/action-button.component.scss +45 -0
  120. package/src/lib/components/shared/action-button/action-button.component.ts +51 -0
  121. package/src/lib/components/shared/action-card/action-card.component.html +78 -0
  122. package/src/lib/components/shared/action-card/action-card.component.scss +238 -0
  123. package/src/lib/components/shared/action-card/action-card.component.ts +56 -0
  124. package/src/lib/components/shared/attachment-uploader/attachment-uploader.component.css +135 -54
  125. package/src/lib/components/shared/attachment-uploader/attachment-uploader.component.html +36 -22
  126. package/src/lib/components/shared/attachment-uploader/attachment-uploader.component.spec.ts +23 -23
  127. package/src/lib/components/shared/attachment-uploader/attachment-uploader.component.ts +71 -45
  128. package/src/lib/components/shared-list/shared-list.component.html +17 -17
  129. package/src/lib/components/shared-list/shared-list.component.spec.ts +23 -23
  130. package/src/lib/components/shared-list/shared-list.component.ts +53 -53
  131. package/src/lib/components/snackbar-static/snackbar-static.component.html +20 -0
  132. package/src/lib/components/snackbar-static/snackbar-static.component.scss +135 -0
  133. package/src/lib/components/snackbar-static/snackbar-static.component.ts +26 -0
  134. package/src/lib/components/title-bar/title-bar.component.html +35 -31
  135. package/src/lib/components/title-bar/title-bar.component.scss +126 -23
  136. package/src/lib/components/title-bar/title-bar.component.spec.ts +23 -23
  137. package/src/lib/components/title-bar/title-bar.component.ts +126 -119
  138. package/src/lib/services/backend-service.ts +287 -286
  139. package/src/lib/services/index.ts +3 -3
  140. package/src/lib/services/top-panel.ts +17 -17
  141. package/src/lib/services/trigger-form.service.ts +11 -11
  142. package/src/lib/share-module/shared-module.ts +10 -10
  143. package/src/lib/utils/base-utils.ts +102 -102
  144. package/src/lib/validators/date-range-validator.ts +31 -31
  145. package/src/lib/validators/index.ts +3 -3
  146. package/src/lib/validators/match-list.validator.ts +10 -10
  147. package/src/lib/validators/multi-email-validator.ts +15 -15
  148. package/src/public-api.ts +29 -21
  149. package/tsconfig.lib.json +15 -15
  150. package/tsconfig.lib.prod.json +11 -11
  151. package/tsconfig.spec.json +15 -15
  152. package/src/lib/components/rejection-comment/action-comment.component.css +0 -33
  153. package/src/lib/components/rejection-comment/action-comment.component.html +0 -46
  154. package/src/lib/styles/invoiceq-theme.scss +0 -252
  155. package/src/lib/styles/styles.scss +0 -1723
@@ -1,317 +1,315 @@
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
- }
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 {MatChip, MatChipGrid, MatChipInput, MatChipListbox} from "@angular/material/chips";
12
+ import {AsyncPipe, NgForOf, NgIf} from "@angular/common";
13
+ import {MatIcon} from "@angular/material/icon";
14
+ import {MatInput} from "@angular/material/input";
15
+ import { MatLabel } from '@angular/material/form-field';
16
+ import {buildItemsList} from "../../utils/base-utils";
17
+ import {FilterRequest} from "../../base-model";
18
+ import {Filter} from "../../base-model";
19
+ import {RequireMatch} from "../../validators";
20
+ import { MatChipsModule } from '@angular/material/chips';
21
+ import { MatIconModule } from '@angular/material/icon';
22
+
23
+
24
+ @Component({
25
+ selector: 'app-multi-auto-complete',
26
+ standalone: true,
27
+ imports: [
28
+ MatFormField,
29
+ MatChip,
30
+ MatIconModule,
31
+ MatChipsModule,
32
+ NgForOf,
33
+ MatIcon,
34
+ ReactiveFormsModule,
35
+ MatAutocompleteTrigger,
36
+ MatChipInput,
37
+ MatInput,
38
+ MatAutocomplete,
39
+ MatOption,
40
+ AsyncPipe,
41
+ NgIf,
42
+ MatChipGrid,
43
+ MatChipListbox,
44
+ MatLabel
45
+ ],
46
+ templateUrl: './multi-auto-complete.component.html',
47
+ styleUrl: './multi-auto-complete.component.css'
48
+ })
49
+ export class MultiAutoCompleteComponent {
50
+ // @ts-ignore
51
+ @ViewChild('itemInput') itemInput: ElementRef<HTMLInputElement>;
52
+ @ViewChild('autocompleteTrigger') matACTrigger: MatAutocompleteTrigger;
53
+
54
+ lookupItems$: Observable<LookupItem[]> | undefined;
55
+ private lookupItems: LookupItem[] = [];
56
+
57
+ selectedItems: LookupItem[] = [];
58
+ cascadedIdz: number[] = [];
59
+ isRequired: boolean= false;
60
+ private timer: any;
61
+
62
+ @Input()// @ts-ignore
63
+ field: FieldInfo;
64
+ //
65
+ @Input()// @ts-ignore
66
+ form: FormGroup;
67
+
68
+ @Input()// @ts-ignore
69
+ defaultValue: any;
70
+
71
+ @Input()
72
+ pathParam: any;
73
+
74
+ @Input()
75
+ readonly : boolean=false;
76
+
77
+ @Output()
78
+ selectedValue: EventEmitter<LookupItem[]> = new EventEmitter();
79
+
80
+ question = 'Would you like to add ?"';
81
+
82
+ multiControl = new FormControl();
83
+
84
+ constructor(private backendService: BackendService, private translateService: TranslateService) {
85
+ }
86
+
87
+ ngOnInit(): void {
88
+ // this.form = this.rootFormGroup.control;
89
+ this.selectedItems = [];
90
+ this.multiControl = new FormControl(this.form?.controls[this.field?.property]);
91
+
92
+ this.multiControl.enable();
93
+ this.updateFieldAttributes();
94
+
95
+ if (!this.field.acceptNewItem){
96
+ // @ts-ignore
97
+ this.multiControl.setValidators([RequireMatch]);
98
+ }
99
+
100
+ this.multiControl.setValue(this.form?.controls[this.field?.property]?.value);
101
+
102
+ this.multiControl.valueChanges.subscribe( v => {
103
+ if (typeof v === 'string') {
104
+ clearTimeout(this.timer);
105
+ this.timer = setTimeout(() => {
106
+ this.filterLookupItems(v, null);}, 700);
107
+ }
108
+ }
109
+ );
110
+
111
+ // this.multiControl.valueChanges.subscribe(v => {
112
+ // if (typeof v === 'string') this.filterLookupItems(v, null);
113
+ // })
114
+ // this.filterAutoCompleteData(this.field.property);
115
+
116
+ this.getLookupData();
117
+ if (this.field.cascadedBy) {
118
+ this.form?.get(this.field.cascadedBy)?.valueChanges.subscribe(v => {
119
+ this.removeChildItemsIfCascadedByRemoved(v);
120
+ });
121
+ }
122
+
123
+ }
124
+
125
+ private updateFieldAttributes() {
126
+ if (this.field.readonly) {
127
+ this.multiControl.disable();
128
+ this.isRequired = false;
129
+ } else if (this.field.required) {
130
+ this.form?.controls[this.field?.property].setValidators([Validators.required]);
131
+ this.form?.controls[this.field?.property].updateValueAndValidity();
132
+ this.multiControl.setValidators([Validators.required]);
133
+ this.isRequired = true;
134
+ }
135
+ this.multiControl.updateValueAndValidity();
136
+ }
137
+
138
+ private removeChildItemsIfCascadedByRemoved(v) {
139
+ this.cascadedIdz = [];
140
+ v.forEach(a => {
141
+ this.cascadedIdz.push(a.id);
142
+ });
143
+ let toDeleteIdz = [];
144
+ this.selectedItems?.forEach(x => {
145
+ const del = this.cascadedIdz.findIndex(y => y === x[this.field.cascadedBy]?.id);
146
+ if (del < 0) {
147
+ toDeleteIdz.push(x['id']);
148
+ }
149
+ });
150
+
151
+ if (toDeleteIdz) {
152
+ this.selectedItems = this.selectedItems.filter(s => !toDeleteIdz.includes(s.id));
153
+ this.form.get(this.field.property).patchValue(this.selectedItems, {emitEvent: true});
154
+ }
155
+ }
156
+
157
+ ngOnChanges({defaultValue, field}: SimpleChanges): void {
158
+ this.patchLookupValue(defaultValue?.currentValue);
159
+ if (field?.currentValue) {
160
+ this.field = field.currentValue;
161
+ this.updateFieldAttributes();
162
+ }
163
+ }
164
+
165
+ private getLookupData() {
166
+ const listItems: GetItemsList = buildItemsList('', this.field.lookupApiPath);
167
+
168
+ const filterRequest: FilterRequest = new FilterRequest();
169
+ filterRequest.filters = [];
170
+ this.cascadedIdz = [];
171
+ if (this.field.cascadedBy) {
172
+ this.form.get(this.field.cascadedBy)?.value?.forEach(a => {
173
+ this.cascadedIdz.push(a.id);
174
+ })
175
+ }
176
+ if (this.field.cascadedBy && this.cascadedIdz?.length > 0) {
177
+ filterRequest.filters.push(new Filter({
178
+ key: 'id', fieldName: this.field.cascadedBy, label: '',
179
+ filterType: 'FILED_FILTER', operator: 'IN',
180
+ valueObject: this.cascadedIdz
181
+ }));
182
+ }
183
+
184
+ filterRequest.columns = ['id','code','englishName','arabicName'];
185
+ this.backendService.getLookupItemsByMultiFilter(listItems, filterRequest).subscribe((resp => {
186
+ if (resp.valid) {
187
+ this.lookupItems = this.field.includeAllOption ? [ALL_ITEM].concat(resp.body) : resp.body;
188
+ if (!this.form.get('id')?.value) {
189
+ this.patchLookupValue(this.lookupItems?.find(l => l.defaultValue));
190
+ }
191
+ this.patchLookupValue(this.multiControl.value);
192
+
193
+ }
194
+
195
+ }));
196
+ }
197
+
198
+
199
+ selected(value: any): void {
200
+ if (value.id !== -1 && this.selectedItems.findIndex(i => i.id === value.id) === -1) {
201
+ this.selectedItems?.push(value);
202
+ this.itemInput.nativeElement.value = '';
203
+ this.form.get(this.field.property)?.setValue(this.selectedItems);
204
+ // @ts-ignore
205
+ this.selectedValue.emit(this.selectedItems);
206
+ this.multiControl.setValue(null);
207
+ }
208
+ }
209
+
210
+ displayFn(selected: any): string {
211
+ return selected ? selected.englishName : undefined;
212
+
213
+ }
214
+ isSelected(item: any): boolean {
215
+ return this.selectedItems?.some(selected => selected.id === item.id); // Use your unique identifier
216
+ }
217
+
218
+ _allowSelection(option: LookupItem): { [className: string]: boolean } {
219
+ return {
220
+ 'prevent-selection': option.id === -1,
221
+ }
222
+ }
223
+
224
+ itemNameByLag(item: any): string {
225
+ // return item.arabicName;
226
+ return this.translateService.getDefaultLang() === 'en' ? item.englishName : item.arabicName
227
+ }
228
+
229
+ private readonly _columns = ['id','code','englishName','arabicName'];
230
+
231
+ filterLookupItems(name: any, trigger: MatAutocompleteTrigger) {
232
+ if (trigger) {
233
+ trigger.openPanel();
234
+ }
235
+ if (name === undefined) {
236
+ name = '';
237
+ }
238
+
239
+ const listItems: GetItemsList = buildItemsList(name, this.field.lookupApiPath);
240
+
241
+ const filterRequest: FilterRequest = new FilterRequest();
242
+ filterRequest.filters = this.field.lookupFilterList || [];
243
+ filterRequest.searchStr = name;
244
+ this.cascadedIdz = [];
245
+ if (this.field.cascadedBy) {
246
+ this.form.get(this.field.cascadedBy)?.value?.forEach(a => {
247
+ this.cascadedIdz.push(a.id);
248
+ })
249
+ }
250
+ if (this.field.cascadedBy && this.cascadedIdz?.length > 0) {
251
+ filterRequest.filters.push(new Filter({
252
+ key: 'id',joinObjectName: this.field.cascadedBy, fieldName: this.field.cascadedBy, label: '',
253
+ filterType: 'FILED_FILTER', operator: 'IN',
254
+ valueObject: this.cascadedIdz
255
+ }));
256
+ }
257
+
258
+ filterRequest.columns = [...this._columns, ...(this.field.extraLookupsColumns ?? [])];
259
+
260
+ this.backendService.getLookupItemsByMultiFilter(listItems, filterRequest).subscribe(resp => {
261
+ // console.log('result -->', resp);
262
+ let items: LookupItem[] = [];
263
+ if (resp.valid) {
264
+ // resp.body.forEach(r => items.push(r));
265
+ items = resp.body.filter(item =>
266
+ // (item.englishName.toLowerCase().includes(name.toLowerCase()) ||
267
+ // item.arabicName.toLowerCase().includes(name.toLowerCase())) &&
268
+ this.selectedItems?.findIndex( i=> i.id ===item.id ) ===-1 );
269
+ if (!items?.length) {
270
+ this.field.acceptNewItem ?
271
+ items?.push(new LookupItem({id: -2, englishName: this.question + name, arabicName: this.question + name})):
272
+ items?.push(new LookupItem({id: -1, englishName: 'No match ...', arabicName: 'غير موجود ... '})) ;
273
+
274
+ }
275
+ } else {
276
+ this.field.acceptNewItem ?
277
+ items?.push(new LookupItem({id: -2, englishName: this.question + name, arabicName: this.question + name})):
278
+ items?.push(new LookupItem({id: -1, englishName: 'No match ...', arabicName: 'غير موجود ... '})) ;
279
+ }
280
+ this.lookupItems$ = of(items);
281
+ });
282
+ return;
283
+ }
284
+
285
+ private patchLookupValue(defaultValue: any) {
286
+ if (defaultValue) {
287
+ // this.form?.get(this.field.property)?.patchValue(defaultValue);
288
+ this.selectedItems = defaultValue;
289
+ this.multiControl.setValue(defaultValue);
290
+ this.multiControl.patchValue(defaultValue, {emitEvent: true});
291
+ }
292
+ }
293
+
294
+ remove(item: LookupItem) {
295
+ const index = this.selectedItems.indexOf(item);
296
+ if (index >= 0) {
297
+ this.selectedItems.splice(index, 1);
298
+ this.form.get(this.field.property).patchValue(this.selectedItems, {emitEvent: true});
299
+ }
300
+ }
301
+
302
+ getLabel() {
303
+ return this.translateService.instant(this.field.label);
304
+ }
305
+ onKeydown(event: KeyboardEvent, autocompleteTrigger: MatAutocompleteTrigger) {
306
+ if (event.key === 'Enter') {
307
+ this.filterLookupItems('', autocompleteTrigger);
308
+ autocompleteTrigger.openPanel();
309
+ event.preventDefault();
310
+ }
311
+ }
312
+ getId() {
313
+ return this.field.property;
314
+ }
315
+ }