@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,330 +1,331 @@
1
- import {
2
- Component,
3
- EventEmitter,
4
- Inject,
5
- Input,
6
- OnChanges,
7
- OnInit,
8
- Output,
9
- SimpleChanges,
10
- ViewChild
11
- } from '@angular/core';
12
- import {FieldInfo, Filter, LookupItem} from "../../base-model";
13
- import {MatAutocomplete, MatAutocompleteTrigger, MatOption} from "@angular/material/autocomplete";
14
- import {Observable, of} from "rxjs";
15
- import {FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms";
16
- import {BackendService} from "../../services";
17
- import {GetItemsList} from "../../base-model/get-items-list";
18
- import {TranslateModule, TranslateService} from "@ngx-translate/core";
19
- import {MatFormFieldModule} from "@angular/material/form-field";
20
- import {MatInputModule} from "@angular/material/input";
21
- import {AsyncPipe, NgClass, NgForOf, NgIf} from "@angular/common";
22
- import {MatIcon} from "@angular/material/icon";
23
- import {FlexLayoutModule} from "@angular/flex-layout";
24
- import {buildItemsList} from "../../utils/base-utils";
25
- import {RequireMatch} from "../../validators";
26
-
27
- @Component({
28
- selector: 'app-auto-complete',
29
- standalone: true,
30
- imports: [
31
- TranslateModule,
32
- MatFormFieldModule,
33
- MatAutocompleteTrigger,
34
- MatInputModule,
35
- ReactiveFormsModule,
36
- NgIf,
37
- MatAutocomplete,
38
- AsyncPipe,
39
- MatOption,
40
- NgClass,
41
- MatIcon,
42
- NgForOf,
43
- FlexLayoutModule,
44
-
45
- ],
46
- templateUrl: './auto-complete.component.html',
47
- styleUrl: './auto-complete.component.css'
48
- })
49
- export class AutoCompleteComponent implements OnInit, OnChanges {
50
- @ViewChild('autocompleteTrigger') matACTrigger: MatAutocompleteTrigger;
51
-
52
- lookupItems$: Observable<LookupItem[]> | undefined;
53
- lookupItems: LookupItem[] = [];
54
- items: LookupItem[] = [];
55
-
56
- // @ts-ignore
57
- // form: FormGroup;
58
-
59
- @Input()// @ts-ignore
60
- field: FieldInfo;
61
-
62
- @Input()// @ts-ignore
63
- form: FormGroup;
64
- @Input()// @ts-ignore
65
- defaultValue: any;
66
-
67
- @Input()
68
- readonly:boolean = false;
69
-
70
- @Input()
71
- supportingAttributes: any;
72
-
73
- @Output()
74
- selectedValue: EventEmitter<LookupItem> = new EventEmitter();
75
-
76
- @ViewChild(MatAutocompleteTrigger) autocompleteTrigger!: MatAutocompleteTrigger;
77
-
78
- question = 'Would you like to add ?"';
79
- cascadedId: number = 0;
80
- isRequired:boolean = false;
81
-
82
- private timer: any;
83
- myControl = new FormControl();
84
-
85
- constructor(private backendService: BackendService,
86
- protected translateService: TranslateService,
87
- @Inject('secretsManager') private secretsManager: any) {
88
- }
89
-
90
- ngOnInit(): void {
91
- this.buildControl();
92
- this.extractWorkflowGuidedCascading();
93
- this.getLookupData();
94
- // this.myControl.valueChanges.subscribe(v => {
95
- // if (typeof v === 'string') this.filterLookupItems(v, null);
96
- // });
97
-
98
- this.form.get(this.field.property).valueChanges.subscribe(x => {
99
- if (x?.id === -100) {
100
- this.myControl.reset()
101
- }
102
- })
103
- if (this.field.cascadedBy) {
104
- this.form.get(this.field.cascadedByProperty)?.valueChanges.subscribe(v => {
105
- this.cascadedId = this.form.get(this.field.cascadedByProperty).value?.id;
106
- this.lookupItems = [];
107
- this.myControl.setValue(undefined);
108
- this.myControl.patchValue(undefined, {emitEvent: true});
109
- this.form.get(this.field.property)?.setValue(undefined);
110
- this.form.get(this.field.property)?.patchValue(undefined, {emitEvent: true});
111
- this.getLookupData();
112
- });
113
- }
114
-
115
- }
116
-
117
- private extractWorkflowGuidedCascading() {
118
- if (this.supportingAttributes && this.field.workflowCascadingField && this.supportingAttributes[this.field.workflowCascadingField]) {
119
- this.cascadedId = this.supportingAttributes[this.field.workflowCascadingField];
120
- }
121
- }
122
-
123
- private buildControl() {
124
- this.myControl = new FormControl(this.form?.controls[this.field?.property]);
125
- this.backendService.fieldAction$.subscribe( e => {
126
- if (e.property === this.field?.property) {
127
- this.field.readonly = e.fieldInfo.readonly;
128
- this.updateAttributes();
129
- }
130
- });
131
- this.updateAttributes();
132
-
133
- this.myControl.valueChanges.subscribe( v => {
134
- if (typeof v === 'string') {
135
- clearTimeout(this.timer);
136
- this.timer = setTimeout(() => {
137
- this.filterLookupItems(v, null); }, 700);
138
- }
139
- }
140
- );
141
- }
142
-
143
- private updateAttributes() {
144
- this.myControl.enable();
145
- if (this.field.readonly) {
146
- this.myControl.disable();
147
- this.isRequired = false;
148
- } else if (this.field.required) {
149
- this.form?.controls[this.field?.property].setValidators([Validators.required]);
150
- this.myControl.setValidators([Validators.required]);
151
- this.isRequired = true;
152
- }
153
-
154
- if (!this.field.acceptNewItem) {
155
- // @ts-ignore
156
- this.myControl.setValidators(RequireMatch);
157
- }
158
-
159
- this.myControl.setValue(this.form?.get(this.field?.property)?.value);
160
- }
161
-
162
- ngOnChanges({defaultValue}: SimpleChanges): void {
163
- this.patchLookupValue(defaultValue?.currentValue);
164
- }
165
-
166
- private getLookupData() {
167
- if (this.field.cascadedBy && !this.cascadedId) {
168
- } else {
169
- const listItems = buildItemsList('', this.field.lookupApiPath, this.field.cascadedBy);
170
- listItems.columns = ['id','code','englishName','arabicName'];
171
-
172
- this.backendService.getLookupItemsByFilter(listItems, this.cascadedId).subscribe((resp => {
173
- if (resp.valid) {
174
- this.lookupItems = resp.body;
175
- this.lookupItems = [];
176
- resp.body.forEach((r: LookupItem) => this.lookupItems.push(r));
177
- if (!this.form?.get('id')?.value) {
178
- this.patchLookupValue(this.lookupItems?.find(l => l.defaultValue));
179
- }
180
- this.patchLookupValue(this.myControl.value);
181
- }
182
-
183
- }));
184
- }
185
- }
186
-
187
- itemSelected(value: any) {
188
- if (value && value.id !== -1) {
189
- if (value.englishName.indexOf(this.question) === 0) {
190
- value.englishName = value.englishName.substring(this.question.length).split('"?')[0];
191
- }
192
- if (value.arabicName && value.arabicName.indexOf(this.question) === 0) {
193
- value.arabicName = value.arabicName.substring(this.question.length).split('"?')[0];
194
- }
195
- this.form.get(this.field.property)?.setValue(value);
196
- // @ts-ignore
197
- this.selectedValue.emit(this.myControl.value);
198
- }
199
- setTimeout(() => {
200
- this.autocompleteTrigger.closePanel();
201
- });
202
-
203
- }
204
-
205
- displayFn(selected: any): string | undefined {
206
- if (selected?.id < 0) {
207
- return null;
208
- }
209
-
210
- if (Array.isArray(selected))
211
- selected = this.defaultValue
212
- return this.translateService.getDefaultLang() === 'en'
213
- ? selected?.englishName
214
- : selected?.arabicName;
215
- }
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 this.translateService.getDefaultLang() === 'en' ? item.englishName : item.arabicName
226
-
227
- }
228
-
229
- filterLookupItems(name: any, trigger: MatAutocompleteTrigger) {
230
- if (this.myControl.enabled) {
231
- if (trigger) {
232
- trigger.openPanel();
233
- }
234
- if (name === undefined) {
235
- name = '';
236
- }
237
-
238
- if (name === '' && !this.myControl.value) {
239
- this.myControl.setValue(undefined);
240
- this.myControl.updateValueAndValidity();
241
- }
242
-
243
- const listItems: GetItemsList = buildItemsList(name, this.field.lookupApiPath, this.field.cascadedBy);
244
- listItems.filters = this.field.lookupFilterList;
245
- this.addLookupFilterByAnotherProperties(listItems);
246
- listItems.columns = ['id','code','englishName','arabicName'];
247
- this.backendService.getLookupItemsByFilter(listItems);
248
- if (this.field.cascadedBy) {
249
- this.cascadedId = this.form.get(this.field.cascadedByProperty).value?.id;
250
- } else {
251
- this.extractWorkflowGuidedCascading();
252
- }
253
- listItems.columns = ['id','code','englishName','arabicName'];
254
- this.backendService.getLookupItemsByFilter(listItems, this.cascadedId).subscribe(resp => {
255
- if (resp.valid) {
256
- this.items = resp.body.filter((item: { id: any; }) => this.myControl.value?.id !== item.id);
257
- if (!this.items?.length) {
258
- this.field.acceptNewItem ?
259
- this.items?.push(new LookupItem({id: -2, englishName: this.question + name, arabicName: this.question + name})) :
260
- this.items?.push(new LookupItem({id: -1, englishName: 'No match ...', arabicName: 'غير موجود ... '}));
261
- }
262
- } else {
263
- if (this.items?.length == 0)
264
- this.field.acceptNewItem && this.backendService.parentForm.get(this.field.cascadedBy).value != null ?
265
- this.items?.push(new LookupItem({id: -2, englishName: this.question + name, arabicName: this.question + name})) :
266
- this.items?.push(new LookupItem({id: -1, englishName: 'No match ...', arabicName: 'غير موجود ... '}));
267
- }
268
- this.lookupItems$ = of(this.items);
269
- });
270
- return;
271
- }
272
- }
273
-
274
- addLookupFilterByAnotherProperties(listItems: GetItemsList) {
275
- const properties = this.field.lookupFilterList;
276
- if (!Array.isArray(properties)) return;
277
-
278
- const newFilters: Filter[] = [];
279
-
280
- for (const prop of properties) {
281
- const fieldValue = this.form?.value?.[prop.fieldName];
282
- const finalValue = typeof fieldValue === 'object' && fieldValue !== null ? fieldValue.code : fieldValue;
283
- if (!finalValue) continue;
284
-
285
- const fieldName =typeof fieldValue === 'object' && fieldValue !== null ? prop.fieldName + "." + this.field.flattenBySubAttribute : prop.fieldName;
286
- const filter = new Filter({
287
- fieldType: 'FILED_FILTER',
288
- filterType: undefined,
289
- operator: prop.operator,
290
- valueObject: finalValue,
291
- fieldName: fieldName
292
- });
293
-
294
- newFilters.push(filter);
295
- }
296
-
297
- if (newFilters.length) {
298
- listItems.filters = [...(listItems.filters || []), ...newFilters];
299
- }
300
- listItems.filters = listItems.filters.filter(f => f.valueObject);
301
- }
302
-
303
- private patchLookupValue(defaultValue: any) {
304
- if (defaultValue) {
305
-
306
- // this.form?.get(this.field.property)?.patchValue(defaultValue);
307
- this.myControl.setValue(defaultValue);
308
- this.myControl.patchValue(defaultValue, {emitEvent: true});
309
- }
310
- }
311
- getId() {
312
- return this.field.property;
313
- }
314
- onKeydown(event: KeyboardEvent, autocompleteTrigger: MatAutocompleteTrigger) {
315
- if (event.key === 'Enter') {
316
- this.filterLookupItems('', autocompleteTrigger);
317
- autocompleteTrigger.openPanel();
318
- event.preventDefault();
319
- }
320
- }
321
-
322
- closed() {
323
- if (!this.myControl.value){
324
- this.form.get(this.field.property)?.setValue(this.myControl.value);
325
- this.selectedValue.emit(this.myControl.value);
326
- } else if (typeof this.myControl.value === 'string') {
327
- this.myControl.setValue(null);
328
- }
329
- }
330
- }
1
+ import {
2
+ Component,
3
+ EventEmitter,
4
+ Inject,
5
+ Input,
6
+ OnChanges,
7
+ OnInit,
8
+ Output,
9
+ SimpleChanges,
10
+ ViewChild
11
+ } from '@angular/core';
12
+ import {FieldInfo, Filter, LookupItem} from "../../base-model";
13
+ import {MatAutocomplete, MatAutocompleteTrigger, MatOption} from "@angular/material/autocomplete";
14
+ import {Observable, of} from "rxjs";
15
+ import {FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms";
16
+ import {BackendService} from "../../services";
17
+ import {GetItemsList} from "../../base-model/get-items-list";
18
+ import {TranslateModule, TranslateService} from "@ngx-translate/core";
19
+ import {MatFormFieldModule} from "@angular/material/form-field";
20
+ import {MatInputModule} from "@angular/material/input";
21
+ import {AsyncPipe, NgClass, NgForOf, NgIf} from "@angular/common";
22
+ import {MatIcon} from "@angular/material/icon";
23
+ import {buildItemsList} from "../../utils/base-utils";
24
+ import {RequireMatch} from "../../validators";
25
+ import {MatButton, MatIconButton} from "@angular/material/button";
26
+
27
+ @Component({
28
+ selector: 'app-auto-complete',
29
+ standalone: true,
30
+ imports: [
31
+ TranslateModule,
32
+ MatFormFieldModule,
33
+ MatAutocompleteTrigger,
34
+ MatInputModule,
35
+ ReactiveFormsModule,
36
+ NgIf,
37
+ MatAutocomplete,
38
+ AsyncPipe,
39
+ MatOption,
40
+ NgClass,
41
+ MatIcon,
42
+ NgForOf,
43
+ MatIconButton,
44
+ MatButton,
45
+
46
+ ],
47
+ templateUrl: './auto-complete.component.html',
48
+ styleUrl: './auto-complete.component.css'
49
+ })
50
+ export class AutoCompleteComponent implements OnInit, OnChanges {
51
+ @ViewChild('autocompleteTrigger') matACTrigger: MatAutocompleteTrigger;
52
+
53
+ lookupItems$: Observable<LookupItem[]> | undefined;
54
+ lookupItems: LookupItem[] = [];
55
+ items: LookupItem[] = [];
56
+
57
+ // @ts-ignore
58
+ // form: FormGroup;
59
+
60
+ @Input()// @ts-ignore
61
+ field: FieldInfo;
62
+
63
+ @Input()// @ts-ignore
64
+ form: FormGroup;
65
+ @Input()// @ts-ignore
66
+ defaultValue: any;
67
+
68
+ @Input()
69
+ readonly:boolean = false;
70
+
71
+ @Input()
72
+ supportingAttributes: any;
73
+
74
+ @Output()
75
+ selectedValue: EventEmitter<LookupItem> = new EventEmitter();
76
+
77
+ @ViewChild(MatAutocompleteTrigger) autocompleteTrigger!: MatAutocompleteTrigger;
78
+
79
+ question = 'Would you like to add ?"';
80
+ cascadedId: number = 0;
81
+ isRequired:boolean = false;
82
+
83
+ private timer: any;
84
+ myControl = new FormControl();
85
+
86
+ constructor(private backendService: BackendService,
87
+ protected translateService: TranslateService,
88
+ @Inject('secretsManager') private secretsManager: any) {
89
+ }
90
+
91
+ ngOnInit(): void {
92
+ this.buildControl();
93
+ this.extractWorkflowGuidedCascading();
94
+ this.getLookupData();
95
+ // this.myControl.valueChanges.subscribe(v => {
96
+ // if (typeof v === 'string') this.filterLookupItems(v, null);
97
+ // });
98
+
99
+ this.form.get(this.field.property).valueChanges.subscribe(x => {
100
+ if (x?.id === -100) {
101
+ this.myControl.reset()
102
+ }
103
+ })
104
+ if (this.field.cascadedBy) {
105
+ this.form.get(this.field.cascadedByProperty)?.valueChanges.subscribe(v => {
106
+ this.cascadedId = this.form.get(this.field.cascadedByProperty).value?.id;
107
+ this.lookupItems = [];
108
+ this.myControl.setValue(undefined);
109
+ this.myControl.patchValue(undefined, {emitEvent: true});
110
+ this.form.get(this.field.property)?.setValue(undefined);
111
+ this.form.get(this.field.property)?.patchValue(undefined, {emitEvent: true});
112
+ this.getLookupData();
113
+ });
114
+ }
115
+
116
+ }
117
+
118
+ private extractWorkflowGuidedCascading() {
119
+ if (this.supportingAttributes && this.field.workflowCascadingField && this.supportingAttributes[this.field.workflowCascadingField]) {
120
+ this.cascadedId = this.supportingAttributes[this.field.workflowCascadingField];
121
+ }
122
+ }
123
+
124
+ private buildControl() {
125
+ this.myControl = new FormControl(this.form?.controls[this.field?.property]);
126
+ this.backendService.fieldAction$.subscribe( e => {
127
+ if (e.property === this.field?.property) {
128
+ this.field.readonly = e.fieldInfo.readonly;
129
+ this.updateAttributes();
130
+ }
131
+ });
132
+ this.updateAttributes();
133
+
134
+ this.myControl.valueChanges.subscribe( v => {
135
+ if (typeof v === 'string') {
136
+ clearTimeout(this.timer);
137
+ this.timer = setTimeout(() => {
138
+ this.filterLookupItems(v, null); }, 700);
139
+ }
140
+ }
141
+ );
142
+ }
143
+
144
+ private updateAttributes() {
145
+ this.myControl.enable();
146
+ if (this.field.readonly) {
147
+ this.myControl.disable();
148
+ this.isRequired = false;
149
+ } else if (this.field.required) {
150
+ this.form?.controls[this.field?.property].setValidators([Validators.required]);
151
+ this.myControl.setValidators([Validators.required]);
152
+ this.isRequired = true;
153
+ }
154
+
155
+ if (!this.field.acceptNewItem) {
156
+ // @ts-ignore
157
+ this.myControl.setValidators(RequireMatch);
158
+ }
159
+
160
+ this.myControl.setValue(this.form?.get(this.field?.property)?.value);
161
+ }
162
+
163
+ ngOnChanges({defaultValue}: SimpleChanges): void {
164
+ this.patchLookupValue(defaultValue?.currentValue);
165
+ }
166
+
167
+ private getLookupData() {
168
+ if (this.field.cascadedBy && !this.cascadedId) {
169
+ } else {
170
+ const listItems = buildItemsList('', this.field.lookupApiPath, this.field.cascadedBy);
171
+ listItems.columns = ['id','code','englishName','arabicName'];
172
+
173
+ this.backendService.getLookupItemsByFilter(listItems, this.cascadedId).subscribe((resp => {
174
+ if (resp.valid) {
175
+ this.lookupItems = resp.body;
176
+ this.lookupItems = [];
177
+ resp.body.forEach((r: LookupItem) => this.lookupItems.push(r));
178
+ if (!this.form?.get('id')?.value) {
179
+ this.patchLookupValue(this.lookupItems?.find(l => l.defaultValue));
180
+ }
181
+ this.patchLookupValue(this.myControl.value);
182
+ }
183
+
184
+ }));
185
+ }
186
+ }
187
+
188
+ itemSelected(value: any) {
189
+ if (value && value.id !== -1) {
190
+ if (value.englishName.indexOf(this.question) === 0) {
191
+ value.englishName = value.englishName.substring(this.question.length).split('"?')[0];
192
+ }
193
+ if (value.arabicName && value.arabicName.indexOf(this.question) === 0) {
194
+ value.arabicName = value.arabicName.substring(this.question.length).split('"?')[0];
195
+ }
196
+ this.form.get(this.field.property)?.setValue(value);
197
+ // @ts-ignore
198
+ this.selectedValue.emit(this.myControl.value);
199
+ }
200
+ setTimeout(() => {
201
+ this.autocompleteTrigger.closePanel();
202
+ });
203
+
204
+ }
205
+
206
+ displayFn(selected: any): string | undefined {
207
+ if (selected?.id < 0) {
208
+ return null;
209
+ }
210
+
211
+ if (Array.isArray(selected))
212
+ selected = this.defaultValue
213
+ return this.translateService.getDefaultLang() === 'en'
214
+ ? selected?.englishName
215
+ : selected?.arabicName;
216
+ }
217
+
218
+
219
+ _allowSelection(option: LookupItem): { [className: string]: boolean } {
220
+ return {
221
+ 'prevent-selection': option.id === -1,
222
+ };
223
+ }
224
+
225
+ itemNameByLag(item: any): string {
226
+ return this.translateService.getDefaultLang() === 'en' ? item.englishName : item.arabicName
227
+
228
+ }
229
+
230
+ filterLookupItems(name: any, trigger: MatAutocompleteTrigger) {
231
+ if (this.myControl.enabled) {
232
+ if (trigger) {
233
+ trigger.openPanel();
234
+ }
235
+ if (name === undefined) {
236
+ name = '';
237
+ }
238
+
239
+ if (name === '' && !this.myControl.value) {
240
+ this.myControl.setValue(undefined);
241
+ this.myControl.updateValueAndValidity();
242
+ }
243
+
244
+ const listItems: GetItemsList = buildItemsList(name, this.field.lookupApiPath, this.field.cascadedBy);
245
+ listItems.filters = this.field.lookupFilterList;
246
+ this.addLookupFilterByAnotherProperties(listItems);
247
+ listItems.columns = ['id','code','englishName','arabicName'];
248
+ this.backendService.getLookupItemsByFilter(listItems);
249
+ if (this.field.cascadedBy) {
250
+ this.cascadedId = this.form.get(this.field.cascadedByProperty).value?.id;
251
+ } else {
252
+ this.extractWorkflowGuidedCascading();
253
+ }
254
+ listItems.columns = ['id','code','englishName','arabicName'];
255
+ this.backendService.getLookupItemsByFilter(listItems, this.cascadedId).subscribe(resp => {
256
+ if (resp.valid) {
257
+ this.items = resp.body.filter((item: { id: any; }) => this.myControl.value?.id !== item.id);
258
+ if (!this.items?.length) {
259
+ this.field.acceptNewItem ?
260
+ this.items?.push(new LookupItem({id: -2, englishName: this.question + name, arabicName: this.question + name})) :
261
+ this.items?.push(new LookupItem({id: -1, englishName: 'No match ...', arabicName: 'غير موجود ... '}));
262
+ }
263
+ } else {
264
+ if (this.items?.length == 0)
265
+ this.field.acceptNewItem && this.backendService.parentForm.get(this.field.cascadedBy).value != null ?
266
+ this.items?.push(new LookupItem({id: -2, englishName: this.question + name, arabicName: this.question + name})) :
267
+ this.items?.push(new LookupItem({id: -1, englishName: 'No match ...', arabicName: 'غير موجود ... '}));
268
+ }
269
+ this.lookupItems$ = of(this.items);
270
+ });
271
+ return;
272
+ }
273
+ }
274
+
275
+ addLookupFilterByAnotherProperties(listItems: GetItemsList) {
276
+ const properties = this.field.lookupFilterList;
277
+ if (!Array.isArray(properties)) return;
278
+
279
+ const newFilters: Filter[] = [];
280
+
281
+ for (const prop of properties) {
282
+ const fieldValue = this.form?.value?.[prop.fieldName];
283
+ const finalValue = typeof fieldValue === 'object' && fieldValue !== null ? fieldValue.code : fieldValue;
284
+ if (!finalValue) continue;
285
+
286
+ const fieldName =typeof fieldValue === 'object' && fieldValue !== null ? prop.fieldName + "." + this.field.flattenBySubAttribute : prop.fieldName;
287
+ const filter = new Filter({
288
+ fieldType: 'FILED_FILTER',
289
+ filterType: undefined,
290
+ operator: prop.operator,
291
+ valueObject: finalValue,
292
+ fieldName: fieldName
293
+ });
294
+
295
+ newFilters.push(filter);
296
+ }
297
+
298
+ if (newFilters.length) {
299
+ listItems.filters = [...(listItems.filters || []), ...newFilters];
300
+ }
301
+ listItems.filters = listItems.filters.filter(f => f.valueObject);
302
+ }
303
+
304
+ private patchLookupValue(defaultValue: any) {
305
+ if (defaultValue) {
306
+
307
+ // this.form?.get(this.field.property)?.patchValue(defaultValue);
308
+ this.myControl.setValue(defaultValue);
309
+ this.myControl.patchValue(defaultValue, {emitEvent: true});
310
+ }
311
+ }
312
+ getId() {
313
+ return this.field.property;
314
+ }
315
+ onKeydown(event: KeyboardEvent, autocompleteTrigger: MatAutocompleteTrigger) {
316
+ if (event.key === 'Enter') {
317
+ this.filterLookupItems('', autocompleteTrigger);
318
+ autocompleteTrigger.openPanel();
319
+ event.preventDefault();
320
+ }
321
+ }
322
+
323
+ closed() {
324
+ if (!this.myControl.value){
325
+ this.form.get(this.field.property)?.setValue(this.myControl.value);
326
+ this.selectedValue.emit(this.myControl.value);
327
+ } else if (typeof this.myControl.value === 'string') {
328
+ this.myControl.setValue(null);
329
+ }
330
+ }
331
+ }