@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,573 +1,680 @@
1
- import {
2
- Component,
3
- computed,
4
- ElementRef,
5
- EventEmitter,
6
- Input,
7
- model,
8
- OnChanges,
9
- OnDestroy,
10
- OnInit,
11
- Output,
12
- signal,
13
- SimpleChanges,
14
- ViewChild
15
- } from '@angular/core';
16
- import {FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule} from "@angular/forms";
17
- import {
18
- MatChip,
19
- MatChipGrid,
20
- MatChipInput,
21
- MatChipInputEvent,
22
- MatChipListbox,
23
- MatChipOption,
24
- MatChipRemove,
25
- MatChipRow
26
- } from "@angular/material/chips";
27
- import {FieldInfo} from "../../base-model/field-info";
28
- import {ActivatedRoute} from "@angular/router";
29
- import {BackendService} from "../../services/backend-service";
30
- import {TranslateModule, TranslateService} from "@ngx-translate/core";
31
- import {PageInfo} from "../../base-model/page-info";
32
- import {COMMA, ENTER, SEMICOLON} from "@angular/cdk/keycodes";
33
- import {LookupItem} from "../../base-model/lookupItem";
34
- import {FieldMessage} from "../../base-model/api-response";
35
- import {
36
- CurrencyPipe,
37
- DatePipe,
38
- KeyValuePipe,
39
- NgForOf,
40
- NgIf,
41
- NgSwitch,
42
- NgSwitchCase,
43
- NgTemplateOutlet
44
- } from "@angular/common";
45
- import {FlexLayoutModule} from "@angular/flex-layout";
46
- import {MatError, MatFormFieldModule, MatLabel} from "@angular/material/form-field";
47
- import {MatInputModule} from "@angular/material/input";
48
- import {MatDatepicker, MatDatepickerInput, MatDatepickerToggle} from "@angular/material/datepicker";
49
- import {MatIcon} from "@angular/material/icon";
50
- import {CdkTextareaAutosize} from "@angular/cdk/text-field";
51
- import {MatRadioButton, MatRadioGroup} from "@angular/material/radio";
52
- import {MatCheckbox} from "@angular/material/checkbox";
53
- import {MatOption, MatSelect} from "@angular/material/select";
54
- import {MatDivider} from "@angular/material/divider";
55
- import {NgxJsonViewerModule} from "ngx-json-viewer";
56
- import {NgxMatIntlTelInputComponent} from "ngx-mat-intl-tel-input";
57
- import {MultiAutoCompleteComponent} from "../multi-auto-complete/multi-auto-complete.component";
58
- import {AutoCompleteComponent} from "../auto-complete/auto-complete.component";
59
- import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
60
- import {BehaviorSubject, map} from "rxjs";
61
- import {LiveAnnouncer} from "@angular/cdk/a11y";
62
- import {buildFormFields} from "../../utils/base-utils";
63
- import moment, {Moment} from "moment/moment";
64
-
65
- @Component({
66
- selector: 'app-base-form-canvas',
67
- standalone: true,
68
- imports: [
69
- ReactiveFormsModule,
70
- NgSwitch,
71
- FormsModule,
72
- FlexLayoutModule,
73
- NgForOf,
74
- NgTemplateOutlet,
75
- KeyValuePipe,
76
- NgIf,
77
- MatLabel,
78
- MatFormFieldModule,
79
- TranslateModule,
80
- MatInputModule,
81
- MatDatepickerInput,
82
- MatDatepicker,
83
- MatDatepickerToggle,
84
- MatError,
85
- NgSwitchCase,
86
- MatIcon,
87
- MatChip,
88
- CdkTextareaAutosize,
89
- MatRadioGroup,
90
- MatCheckbox,
91
- MatRadioButton,
92
- MatSelect,
93
- MatOption,
94
- MatDivider,
95
- DatePipe,
96
- NgxJsonViewerModule,
97
- CurrencyPipe,
98
- NgxMatIntlTelInputComponent,
99
- MatChipGrid,
100
- MatChipInput,
101
- MatChipRow,
102
- MatChipListbox,
103
- MatChipOption,
104
- MultiAutoCompleteComponent,
105
- AutoCompleteComponent,
106
- MatChipRemove
107
- ],
108
- templateUrl: './base-form-canvas.component.html',
109
- styleUrl: './base-form-canvas.component.css'
110
- })
111
- export class BaseFormCanvasComponent implements OnInit, OnChanges, OnDestroy {
112
- public hide: boolean = true;
113
- dateViewMode: 'month' | 'year' | 'multi-year' = 'month';
114
- @Input()
115
- pageInfo!: PageInfo;
116
-
117
- @Input()// @ts-ignore
118
- fields: FieldInfo[] = [];
119
-
120
- @Input()// @ts-ignore
121
- errors: FieldMessage[] = [];
122
-
123
- @Input()
124
- workflowEditableFields: string[]=[];
125
- @Input()
126
- isWorkflowEditableManaged: boolean = false;
127
-
128
- @Input()
129
- supportingAttributes: any;
130
-
131
- @Input()
132
- editable: boolean = false;
133
-
134
- @Input()
135
- item: any = {};
136
-
137
- _defaults: any = {};
138
-
139
- @Output() attachmentEmitter: EventEmitter<any> = new EventEmitter<any>();
140
-
141
- @Output() actionEmitter: EventEmitter<any> = new EventEmitter<any>();
142
-
143
-
144
- @Output()
145
- formUpdated: EventEmitter<FormGroup> = new EventEmitter<FormGroup>();
146
-
147
- @Output()
148
- hyperTextEvent: EventEmitter<boolean> = new EventEmitter();
149
-
150
-
151
- formParam!: FormGroup;
152
- screenFields = new Map<number, FieldInfo[]>();
153
- removable = true;
154
- selectable = true;
155
- addOnBlur = true;
156
-
157
- readonly separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON];
158
-
159
-
160
- public isPending: boolean = false;
161
- public isNew: boolean = false;
162
- itemId: number | undefined;
163
-
164
- equationOperators : string[] = ['+','-','(' ];
165
- @Input() equationSuggestedFields: { name: string, id: string }[] = [];
166
- allEquationFields: BehaviorSubject<string[]>;
167
- readonly equationValues = signal<string[]>([]);
168
- currentEquationValue = model<string>('');
169
-
170
- readonly equationFilteredFields = computed(() => {
171
- const currentEquationValue = this.currentEquationValue().toLowerCase();
172
- return this.allEquationFields.pipe(
173
- map(fields =>
174
- currentEquationValue ? fields?.filter(field => field.toLowerCase().includes(currentEquationValue)) : fields?.slice()
175
- )
176
- );
177
- });
178
- @ViewChild('EquationValueInput') fieldsInput: ElementRef<HTMLInputElement>;
179
-
180
- set defaults(value: any | undefined) {
181
- // this.backendService._defaults = value;
182
- this._defaults = value;
183
- this.buildForm();
184
- // this.myCallback = value;
185
- }
186
-
187
- get defaults(): any | undefined {
188
- // this.myCallback;
189
- return this._defaults;
190
- // return this.backendService._defaults;
191
- }
192
-
193
- constructor(protected translateService: TranslateService,
194
- protected activatedRoute: ActivatedRoute,
195
- protected backendService: BackendService,
196
- private fb: FormBuilder,
197
- private announcer: LiveAnnouncer) {
198
-
199
- }
200
-
201
- ngOnInit(): void {
202
- if(this.item && this.item.fieldValue){
203
- this.equationValues.update(value => this.item.fieldValue)
204
- }
205
- this.defaults = this.item;
206
- this.extractLables();
207
- this.buildForm();
208
- this.activatedRoute.data.subscribe(data => {
209
- this.isPending = data['mode'] === 'pending';
210
- this.isNew = data['mode'] === 'new';
211
- });
212
- // this.authService.summaryPanelState$.next(Constants.SUMMARY_PANEL_CLOSED);
213
- // this.defaults = {};
214
- // this.invoiceQInit();
215
- this.allEquationFields = new BehaviorSubject<string[]>([]);
216
-
217
- this.updateEquationOptions();
218
- }
219
-
220
- formatTimeToDate(v: any){
221
- const fromTime: Date = new Date();
222
- if (v) {
223
- let split = v.split(":");
224
- fromTime.setHours(split[0], split[1]);
225
- }
226
- return new Date(fromTime.getTime());
227
- }
228
-
229
- patchLookupValue($event: LookupItem, fieldName: string) {
230
- if ($event?.id !== -1 && this.defaults) {
231
- // @ts-ignore
232
- this.defaults[fieldName] = this.formParam.get(fieldName).value;
233
- }
234
- }
235
- getBankName(fieldName: string) {
236
- //TODO Batool do we need this here ?
237
- if (this.formParam.get(fieldName).value?.length > 10 && !isNaN(this.formParam.get(fieldName).value) ) {
238
- return ' / ' + this.translateService.instant('BANK.' + this.formParam.get(fieldName).value?.substr(4, 4));
239
- } else {
240
- return '';
241
- }
242
- }
243
-
244
- patchMultiSelectValue($event: LookupItem[], fieldName: string) {
245
- if ($event?.length >= 0 && this.defaults) {
246
- this.defaults[fieldName] = this.formParam.get(fieldName).value;
247
- }
248
- }
249
-
250
- ngOnChanges({item, errors, fields}: SimpleChanges): void {
251
- errors?.currentValue?.forEach((msg: FieldMessage) => {
252
- this.formParam.get(msg.fieldName)?.setErrors({serverError: msg.message});
253
- });
254
-
255
- if (item?.currentValue && !item.isFirstChange()){
256
- this.defaults = item.currentValue;
257
- }
258
-
259
- if (fields?.currentValue && !fields.isFirstChange()){
260
- this.buildForm();
261
- }
262
- }
263
-
264
- showField(item: any, field: any) {
265
- return (item.id || (!item.id && !field.updateOnly)) && field.visible
266
- }
267
-
268
- workflowEditableField(field: { property: string; }) {
269
- const x = this.isWorkflowEditableManaged ? this.workflowEditableFields?.findIndex(d => d === field.property) > -1 : true;
270
- return x;
271
- }
272
-
273
- showMultiSelectValuesAsReadonly(field: FieldInfo) {
274
- const viewProp = this.translateService.getDefaultLang() === 'en' ? 'englishName' : 'arabicName';
275
- return this.extractFieldName(this.defaults,field.property)?.map((a: { [x: string]: any; }) => a[viewProp]);
276
- }
277
-
278
- splitReadonly(values: any) {
279
- return !values || values === '' ? []: values.split(';');
280
- }
281
-
282
-
283
- referenceAttributeTrue(fieldName: string, attribute: any) {
284
- // return this.authService.referenceAttributeTrue(fieldName, this.pageInfo.processId, attribute);
285
- }
286
-
287
- translateLable(itemElement: any) {
288
- return this.translateService.instant(itemElement)
289
- }
290
-
291
- buildForm() {
292
- if (this.fields && this.fields.length > 0 && this.defaults) {
293
- this.formParam = this.fb.group(buildFormFields(this.fields, this.defaults));
294
- this.formUpdated.next(this.formParam);
295
- }}
296
-
297
- labelKey(column: any) {
298
- const configuredKey = column.label ? column.label : column.property
299
- return configuredKey.startsWith(this.pageInfo.labelsSection) ? `${configuredKey}` : `${this.pageInfo.labelsSection}.${configuredKey}`;
300
- }
301
-
302
- getErrors(prop: string) {
303
-
304
- if (this.formParam.get(prop).value && this.formParam.get(prop).status !== 'VALID' && !this.formParam.get(prop).disabled) {
305
- return this.getFirstErrorKey(prop);
306
- // if (this.formParam.get(prop).errors?.['invalidMathExpression']) {
307
- // return 'invalidMathExpression'
308
- // } else {
309
- // return this.formParam.get(prop).errors?.['serverError'] || 'INVALID_FORMAT';
310
- // }
311
-
312
- // return this.formParam.get(prop).errors?.find(e => typeof e !== 'undefined')?.serverError || 'INVALID_FORMAT';
313
- }
314
- return '';
315
- }
316
-
317
- getFirstErrorKey(prop: string): string {
318
- const errors = this.formParam.get(prop)?.errors;
319
-
320
- if (!errors || Object.keys(errors).length === 0) {
321
- return errors?.['serverError'] || 'INVALID_FORMAT';
322
- }
323
-
324
- return Object.keys(errors)[0];
325
- }
326
-
327
- getCountryCodeList() {
328
- return ["jo"]
329
- }
330
-
331
-
332
- friendlyName(fullPath: string , subPath: string , item: string){
333
- return fullPath.substring(0, fullPath.indexOf(subPath)) + item ;
334
- }
335
-
336
- showValue(defaults, field){
337
- return this.friendlyName(field.label , field.property , defaults[field.property]);
338
- }
339
-
340
- showError(prop: string) {
341
- return this.formParam?.get(prop)?.value && this.formParam.get(prop).status === 'INVALID';
342
- }
343
-
344
- ngOnDestroy(): void {
345
- this.defaults = null;
346
- }
347
-
348
- private extractLables() {
349
- this.fields.forEach(f => {
350
- if (f.visible) {
351
- f.label = this.labelKey(f);
352
-
353
- if (!this.screenFields.has(f.row)) {
354
- this.screenFields.set(f.row, []);
355
- }
356
- this.screenFields.get(f.row)!.push(f);
357
-
358
- if (f.cascadedBy) {
359
- this.formParam?.get(f.cascadedBy)?.valueChanges.subscribe(() => {
360
- this.formParam?.get(f.property)?.patchValue(null);
361
- })
362
- }
363
- }
364
- });
365
-
366
- for (let i = 1; i < this.screenFields.size; i++) {
367
- this.calcFlexSize(i);
368
- }
369
- // console.log(this.screenFields)
370
- }
371
-
372
- private calcFlexSize(i: number){
373
- if (this.screenFields.get(i)) {
374
- const autoSizeFields = this.screenFields.get(i).filter(f => !f.fieldSize);
375
- const configLength = this.screenFields.get(i).filter(f => f.fieldSize > 0)
376
- .reduce((sum, obj) => {
377
- return sum + obj.fieldSize;
378
- }, 0);
379
- if (autoSizeFields.length > 0) {
380
- const x = (100 - configLength) / autoSizeFields.length;
381
- autoSizeFields.forEach(f => f.fieldSize = x);
382
- }
383
- }
384
- }
385
-
386
- getCurrencyPattern(field: FieldInfo) {
387
- // const decimalPlaces = this.authService?.myCurrencies?.find(c => c.key === this.formParam.get(field.currency)?.value)?.currencyNumOfDecimalPattern || 3;
388
- const decimalPlaces = 3;
389
- return '^\\d*(?:[.,]\\d{1,' + decimalPlaces + '})?$';
390
- }
391
-
392
- splittedChips(form: FormGroup, fieldName: string):string[] {
393
- return form.get(fieldName).value === '' ? []: form.get(fieldName).value.trim().split(';');
394
- }
395
-
396
-
397
- fixArabicNumbers(str: any) {
398
- // if(typeof str === 'string') {
399
- // str = str.replace(/([٠١٢٣٤٥٦٧٨٩])|([۰۱۲۳۴۵۶۷۸۹])/g, (m, $1, $2) => m.charCodeAt(0) - ($1 ? 1632 : 1776));
400
- // }
401
- // console.log(str);
402
- }
403
-
404
- listShowValue(defaults: any, field: FieldInfo) {
405
- return defaults[field.property] ? this.showValue(defaults, field) : '';
406
- }
407
-
408
- removeChipsItem(form: FormGroup, fieldName: string, idx: number) {
409
- const emails = this.splittedChips(form, fieldName);
410
- if (emails.length >= 1){
411
- emails.splice(idx,1);
412
- }
413
- let cleanedEmails = emails.join(";");
414
- form.get(fieldName).setValue(cleanedEmails);
415
- }
416
-
417
- extractFieldName(element: any, property: string) {
418
- if (property?.includes('.')) {
419
- return property.split('.').reduce((acc, part) => acc && acc[part], element);
420
- } else {
421
- return element[property];
422
- }
423
- }
424
-
425
- getMultiValue(field: FieldInfo) {
426
- return this.formParam.get(field.property)?.value;
427
- }
428
-
429
- private validateEmail(email) {
430
- var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
431
- return re.test(String(email).toLowerCase());
432
- }
433
-
434
- addEmailToChips(event: MatChipInputEvent, form: FormGroup, fieldName: string) {
435
-
436
- const input = event.input;
437
- const value = event.value;
438
- const items = value.split(';');
439
- // value.split(' ');
440
- items.forEach(itm => {
441
- if (this.validateEmail((itm || '').trim())) {
442
- const x = this.splittedChips(form, fieldName);
443
- x.push(itm);
444
- form.get(fieldName).patchValue(x.join(';'));
445
- if (input) {
446
- input.value = '';
447
- }
448
- }
449
- });
450
-
451
- }
452
-
453
- extractJsonFieldName(defaults: any, property: string) {
454
- const x= this.extractFieldName(defaults, property);
455
- if (x?.length > 0)
456
- return JSON.parse(x);
457
- else return '{}';
458
- }
459
-
460
- getOptionValue(item: any) {
461
- const value = typeof item === 'object' ?
462
- this.translateService.getDefaultLang() === 'en' ? item.englishName : item.arabicName
463
- : item;
464
- return value;
465
- }
466
-
467
- getHyperTextEvent() {
468
- this.hyperTextEvent.emit(true);
469
- }
470
-
471
- add(event: MatChipInputEvent): void {
472
- const value = (event.value || '').trim();
473
- if (value) {
474
- if (this.equationOperators.includes(value) || !this.equationValues().includes(value)) {
475
- this.equationValues.update(equationValues => [...equationValues, value]);
476
- }
477
- }
478
-
479
- this.fieldsInput.nativeElement.value = '';
480
- this.currentEquationValue.set('');
481
- this.updateEquationOptions();
482
-
483
- }
484
-
485
- remove(item: string, index: number): void {
486
- this.equationValues.update(equationValues => {
487
- if (index < 0) {
488
- return equationValues;
489
- }
490
-
491
- equationValues.splice(index, 1);
492
- this.announcer.announce(`Removed ${item}`);
493
- return [...equationValues];
494
- });
495
- this.updateEquationOptions();
496
- }
497
-
498
- selected(event: MatAutocompleteSelectedEvent): void{
499
- const selectedValue = event.option.viewValue.trim();
500
-
501
- if (!this.equationOperators.includes(selectedValue) && !this.equationValues().includes(selectedValue)) {
502
- this.equationValues.update(equationValues => [...equationValues, selectedValue]);
503
- } else if (this.equationOperators.includes(selectedValue)) {
504
- this.equationValues.update(equationValues => [...equationValues, selectedValue]);
505
- }
506
-
507
- this.currentEquationValue.set('');
508
- event.option.deselect();
509
- this.fieldsInput.nativeElement.value = '';
510
- this.updateEquationOptions();
511
- }
512
-
513
- updateEquationOptions(){
514
- this.reflectEquationValueOnForm();
515
-
516
- let updatedFields: string[];
517
- if (this.equationValues().length === 0) {
518
- updatedFields = this.getNamesFromSuggestedEquationFields();
519
- } else {
520
- let lastElement = this.equationValues()[this.equationValues().length - 1];
521
- if (this.equationOperators.includes(lastElement)) {
522
- updatedFields = this.getNamesFromSuggestedEquationFields();
523
- } else {
524
- updatedFields = this.equationOperators;
525
- }
526
- }
527
- // console.log('*****', updatedFields);
528
- this.allEquationFields.next(updatedFields);
529
- }
530
-
531
- getNamesFromSuggestedEquationFields(): string[] {
532
- return this.equationSuggestedFields?.map(field => field.name);
533
- }
534
-
535
- reflectEquationValueOnForm() {
536
- if (this.formParam && this.formParam.get('fieldValue')) {
537
- this.formParam.get('fieldValue').setValue(this.equationValues());
538
- } else {
539
- // console.log('Form or fieldValue is not available.');
540
- }
541
- }
542
-
543
- checkEquation(field: FieldInfo, $event: Event) {
544
- // console.log('$event', $event);
545
- // try {
546
- // evaluate(equation.replace(/[a-zA-Z0-9]+/g, '1')); // Replace variables with dummy values
547
- // this.formGroups[index].get('xyz').setErrors(null);
548
- // console.log('valid ', equation)
549
- // } catch (e) {
550
- // this.formGroups[index].get('xyz').setErrors({serverError: 'incomplete '});
551
- // console.log('bad ', equation)
552
- // }
553
- }
554
-
555
- getView(field): 'month' | 'year' | 'multi-year' {
556
- return field.pattern || 'month';
557
- }
558
-
559
- setMonthAndYear(normalizedMonthAndYear: Moment, picker: MatDatepicker<Moment>, field) {
560
- console.log('field.pattern', field.pattern)
561
- if (field.pattern === 'multi-year') {
562
- console.log('hi')
563
- const date = new FormControl(moment());
564
- const ctrlValue = date.value;
565
- ctrlValue.year(normalizedMonthAndYear.year());
566
- ctrlValue.month(1);
567
- ctrlValue.dayOfYear(1);
568
- this.formParam.get(field.property).setValue(ctrlValue)
569
- picker.close();
570
- }
571
- }
572
- }
573
-
1
+ import {
2
+ Component, ElementRef,
3
+ EventEmitter,
4
+ Input,
5
+ OnChanges,
6
+ OnDestroy,
7
+ OnInit,
8
+ Output,
9
+ SimpleChanges,
10
+ ViewChild,
11
+ computed,
12
+ model,
13
+ signal
14
+ } from '@angular/core';
15
+ import {FormBuilder, FormGroup, ReactiveFormsModule, FormsModule, FormControl} from "@angular/forms";
16
+ import {
17
+ MatChip,
18
+ MatChipGrid,
19
+ MatChipInput,
20
+ MatChipInputEvent,
21
+ MatChipListbox, MatChipOption, MatChipRemove,
22
+ MatChipRow
23
+ } from "@angular/material/chips";
24
+ import {FieldInfo} from "../../base-model/field-info";
25
+ import {ActivatedRoute} from "@angular/router";
26
+ import {BackendService} from "../../services/backend-service";
27
+ import {TranslateModule, TranslateService} from "@ngx-translate/core";
28
+ import {PageInfo} from "../../base-model/page-info";
29
+ import {COMMA, ENTER, SEMICOLON} from "@angular/cdk/keycodes";
30
+ import {LookupItem} from "../../base-model/lookupItem";
31
+ import {FieldMessage} from "../../base-model/api-response";
32
+ import {
33
+ AsyncPipe,
34
+ CurrencyPipe,
35
+ DatePipe,
36
+ KeyValuePipe,
37
+ NgForOf,
38
+ NgIf, NgStyle,
39
+ NgSwitch,
40
+ NgSwitchCase,
41
+ NgTemplateOutlet
42
+ } from "@angular/common";
43
+ import {MatError, MatFormFieldModule, MatLabel} from "@angular/material/form-field";
44
+ import {MatInputModule} from "@angular/material/input";
45
+ import {MatDatepicker, MatDatepickerInput, MatDatepickerToggle, MatDateRangeInput} from "@angular/material/datepicker";
46
+ import {MatIcon} from "@angular/material/icon";
47
+ import {CdkTextareaAutosize} from "@angular/cdk/text-field";
48
+ import {MatRadioButton, MatRadioGroup} from "@angular/material/radio";
49
+ import {MatCheckbox} from "@angular/material/checkbox";
50
+ import {MatOption, MatSelect} from "@angular/material/select";
51
+ import {MatDivider} from "@angular/material/divider";
52
+ import {NgxJsonViewerModule} from "ngx-json-viewer";
53
+ import {CKEditorModule} from "@ckeditor/ckeditor5-angular";
54
+ import {NgxMatIntlTelInputComponent} from "ngx-mat-intl-tel-input";
55
+ import {MultiAutoCompleteComponent} from "../multi-auto-complete/multi-auto-complete.component";
56
+ import {AutoCompleteComponent} from "../auto-complete/auto-complete.component";
57
+ import {MatAutocomplete, MatAutocompleteSelectedEvent, MatAutocompleteTrigger} from "@angular/material/autocomplete";
58
+ import {BehaviorSubject, map} from "rxjs";
59
+ import {LiveAnnouncer} from "@angular/cdk/a11y";
60
+ import {buildFormFields, extractFieldName} from "../../utils/base-utils";
61
+ import ClassicEditor from "@ckeditor/ckeditor5-build-classic"
62
+ import moment, {Moment} from "moment/moment";
63
+ import {MatCell, MatCellDef, MatHeaderCell, MatHeaderCellDef} from "@angular/material/table";
64
+ import {DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE} from "@angular/material/core";
65
+ import {MAT_MOMENT_DATE_ADAPTER_OPTIONS, MomentDateAdapter} from "@angular/material-moment-adapter";
66
+ export const DD_MM_YYYY_Format = {
67
+ parse: { dateInput: 'DD/MM/YYYY' },
68
+ display: {
69
+ dateInput: 'DD/MM/YYYY',
70
+ monthYearLabel: 'MMM YYYY',
71
+ dateA11yLabel: 'DD/MM/YYYY',
72
+ monthYearA11yLabel: 'MMMM YYYY',
73
+ },
74
+ };
75
+ @Component({
76
+ selector: 'app-base-form-canvas',
77
+ standalone: true,
78
+ imports: [
79
+ ReactiveFormsModule,
80
+ NgSwitch,
81
+ FormsModule,
82
+ NgForOf,
83
+ NgTemplateOutlet,
84
+ KeyValuePipe,
85
+ NgIf,
86
+ MatLabel,
87
+ MatFormFieldModule,
88
+ TranslateModule,
89
+ MatInputModule,
90
+ MatDatepickerInput,
91
+ MatDatepicker,
92
+ MatDatepickerToggle,
93
+ MatError,
94
+ NgSwitchCase,
95
+ MatIcon,
96
+ MatAutocomplete,
97
+ MatAutocompleteTrigger,
98
+ MatChip,
99
+ CdkTextareaAutosize,
100
+ MatRadioGroup,
101
+ MatCheckbox,
102
+ MatRadioButton,
103
+ MatSelect,
104
+ MatOption,
105
+ MatDivider,
106
+ DatePipe,
107
+ NgxJsonViewerModule,
108
+ CurrencyPipe,
109
+ CKEditorModule,
110
+ NgxMatIntlTelInputComponent,
111
+ MatChipGrid,
112
+ MatChipInput,
113
+ MatChipRow,
114
+ MatChipListbox,
115
+ MatChipOption,
116
+ MultiAutoCompleteComponent,
117
+ AutoCompleteComponent,
118
+ MatDateRangeInput,
119
+ AsyncPipe,
120
+ MatChipRemove,
121
+ MatCell,
122
+ MatCellDef,
123
+ MatHeaderCell,
124
+ MatHeaderCellDef,
125
+ NgStyle
126
+ ],
127
+ templateUrl: './base-form-canvas.component.html',
128
+ styleUrl: './base-form-canvas.component.css',
129
+ providers: [
130
+ {
131
+ provide: DateAdapter,
132
+ useClass: MomentDateAdapter,
133
+ deps: [MAT_DATE_LOCALE, MAT_MOMENT_DATE_ADAPTER_OPTIONS]
134
+ },
135
+ { provide: MAT_DATE_FORMATS, useValue: DD_MM_YYYY_Format },
136
+ { provide: MAT_DATE_LOCALE, useValue: 'en-GB' }
137
+ ]
138
+
139
+ })
140
+ export class BaseFormCanvasComponent implements OnInit, OnChanges, OnDestroy {
141
+ public Editor = ClassicEditor;
142
+ public hide: boolean = true;
143
+ dateViewMode: 'month' | 'year' | 'multi-year' = 'month';
144
+ @Input()
145
+ pageInfo!: PageInfo;
146
+
147
+
148
+ @Input()
149
+ currency: string;
150
+
151
+ @Input()// @ts-ignore
152
+ fields: FieldInfo[] = [];
153
+
154
+ @Input()// @ts-ignore
155
+ errors: FieldMessage[] = [];
156
+
157
+ @Input()
158
+ workflowEditableFields: string[]=[];
159
+ @Input()
160
+ isWorkflowEditableManaged: boolean = false;
161
+
162
+ @Input()
163
+ supportingAttributes: any;
164
+
165
+ @Input()
166
+ editable: boolean = false;
167
+
168
+ // When true, emit formUpdated on every value change. Default is false to avoid
169
+ // noisy emissions for all consumers. Item-line-editor will enable this explicitly.
170
+ @Input()
171
+ emitOnValueChanges: boolean = false;
172
+
173
+ @Input()
174
+ item: any = {};
175
+
176
+ _defaults: any = {};
177
+
178
+ @Output() attachmentEmitter: EventEmitter<any> = new EventEmitter<any>();
179
+
180
+ @Output() actionEmitter: EventEmitter<any> = new EventEmitter<any>();
181
+
182
+
183
+ @Output()
184
+ formUpdated: EventEmitter<FormGroup> = new EventEmitter<FormGroup>();
185
+
186
+ @Output()
187
+ hyperTextEvent: EventEmitter<boolean> = new EventEmitter();
188
+
189
+
190
+ formParam!: FormGroup;
191
+ screenFields = new Map<number, FieldInfo[]>();
192
+ removable = true;
193
+ selectable = true;
194
+ addOnBlur = true;
195
+
196
+ readonly separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON];
197
+
198
+
199
+ public isPending: boolean = false;
200
+ public isNew: boolean = false;
201
+ itemId: number | undefined;
202
+
203
+ equationOperators : string[] = ['+','-','(' ];
204
+ @Input() equationSuggestedFields: { name: string, id: string }[] = [];
205
+ allEquationFields: BehaviorSubject<string[]>;
206
+ readonly equationValues = signal<string[]>([]);
207
+ currentEquationValue = model<string>('');
208
+
209
+ readonly equationFilteredFields = computed(() => {
210
+ const currentEquationValue = this.currentEquationValue().toLowerCase();
211
+ return this.allEquationFields.pipe(
212
+ map(fields =>
213
+ currentEquationValue ? fields?.filter(field => field.toLowerCase().includes(currentEquationValue)) : fields?.slice()
214
+ )
215
+ );
216
+ });
217
+ @ViewChild('EquationValueInput') fieldsInput: ElementRef<HTMLInputElement>;
218
+
219
+ set defaults(value: any | undefined) {
220
+ // this.backendService._defaults = value;
221
+ this._defaults = value;
222
+ this.buildForm();
223
+ // this.myCallback = value;
224
+ }
225
+
226
+ get defaults(): any | undefined {
227
+ // this.myCallback;
228
+ return this._defaults;
229
+ // return this.backendService._defaults;
230
+ }
231
+
232
+ constructor(protected translateService: TranslateService,
233
+ protected activatedRoute: ActivatedRoute,
234
+ protected backendService: BackendService,
235
+ private fb: FormBuilder,
236
+ private announcer: LiveAnnouncer) {
237
+ ClassicEditor.defaultConfig = {
238
+ toolbar: {
239
+ items: ['heading', '|', 'bold', 'italic', '|', 'bulletedList', 'numberedList']
240
+ },
241
+ image: {
242
+ toolbar: ['imageStyle:side', '|', 'imageTextAlternative']
243
+ },
244
+ table: {
245
+ contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ]
246
+ },
247
+ language: 'en'
248
+ };
249
+ }
250
+
251
+ ngOnInit(): void {
252
+ if(this.item && this.item.fieldValue){
253
+ this.equationValues.update(value => this.item.fieldValue)
254
+ }
255
+ this.defaults = this.item;
256
+ this.extractLables();
257
+ this.buildForm();
258
+ this.activatedRoute.data.subscribe(data => {
259
+ this.isPending = data['mode'] === 'pending';
260
+ this.isNew = data['mode'] === 'new';
261
+ });
262
+ // this.authService.summaryPanelState$.next(Constants.SUMMARY_PANEL_CLOSED);
263
+ // this.defaults = {};
264
+ // this.invoiceQInit();
265
+ this.allEquationFields = new BehaviorSubject<string[]>([]);
266
+
267
+ this.updateEquationOptions();
268
+ }
269
+
270
+ formatTimeToDate(v: any){
271
+ const fromTime: Date = new Date();
272
+ if (v) {
273
+ let split = v.split(":");
274
+ fromTime.setHours(split[0], split[1]);
275
+ }
276
+ return new Date(fromTime.getTime());
277
+ }
278
+
279
+ patchLookupValue($event: LookupItem, fieldName: string) {
280
+ if ($event?.id !== -1 && this.defaults) {
281
+ // @ts-ignore
282
+ this.defaults[fieldName] = this.formParam.get(fieldName).value;
283
+ }
284
+ }
285
+ getBankName(fieldName: string) {
286
+ //TODO Batool do we need this here ?
287
+ if (this.formParam.get(fieldName).value?.length > 10 && !isNaN(this.formParam.get(fieldName).value) ) {
288
+ return ' / ' + this.translateService.instant('BANK.' + this.formParam.get(fieldName).value?.substr(4, 4));
289
+ } else {
290
+ return '';
291
+ }
292
+ }
293
+
294
+ patchMultiSelectValue($event: LookupItem[], fieldName: string) {
295
+ if ($event?.length >= 0 && this.defaults) {
296
+ this.defaults[fieldName] = this.formParam.get(fieldName).value;
297
+ }
298
+ }
299
+
300
+ ngOnChanges({item, errors, fields}: SimpleChanges): void {
301
+ errors?.currentValue?.forEach((msg: FieldMessage) => {
302
+ this.formParam.get(msg.fieldName)?.setErrors({serverError: msg.message});
303
+ });
304
+
305
+ if (item?.currentValue && !item.isFirstChange()){
306
+ this.defaults = item.currentValue;
307
+ }
308
+
309
+ if (fields?.currentValue && !fields.isFirstChange()){
310
+ this.buildForm();
311
+ }
312
+ }
313
+
314
+ showField(item: any, field: any) {
315
+ return (item.id || (!item.id && !field.updateOnly)) && field.visible
316
+ }
317
+
318
+ workflowEditableField(field: { property: string; }) {
319
+ const x = this.isWorkflowEditableManaged ? this.workflowEditableFields?.findIndex(d => d === field.property) > -1 : true;
320
+ return x;
321
+ }
322
+
323
+ showMultiSelectValuesAsReadonly(field: FieldInfo) {
324
+ const viewProp = this.translateService.getDefaultLang() === 'en' ? 'englishName' : 'arabicName';
325
+ return this.extractFieldName(this.defaults,field.property)?.map((a: { [x: string]: any; }) => a[viewProp]);
326
+ }
327
+
328
+ splitReadonly(values: any) {
329
+ return !values || values === '' ? []: values.split(';');
330
+ }
331
+
332
+
333
+ referenceAttributeTrue(fieldName: string, attribute: any) {
334
+ // return this.authService.referenceAttributeTrue(fieldName, this.pageInfo.processId, attribute);
335
+ }
336
+
337
+ translateLable(itemElement: any) {
338
+ return this.translateService.instant(itemElement)
339
+ }
340
+
341
+ buildForm() {
342
+ if (!this.fields || this.fields.length === 0 || !this.defaults) return;
343
+
344
+ if (!this.formParam) {
345
+ this.formParam = this.fb.group(buildFormFields(this.fields, this.defaults));
346
+ // Emit initial form reference
347
+ this.formUpdated.next(this.formParam);
348
+ // Emit on every value change so parent components can react live
349
+ if (this.emitOnValueChanges) {
350
+ this.formParam.valueChanges.subscribe(() => {
351
+ this.formUpdated.emit(this.formParam);
352
+ });
353
+ }
354
+ return;
355
+ }
356
+
357
+ const patchValues: any = {};
358
+ for (const field of this.fields) {
359
+ if (field.readonly) {
360
+ this.formParam.get(field.property)?.disable({ emitEvent: false });
361
+ } else {
362
+ this.formParam.get(field.property)?.enable({ emitEvent: false });
363
+ }
364
+ if (field.type !== 'spacer') {
365
+ patchValues[field.property] = extractFieldName(this.defaults, field.property);
366
+ }
367
+ }
368
+
369
+ this.formParam.patchValue(patchValues, { emitEvent: false });
370
+ }
371
+
372
+ labelKey(column: any) {
373
+ const configuredKey = column.label ? column.label : column.property
374
+ return configuredKey.startsWith(this.pageInfo.labelsSection) ? `${configuredKey}` : `${this.pageInfo.labelsSection}.${configuredKey}`;
375
+ }
376
+
377
+ labelKeyPlaceHolder(column: any) {
378
+ const configuredKey = column.placeholder ? column.placeholder : column.property
379
+ return configuredKey.startsWith(this.pageInfo.labelsSection) ? `${configuredKey}` : `${this.pageInfo.labelsSection}.${configuredKey}`;
380
+ }
381
+
382
+ getErrors(prop: string): string {
383
+ const control = this.formParam.get(prop);
384
+ if (!control || control.disabled) return '';
385
+
386
+ const field = this.fields.find(f => f.property === prop);
387
+
388
+ if (!field?.validationMessages) {
389
+ if (control.value && control.status !== 'VALID' && this.getFirstErrorKey) {
390
+ return this.getFirstErrorKey(prop);
391
+ }
392
+ return '';
393
+ }
394
+
395
+ if (control.invalid) {
396
+ const errors = control.errors || {};
397
+ const prefix = this.pageInfo.labelsSection + ".";
398
+
399
+ if (errors['required']) return prefix + 'REQUIRED';
400
+ if (errors['min']) return prefix + (field.validationMessages?.min || 'NUMBER_MIN');
401
+ if (errors['max']) return prefix + (field.validationMessages?.max || 'NUMBER_MAX');
402
+ if (errors['maxlength']) return prefix + (field.validationMessages?.maxlength || 'MAX_LENGTH');
403
+ if (errors['decimalMin']) return prefix + (field.validationMessages?.decimalMin || 'DECIMAL_MIN');
404
+ if (errors['decimalMax']) return prefix + (field.validationMessages?.decimalMax || 'DECIMAL_MAX');
405
+ if (errors['pattern']) return prefix + (field.validationMessages?.pattern || 'INVALID_FORMAT');
406
+ if (errors['invalidMathExpression']) return prefix + 'INVALID_MATH';
407
+ if (errors['serverError']) return prefix + errors['serverError'];
408
+
409
+ return prefix + 'INVALID_VALUE';
410
+ }
411
+
412
+ return '';
413
+ }
414
+
415
+
416
+ getFirstErrorKey(prop: string): string {
417
+ const errors = this.formParam.get(prop)?.errors;
418
+
419
+ if (!errors || Object.keys(errors).length === 0) {
420
+ return errors?.['serverError'] || 'INVALID_FORMAT';
421
+ }
422
+
423
+ return Object.keys(errors)[0];
424
+ }
425
+
426
+ public getCountryCodeList() {
427
+ return ['af', 'ax', 'al', 'dz', 'as', 'ad', 'ao', 'ai', 'aq', 'ag', 'ar', 'am', 'aw', 'au', 'at', 'az',
428
+ 'bs', 'bh', 'bd', 'bb', 'by', 'be', 'bz', 'bj', 'bm', 'bt', 'bo', 'ba', 'bw', 'bv', 'br', 'io', 'bn',
429
+ 'bg', 'bf', 'bi', 'kh', 'cm', 'ca', 'cv', 'ky', 'cf', 'td', 'cl', 'cn', 'cx', 'cc', 'co', 'km', 'cg',
430
+ 'cd', 'ck', 'cr', 'ci', 'hr', 'cu', 'cy', 'cz', 'dk', 'dj', 'dm', 'do', 'ec', 'eg', 'sv', 'gq', 'er',
431
+ 'ee', 'et', 'fk', 'fo', 'fj', 'fi', 'fr', 'gf', 'pf', 'tf', 'ga', 'gm', 'ge', 'de', 'gh', 'gi', 'gr',
432
+ 'gl', 'gd', 'gp', 'gu', 'gt', 'gg', 'gn', 'gw', 'gy', 'ht', 'hm', 'va', 'hn', 'hk', 'hu', 'is', 'in',
433
+ 'id', 'ir', 'iq', 'ie', 'im', 'it', 'jm', 'jp', 'je', 'jo', 'kz', 'ke', 'ki', 'kr', 'kw', 'kg', 'la',
434
+ 'lv', 'lb', 'ls', 'lr', 'ly', 'li', 'lt', 'lu', 'mo', 'mk', 'mg', 'mw', 'my', 'mv', 'ml', 'mt', 'mh',
435
+ 'mq', 'mr', 'mu', 'yt', 'mx', 'fm', 'md', 'mc', 'mn', 'me', 'ms', 'ma', 'mz', 'mm', 'na', 'nr', 'np',
436
+ 'nl', 'an', 'nc', 'nz', 'ni', 'ne', 'ng', 'nu', 'nf', 'mp', 'no', 'om', 'pk', 'pw', 'ps', 'pa', 'pg',
437
+ 'py', 'pe', 'ph', 'pn', 'pl', 'pt', 'pr', 'qa', 're', 'ro', 'ru', 'rw', 'bl', 'sh', 'kn', 'lc', 'mf',
438
+ 'pm', 'vc', 'ws', 'sm', 'st', 'sa', 'sn', 'rs', 'sc', 'sl', 'sg', 'sk', 'si', 'sb', 'so', 'za', 'gs',
439
+ 'es', 'lk', 'sd', 'sr', 'sj', 'sz', 'se', 'ch', 'sy', 'tw', 'tj', 'tz', 'th', 'tl', 'tg', 'tk', 'to',
440
+ 'tt', 'tn', 'tr', 'tm', 'tc', 'tv', 'ug', 'ua', 'ae', 'gb', 'us', 'um', 'uy', 'uz', 'vu', 've', 'vn',
441
+ 'vg', 'vi', 'wf', 'eh', 'ye', 'zm', 'zw'];
442
+ }
443
+
444
+
445
+ friendlyName(fullPath: string , subPath: string , item: string){
446
+ return fullPath.substring(0, fullPath.indexOf(subPath)) + item ;
447
+ }
448
+
449
+ showValue(defaults, field){
450
+ return this.friendlyName(field.label , field.property , defaults[field.property]);
451
+ }
452
+
453
+ showError(prop: string) {
454
+ return (this.formParam?.get(prop)?.value || this.formParam?.get(prop)?.value == 0)
455
+ && this.formParam.get(prop).status === 'INVALID';
456
+ }
457
+
458
+ ngOnDestroy(): void {
459
+ this.defaults = null;
460
+ }
461
+
462
+ private extractLables() {
463
+ this.fields.forEach(f => {
464
+ if (f.visible) {
465
+ f.label = this.labelKey(f);
466
+
467
+ if (!this.screenFields.has(f.row)) {
468
+ this.screenFields.set(f.row, []);
469
+ }
470
+ this.screenFields.get(f.row)!.push(f);
471
+
472
+ if (f.cascadedBy) {
473
+ this.formParam?.get(f.cascadedBy)?.valueChanges.subscribe(() => {
474
+ this.formParam?.get(f.property)?.patchValue(null);
475
+ })
476
+ }
477
+ }
478
+ });
479
+
480
+ for (let i = 1; i < this.screenFields.size; i++) {
481
+ this.calcFlexSize(i);
482
+ }
483
+ // console.log(this.screenFields)
484
+ }
485
+
486
+ private calcFlexSize(i: number){
487
+ // Preserve explicit fieldSize values only. For fields without fieldSize, allow
488
+ // the layout to auto-size via flex (handled in the template/CSS).
489
+ // Intentionally do nothing here so that unspecified fields remain flexible.
490
+ return;
491
+ }
492
+
493
+ getCurrencyPattern(field: FieldInfo) {
494
+ // const decimalPlaces = this.authService?.myCurrencies?.find(c => c.key === this.formParam.get(field.currency)?.value)?.currencyNumOfDecimalPattern || 3;
495
+ const decimalPlaces = 3;
496
+ return '^\\d*(?:[.,]\\d{1,' + decimalPlaces + '})?$';
497
+ }
498
+
499
+ splittedChips(form: FormGroup, fieldName: string):string[] {
500
+ return form.get(fieldName).value === '' ? []: form.get(fieldName).value.trim().split(';');
501
+ }
502
+
503
+
504
+ fixArabicNumbers(str: any) {
505
+ // if(typeof str === 'string') {
506
+ // str = str.replace(/([٠١٢٣٤٥٦٧٨٩])|([۰۱۲۳۴۵۶۷۸۹])/g, (m, $1, $2) => m.charCodeAt(0) - ($1 ? 1632 : 1776));
507
+ // }
508
+ // console.log(str);
509
+ }
510
+
511
+ listShowValue(defaults: any, field: FieldInfo) {
512
+ return defaults[field.property] ? this.showValue(defaults, field) : '';
513
+ }
514
+
515
+ removeChipsItem(form: FormGroup, fieldName: string, idx: number) {
516
+ const emails = this.splittedChips(form, fieldName);
517
+ if (emails.length >= 1){
518
+ emails.splice(idx,1);
519
+ }
520
+ let cleanedEmails = emails.join(";");
521
+ form.get(fieldName).setValue(cleanedEmails);
522
+ }
523
+
524
+ extractFieldName(element: any, property: string) {
525
+ if (property?.includes('.')) {
526
+ return property.split('.').reduce((acc, part) => acc && acc[part], element);
527
+ } else {
528
+ return element[property];
529
+ }
530
+ }
531
+
532
+ getMultiValue(field: FieldInfo) {
533
+ return this.formParam.get(field.property)?.value;
534
+ }
535
+
536
+ private validateEmail(email) {
537
+ var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
538
+ return re.test(String(email).toLowerCase());
539
+ }
540
+
541
+ addEmailToChips(event: MatChipInputEvent, form: FormGroup, fieldName: string) {
542
+
543
+ const input = event.input;
544
+ const value = event.value;
545
+ const items = value.split(';');
546
+ // value.split(' ');
547
+ items.forEach(itm => {
548
+ if (this.validateEmail((itm || '').trim())) {
549
+ const x = this.splittedChips(form, fieldName);
550
+ x.push(itm);
551
+ form.get(fieldName).patchValue(x.join(';'));
552
+ if (input) {
553
+ input.value = '';
554
+ }
555
+ }
556
+ });
557
+
558
+ }
559
+
560
+ extractJsonFieldName(defaults: any, property: string) {
561
+ const x= this.extractFieldName(defaults, property);
562
+ if (x?.length > 0)
563
+ return JSON.parse(x);
564
+ else return '{}';
565
+ }
566
+
567
+ getOptionValue(item: any) {
568
+ const value = typeof item === 'object' ?
569
+ this.translateService.getDefaultLang() === 'en' ? item.englishName : item.arabicName
570
+ : item;
571
+ return value;
572
+ }
573
+
574
+ getHyperTextEvent() {
575
+ this.hyperTextEvent.emit(true);
576
+ }
577
+
578
+ add(event: MatChipInputEvent): void {
579
+ const value = (event.value || '').trim();
580
+ if (value) {
581
+ if (this.equationOperators.includes(value) || !this.equationValues().includes(value)) {
582
+ this.equationValues.update(equationValues => [...equationValues, value]);
583
+ }
584
+ }
585
+
586
+ this.fieldsInput.nativeElement.value = '';
587
+ this.currentEquationValue.set('');
588
+ this.updateEquationOptions();
589
+
590
+ }
591
+
592
+ remove(item: string, index: number): void {
593
+ this.equationValues.update(equationValues => {
594
+ if (index < 0) {
595
+ return equationValues;
596
+ }
597
+
598
+ equationValues.splice(index, 1);
599
+ this.announcer.announce(`Removed ${item}`);
600
+ return [...equationValues];
601
+ });
602
+ this.updateEquationOptions();
603
+ }
604
+
605
+ selected(event: MatAutocompleteSelectedEvent): void{
606
+ const selectedValue = event.option.viewValue.trim();
607
+
608
+ if (!this.equationOperators.includes(selectedValue) && !this.equationValues().includes(selectedValue)) {
609
+ this.equationValues.update(equationValues => [...equationValues, selectedValue]);
610
+ } else if (this.equationOperators.includes(selectedValue)) {
611
+ this.equationValues.update(equationValues => [...equationValues, selectedValue]);
612
+ }
613
+
614
+ this.currentEquationValue.set('');
615
+ event.option.deselect();
616
+ this.fieldsInput.nativeElement.value = '';
617
+ this.updateEquationOptions();
618
+ }
619
+
620
+ updateEquationOptions(){
621
+ this.reflectEquationValueOnForm();
622
+
623
+ let updatedFields: string[];
624
+ if (this.equationValues().length === 0) {
625
+ updatedFields = this.getNamesFromSuggestedEquationFields();
626
+ } else {
627
+ let lastElement = this.equationValues()[this.equationValues().length - 1];
628
+ if (this.equationOperators.includes(lastElement)) {
629
+ updatedFields = this.getNamesFromSuggestedEquationFields();
630
+ } else {
631
+ updatedFields = this.equationOperators;
632
+ }
633
+ }
634
+ // console.log('*****', updatedFields);
635
+ this.allEquationFields.next(updatedFields);
636
+ }
637
+
638
+ getNamesFromSuggestedEquationFields(): string[] {
639
+ return this.equationSuggestedFields?.map(field => field.name);
640
+ }
641
+
642
+ reflectEquationValueOnForm() {
643
+ if (this.formParam && this.formParam.get('fieldValue')) {
644
+ this.formParam.get('fieldValue').setValue(this.equationValues());
645
+ } else {
646
+ // console.log('Form or fieldValue is not available.');
647
+ }
648
+ }
649
+
650
+ checkEquation(field: FieldInfo, $event: Event) {
651
+ // console.log('$event', $event);
652
+ // try {
653
+ // evaluate(equation.replace(/[a-zA-Z0-9]+/g, '1')); // Replace variables with dummy values
654
+ // this.formGroups[index].get('xyz').setErrors(null);
655
+ // console.log('valid ', equation)
656
+ // } catch (e) {
657
+ // this.formGroups[index].get('xyz').setErrors({serverError: 'incomplete '});
658
+ // console.log('bad ', equation)
659
+ // }
660
+ }
661
+
662
+ getView(field): 'month' | 'year' | 'multi-year' {
663
+ return field.pattern || 'month';
664
+ }
665
+
666
+ setMonthAndYear(normalizedMonthAndYear: Moment, picker: MatDatepicker<Moment>, field) {
667
+ console.log('field.pattern', field.pattern)
668
+ if (field.pattern === 'multi-year') {
669
+ console.log('hi')
670
+ const date = new FormControl(moment());
671
+ const ctrlValue = date.value;
672
+ ctrlValue.year(normalizedMonthAndYear.year());
673
+ ctrlValue.month(1);
674
+ ctrlValue.dayOfYear(1);
675
+ this.formParam.get(field.property).setValue(ctrlValue)
676
+ picker.close();
677
+ }
678
+ }
679
+ }
680
+