@codex-ts/core-lib 1.0.0
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.
- package/dist/core-lib/fesm2022/core-lib.mjs +2349 -0
- package/dist/core-lib/fesm2022/core-lib.mjs.map +1 -0
- package/dist/core-lib/index.d.ts +29 -0
- package/dist/core-lib/lib/components/base-form.component.d.ts +62 -0
- package/dist/core-lib/lib/components/base-list-page.component.d.ts +132 -0
- package/dist/core-lib/lib/components/base-page.component.d.ts +25 -0
- package/dist/core-lib/lib/components/base-tabbed-form.component.d.ts +54 -0
- package/dist/core-lib/lib/components/formly/core-formly.module.d.ts +7 -0
- package/dist/core-lib/lib/components/formly/form-field.wrapper.d.ts +21 -0
- package/dist/core-lib/lib/components/formly/formly.constants.d.ts +13 -0
- package/dist/core-lib/lib/components/formly/types/checkbox.type.d.ts +12 -0
- package/dist/core-lib/lib/components/formly/types/date.type.d.ts +20 -0
- package/dist/core-lib/lib/components/formly/types/multiselect.type.d.ts +26 -0
- package/dist/core-lib/lib/components/formly/types/numeric-input.type.d.ts +17 -0
- package/dist/core-lib/lib/components/formly/types/radio.type.d.ts +13 -0
- package/dist/core-lib/lib/components/formly/types/select.type.d.ts +25 -0
- package/dist/core-lib/lib/components/formly/types/text-input.type.d.ts +12 -0
- package/dist/core-lib/lib/components/formly/types/textarea.type.d.ts +14 -0
- package/dist/core-lib/lib/constants/app.constants.d.ts +13 -0
- package/dist/core-lib/lib/constants/app.messages.d.ts +36 -0
- package/dist/core-lib/lib/enums/component-context.enum.d.ts +6 -0
- package/dist/core-lib/lib/enums/tabbed-form-type.enum.d.ts +4 -0
- package/dist/core-lib/lib/models/pagination/filter-criteria.interface.d.ts +9 -0
- package/dist/core-lib/lib/models/pagination/page-request.interface.d.ts +10 -0
- package/dist/core-lib/lib/models/pagination/page.interface.d.ts +9 -0
- package/dist/core-lib/lib/models/pagination/sort-criteria.interface.d.ts +4 -0
- package/dist/core-lib/lib/pipes/indian-date.pipe.d.ts +12 -0
- package/dist/core-lib/lib/services/base-crud.service.d.ts +36 -0
- package/dist/core-lib/lib/services/entity-registry.service.d.ts +26 -0
- package/dist/core-lib/lib/services/entity.service.d.ts +9 -0
- package/dist/core-lib/lib/services/enum-registry.service.d.ts +41 -0
- package/dist/core-lib/lib/services/formly-config.service.d.ts +11 -0
- package/dist/core-lib/lib/services/http-utility.service.d.ts +130 -0
- package/dist/core-lib/lib/services/notification.service.d.ts +13 -0
- package/dist/core-lib/lib/services/reference-data-provider.service.d.ts +8 -0
- package/dist/core-lib/lib/services/utils.service.d.ts +72 -0
- package/ng-package.json +7 -0
- package/package.json +63 -0
- package/src/index.ts +44 -0
- package/src/lib/components/base-form.component.ts +328 -0
- package/src/lib/components/base-list-page.component.ts +327 -0
- package/src/lib/components/base-page.component.ts +33 -0
- package/src/lib/components/base-tabbed-form.component.ts +345 -0
- package/src/lib/components/formly/core-formly.module.ts +34 -0
- package/src/lib/components/formly/field.interface.ts +15 -0
- package/src/lib/components/formly/form-field.wrapper.ts +94 -0
- package/src/lib/components/formly/formly.constants.ts +14 -0
- package/src/lib/components/formly/primeng-formly.module.ts +77 -0
- package/src/lib/components/formly/types/checkbox.type.ts +43 -0
- package/src/lib/components/formly/types/date.type.ts +88 -0
- package/src/lib/components/formly/types/multiselect.type.ts +61 -0
- package/src/lib/components/formly/types/numeric-input.type.ts +45 -0
- package/src/lib/components/formly/types/radio.type.ts +47 -0
- package/src/lib/components/formly/types/select.type.ts +54 -0
- package/src/lib/components/formly/types/text-input.type.ts +33 -0
- package/src/lib/components/formly/types/textarea.type.ts +37 -0
- package/src/lib/constants/app.constants.ts +15 -0
- package/src/lib/constants/app.messages.ts +36 -0
- package/src/lib/enums/component-context.enum.ts +6 -0
- package/src/lib/enums/tabbed-form-type.enum.ts +4 -0
- package/src/lib/models/pagination/filter-criteria.interface.ts +19 -0
- package/src/lib/models/pagination/page-request.interface.ts +13 -0
- package/src/lib/models/pagination/page.interface.ts +9 -0
- package/src/lib/models/pagination/sort-criteria.interface.ts +4 -0
- package/src/lib/pipes/indian-date.pipe.ts +46 -0
- package/src/lib/services/base-crud.service.ts +81 -0
- package/src/lib/services/entity-registry.service.ts +51 -0
- package/src/lib/services/entity.service.ts +10 -0
- package/src/lib/services/enum-registry.service.ts +66 -0
- package/src/lib/services/formly-config.service.ts +50 -0
- package/src/lib/services/http-utility.service.example.ts +253 -0
- package/src/lib/services/http-utility.service.md +175 -0
- package/src/lib/services/http-utility.service.ts +389 -0
- package/src/lib/services/notification.service.ts +35 -0
- package/src/lib/services/reference-data-provider.service.ts +28 -0
- package/src/lib/services/utils.service.ts +154 -0
|
@@ -0,0 +1,2349 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Injectable, Directive, Input, Component, ViewChild, Pipe, NgModule } from '@angular/core';
|
|
3
|
+
import { BehaviorSubject, finalize, Observable, forkJoin, throwError, map, take, of, isObservable } from 'rxjs';
|
|
4
|
+
import * as i1 from 'primeng/api';
|
|
5
|
+
import * as i1$1 from '@angular/common/http';
|
|
6
|
+
import { HttpParams } from '@angular/common/http';
|
|
7
|
+
import { retry, timeout, catchError, finalize as finalize$1 } from 'rxjs/operators';
|
|
8
|
+
import * as i1$4 from '@angular/forms';
|
|
9
|
+
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
|
|
10
|
+
import * as i1$2 from '@angular/router';
|
|
11
|
+
import * as i1$3 from '@angular/common';
|
|
12
|
+
import { CommonModule, formatDate } from '@angular/common';
|
|
13
|
+
import * as i2 from '@ngx-formly/core';
|
|
14
|
+
import { FieldType, FormlyModule, FieldWrapper } from '@ngx-formly/core';
|
|
15
|
+
import * as i3 from 'primeng/inputtext';
|
|
16
|
+
import { InputTextModule } from 'primeng/inputtext';
|
|
17
|
+
import * as i4 from 'primeng/select';
|
|
18
|
+
import { SelectModule } from 'primeng/select';
|
|
19
|
+
import * as i4$1 from 'primeng/radiobutton';
|
|
20
|
+
import { RadioButtonModule } from 'primeng/radiobutton';
|
|
21
|
+
import * as i3$1 from 'primeng/inputnumber';
|
|
22
|
+
import { InputNumberModule } from 'primeng/inputnumber';
|
|
23
|
+
import * as i4$2 from 'primeng/multiselect';
|
|
24
|
+
import { MultiSelectModule } from 'primeng/multiselect';
|
|
25
|
+
import * as i3$2 from 'primeng/datepicker';
|
|
26
|
+
import { DatePickerModule } from 'primeng/datepicker';
|
|
27
|
+
import * as i3$3 from 'primeng/checkbox';
|
|
28
|
+
import { CheckboxModule } from 'primeng/checkbox';
|
|
29
|
+
import * as i3$4 from 'primeng/inputtextarea';
|
|
30
|
+
import { Textarea } from 'primeng/inputtextarea';
|
|
31
|
+
import * as i2$1 from 'primeng/tooltip';
|
|
32
|
+
import { TooltipModule } from 'primeng/tooltip';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A utility service that provides common reusable functionality
|
|
36
|
+
* such as UUID validation and error message display
|
|
37
|
+
*/
|
|
38
|
+
class UtilsService {
|
|
39
|
+
/**
|
|
40
|
+
* Validates if a string is a valid UUID
|
|
41
|
+
* @param uuid The string to validate
|
|
42
|
+
* @returns boolean indicating if the string is a valid UUID
|
|
43
|
+
*/
|
|
44
|
+
isValidUUID(uuid) {
|
|
45
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
46
|
+
return uuidRegex.test(uuid);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Shows an error message using PrimeNG's MessageService
|
|
50
|
+
* @param message The error message to display
|
|
51
|
+
* @param messageService The MessageService instance to use
|
|
52
|
+
*/
|
|
53
|
+
showError(message, messageService) {
|
|
54
|
+
if (messageService) {
|
|
55
|
+
messageService.add({
|
|
56
|
+
severity: 'error',
|
|
57
|
+
summary: 'Error',
|
|
58
|
+
detail: message,
|
|
59
|
+
life: 3000
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Shows a success message using PrimeNG's MessageService
|
|
65
|
+
* @param message The success message to display
|
|
66
|
+
* @param messageService The MessageService instance to use
|
|
67
|
+
*/
|
|
68
|
+
showSuccess(message, messageService) {
|
|
69
|
+
if (messageService) {
|
|
70
|
+
messageService.add({
|
|
71
|
+
severity: 'success',
|
|
72
|
+
summary: 'Success',
|
|
73
|
+
detail: message,
|
|
74
|
+
life: 3000
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Gets the display value for an enum key or array of enum keys
|
|
80
|
+
* @param enumType The enum type
|
|
81
|
+
* @param key The enum key or array of enum keys
|
|
82
|
+
* @returns The display value for the enum key or comma-separated list of display values for array of keys
|
|
83
|
+
*/
|
|
84
|
+
getEnumValue(enumType, key) {
|
|
85
|
+
if (!key)
|
|
86
|
+
return '';
|
|
87
|
+
if (Array.isArray(key)) {
|
|
88
|
+
// Handle array of keys - map each to its display value and join with comma
|
|
89
|
+
return key.map(k => enumType[k] || k).join(', ');
|
|
90
|
+
}
|
|
91
|
+
// Handle single key
|
|
92
|
+
return enumType[key] || key;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Gets all values from an enum type for use in formly select fields
|
|
96
|
+
* @param enumType The enum type to get values from
|
|
97
|
+
* @returns Array of {label, value} pairs for formly select options
|
|
98
|
+
*/
|
|
99
|
+
getEnumValues(enumType) {
|
|
100
|
+
return Object.entries(enumType)
|
|
101
|
+
.filter(([key]) => isNaN(Number(key))) // Filter out reverse mappings
|
|
102
|
+
.map(([key, value]) => ({
|
|
103
|
+
value: key, // enum key as the value
|
|
104
|
+
label: value // enum value as the label
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Converts a camelCase string to Title Case with spaces
|
|
109
|
+
* @param camelCase The camelCase string to convert
|
|
110
|
+
* @returns The Title Case string
|
|
111
|
+
*/
|
|
112
|
+
camelCaseToTitleCase(camelCase) {
|
|
113
|
+
if (!camelCase)
|
|
114
|
+
return '';
|
|
115
|
+
const spacedString = camelCase.replace(/([a-z])([A-Z])/g, '$1 $2');
|
|
116
|
+
return spacedString.charAt(0).toUpperCase() + spacedString.slice(1);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Converts a string to kebab-case
|
|
120
|
+
* @param input The string to convert
|
|
121
|
+
* @returns The kebab-case string
|
|
122
|
+
*/
|
|
123
|
+
toKebabCase(input) {
|
|
124
|
+
return input
|
|
125
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
126
|
+
.replace(/([A-Z])([A-Z])(?=[a-z])/g, '$1-$2')
|
|
127
|
+
.toLowerCase();
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Converts a string to camelCase.
|
|
131
|
+
* Example: "loan-application" becomes "loanApplication"
|
|
132
|
+
* @param input The string to convert
|
|
133
|
+
* @returns The camelCase string
|
|
134
|
+
*/
|
|
135
|
+
toCamelCase(input) {
|
|
136
|
+
if (!input)
|
|
137
|
+
return '';
|
|
138
|
+
// Split by non-alphanumeric chars
|
|
139
|
+
const words = input.split(/[^a-zA-Z0-9]/);
|
|
140
|
+
const result = [words[0].toLowerCase()];
|
|
141
|
+
// Capitalize first letter of remaining words
|
|
142
|
+
for (let i = 1; i < words.length; i++) {
|
|
143
|
+
const word = words[i];
|
|
144
|
+
if (word) {
|
|
145
|
+
result.push(word.charAt(0).toUpperCase() + word.substring(1).toLowerCase());
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return result.join('');
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Removes double forward slashes from an endpoint string while preserving the protocol
|
|
152
|
+
* e.g. "http://localhost:8080//api//v1//users" -> "http://localhost:8080/api/v1/users"
|
|
153
|
+
* @param endpoint The endpoint string to clean
|
|
154
|
+
* @returns The cleaned endpoint string without double slashes
|
|
155
|
+
*/
|
|
156
|
+
removeDoubleSlashes(endpoint) {
|
|
157
|
+
if (!endpoint)
|
|
158
|
+
return '';
|
|
159
|
+
// Split the URL into protocol and the rest to preserve protocol slashes
|
|
160
|
+
const [protocol, ...rest] = endpoint.split('://');
|
|
161
|
+
if (!rest.length) {
|
|
162
|
+
// If no protocol is present, just clean the entire string
|
|
163
|
+
return endpoint.replace(/\/+/g, '/');
|
|
164
|
+
}
|
|
165
|
+
// Clean the path part (after protocol) and reconstruct the URL
|
|
166
|
+
const cleanPath = rest.join('://').replace(/\/+/g, '/');
|
|
167
|
+
return `${protocol}://${cleanPath}`;
|
|
168
|
+
}
|
|
169
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: UtilsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
170
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: UtilsService, providedIn: 'root' });
|
|
171
|
+
}
|
|
172
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: UtilsService, decorators: [{
|
|
173
|
+
type: Injectable,
|
|
174
|
+
args: [{
|
|
175
|
+
providedIn: 'root'
|
|
176
|
+
}]
|
|
177
|
+
}] });
|
|
178
|
+
|
|
179
|
+
class EnumRegistryService {
|
|
180
|
+
utilsService;
|
|
181
|
+
enumMap = new Map();
|
|
182
|
+
constructor(utilsService) {
|
|
183
|
+
this.utilsService = utilsService;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Initialize global enums
|
|
187
|
+
*/
|
|
188
|
+
static registerGlobalEnums(enums) {
|
|
189
|
+
return {
|
|
190
|
+
provide: EnumRegistryService,
|
|
191
|
+
useFactory: (utilsService) => {
|
|
192
|
+
const service = new EnumRegistryService(utilsService);
|
|
193
|
+
Object.entries(enums).forEach(([name, enumType]) => {
|
|
194
|
+
service.register(name, enumType);
|
|
195
|
+
});
|
|
196
|
+
return service;
|
|
197
|
+
},
|
|
198
|
+
deps: [UtilsService]
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Register an enum type with the service
|
|
203
|
+
* @param name Name to register the enum under
|
|
204
|
+
* @param enumType The enum type to register
|
|
205
|
+
*/
|
|
206
|
+
register(name, enumType) {
|
|
207
|
+
this.enumMap.set(name, enumType);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Get formly-compatible options array for a registered enum
|
|
211
|
+
* @param name Name of the registered enum
|
|
212
|
+
* @returns Array of {label, value} pairs suitable for formly select fields
|
|
213
|
+
*/
|
|
214
|
+
getEnumValues(name) {
|
|
215
|
+
const enumType = this.enumMap.get(name);
|
|
216
|
+
if (!enumType) {
|
|
217
|
+
console.warn(`Enum type "${name}" not found in registry`);
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
return this.utilsService.getEnumValues(enumType);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Check if an enum type is registered
|
|
224
|
+
* @param name Name of the enum to check
|
|
225
|
+
*/
|
|
226
|
+
hasEnum(name) {
|
|
227
|
+
return this.enumMap.has(name);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Clear all registered enums
|
|
231
|
+
*/
|
|
232
|
+
clear() {
|
|
233
|
+
this.enumMap.clear();
|
|
234
|
+
}
|
|
235
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EnumRegistryService, deps: [{ token: UtilsService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
236
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EnumRegistryService, providedIn: 'root' });
|
|
237
|
+
}
|
|
238
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EnumRegistryService, decorators: [{
|
|
239
|
+
type: Injectable,
|
|
240
|
+
args: [{
|
|
241
|
+
providedIn: 'root'
|
|
242
|
+
}]
|
|
243
|
+
}], ctorParameters: () => [{ type: UtilsService }] });
|
|
244
|
+
|
|
245
|
+
class FormlyConfigService {
|
|
246
|
+
enumRegistry;
|
|
247
|
+
constructor(enumRegistry) {
|
|
248
|
+
this.enumRegistry = enumRegistry;
|
|
249
|
+
}
|
|
250
|
+
getFormConfig(fields, mode, dynamicOptions) {
|
|
251
|
+
const commonConfig = {
|
|
252
|
+
props: {
|
|
253
|
+
disabled: mode === 'view'
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
return fields.map((field) => {
|
|
257
|
+
// Apply dynamic options from child component
|
|
258
|
+
if (dynamicOptions && field.key && typeof field.key === 'string') {
|
|
259
|
+
const fieldOptions = dynamicOptions[field.key];
|
|
260
|
+
if (fieldOptions) {
|
|
261
|
+
field.props = {
|
|
262
|
+
...field.props,
|
|
263
|
+
...fieldOptions
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// Handle field groups recursively
|
|
268
|
+
if (field.fieldGroup) {
|
|
269
|
+
field.fieldGroup = this.getFormConfig(field.fieldGroup, mode, dynamicOptions);
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
...field,
|
|
273
|
+
...commonConfig,
|
|
274
|
+
props: {
|
|
275
|
+
...field.props,
|
|
276
|
+
...commonConfig.props
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: FormlyConfigService, deps: [{ token: EnumRegistryService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
282
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: FormlyConfigService, providedIn: 'root' });
|
|
283
|
+
}
|
|
284
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: FormlyConfigService, decorators: [{
|
|
285
|
+
type: Injectable,
|
|
286
|
+
args: [{
|
|
287
|
+
providedIn: 'root'
|
|
288
|
+
}]
|
|
289
|
+
}], ctorParameters: () => [{ type: EnumRegistryService }] });
|
|
290
|
+
|
|
291
|
+
class NotificationService {
|
|
292
|
+
messageService;
|
|
293
|
+
messageSubject = new BehaviorSubject(null);
|
|
294
|
+
message$ = this.messageSubject.asObservable();
|
|
295
|
+
constructor(messageService) {
|
|
296
|
+
this.messageService = messageService;
|
|
297
|
+
}
|
|
298
|
+
showSuccess(message) {
|
|
299
|
+
const msgObj = {
|
|
300
|
+
severity: 'success',
|
|
301
|
+
summary: 'Success',
|
|
302
|
+
detail: message,
|
|
303
|
+
life: 3000
|
|
304
|
+
};
|
|
305
|
+
this.messageSubject.next(msgObj);
|
|
306
|
+
}
|
|
307
|
+
showError(message) {
|
|
308
|
+
const msgObj = {
|
|
309
|
+
severity: 'error',
|
|
310
|
+
summary: 'Error',
|
|
311
|
+
detail: message,
|
|
312
|
+
life: 3000
|
|
313
|
+
};
|
|
314
|
+
this.messageSubject.next(msgObj);
|
|
315
|
+
}
|
|
316
|
+
clearMessage() {
|
|
317
|
+
this.messageSubject.next(null);
|
|
318
|
+
}
|
|
319
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: NotificationService, deps: [{ token: i1.MessageService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
320
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: NotificationService });
|
|
321
|
+
}
|
|
322
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: NotificationService, decorators: [{
|
|
323
|
+
type: Injectable
|
|
324
|
+
}], ctorParameters: () => [{ type: i1.MessageService }] });
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* A general-purpose HTTP utility service that provides enhanced HTTP operations
|
|
328
|
+
* beyond the standard Angular HttpClient. This service includes features such as:
|
|
329
|
+
* - Loading state management
|
|
330
|
+
* - Standardized error handling
|
|
331
|
+
* - Request timeouts and retries
|
|
332
|
+
* - File upload capabilities
|
|
333
|
+
* - Batch request handling
|
|
334
|
+
*/
|
|
335
|
+
class HttpUtilityService {
|
|
336
|
+
http;
|
|
337
|
+
messageService;
|
|
338
|
+
loading = new BehaviorSubject(false);
|
|
339
|
+
loading$ = this.loading.asObservable();
|
|
340
|
+
constructor(http, messageService) {
|
|
341
|
+
this.http = http;
|
|
342
|
+
this.messageService = messageService;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Perform a GET request with enhanced options
|
|
346
|
+
* @param url The endpoint URL
|
|
347
|
+
* @param options Additional request options
|
|
348
|
+
* @returns An observable of the response
|
|
349
|
+
*/
|
|
350
|
+
get(url, options) {
|
|
351
|
+
this.loading.next(true);
|
|
352
|
+
let request$ = this.http.get(url, {
|
|
353
|
+
params: options?.params,
|
|
354
|
+
headers: options?.headers,
|
|
355
|
+
responseType: options?.responseType,
|
|
356
|
+
withCredentials: options?.withCredentials
|
|
357
|
+
});
|
|
358
|
+
if (options?.retries) {
|
|
359
|
+
request$ = request$.pipe(retry(options.retries));
|
|
360
|
+
}
|
|
361
|
+
if (options?.timeoutMs) {
|
|
362
|
+
request$ = request$.pipe(timeout(options.timeoutMs));
|
|
363
|
+
}
|
|
364
|
+
return request$.pipe(catchError(this.handleError.bind(this)), finalize(() => this.loading.next(false)));
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Perform a POST request with enhanced options
|
|
368
|
+
* @param url The endpoint URL
|
|
369
|
+
* @param body The request payload
|
|
370
|
+
* @param options Additional request options
|
|
371
|
+
* @returns An observable of the response
|
|
372
|
+
*/
|
|
373
|
+
post(url, body, options) {
|
|
374
|
+
this.loading.next(true);
|
|
375
|
+
let request$ = this.http.post(url, body, {
|
|
376
|
+
params: options?.params,
|
|
377
|
+
headers: options?.headers,
|
|
378
|
+
responseType: options?.responseType,
|
|
379
|
+
withCredentials: options?.withCredentials
|
|
380
|
+
});
|
|
381
|
+
if (options?.retries) {
|
|
382
|
+
request$ = request$.pipe(retry(options.retries));
|
|
383
|
+
}
|
|
384
|
+
if (options?.timeoutMs) {
|
|
385
|
+
request$ = request$.pipe(timeout(options.timeoutMs));
|
|
386
|
+
}
|
|
387
|
+
return request$.pipe(catchError(this.handleError.bind(this)), finalize(() => this.loading.next(false)));
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Perform a PUT request with enhanced options
|
|
391
|
+
* @param url The endpoint URL
|
|
392
|
+
* @param body The request payload
|
|
393
|
+
* @param options Additional request options
|
|
394
|
+
* @returns An observable of the response
|
|
395
|
+
*/
|
|
396
|
+
put(url, body, options) {
|
|
397
|
+
this.loading.next(true);
|
|
398
|
+
let request$ = this.http.put(url, body, {
|
|
399
|
+
params: options?.params,
|
|
400
|
+
headers: options?.headers,
|
|
401
|
+
responseType: options?.responseType,
|
|
402
|
+
withCredentials: options?.withCredentials
|
|
403
|
+
});
|
|
404
|
+
if (options?.retries) {
|
|
405
|
+
request$ = request$.pipe(retry(options.retries));
|
|
406
|
+
}
|
|
407
|
+
if (options?.timeoutMs) {
|
|
408
|
+
request$ = request$.pipe(timeout(options.timeoutMs));
|
|
409
|
+
}
|
|
410
|
+
return request$.pipe(catchError(this.handleError.bind(this)), finalize(() => this.loading.next(false)));
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Perform a DELETE request with enhanced options
|
|
414
|
+
* @param url The endpoint URL
|
|
415
|
+
* @param options Additional request options
|
|
416
|
+
* @returns An observable of the response
|
|
417
|
+
*/
|
|
418
|
+
delete(url, options) {
|
|
419
|
+
this.loading.next(true);
|
|
420
|
+
let request$ = this.http.delete(url, {
|
|
421
|
+
params: options?.params,
|
|
422
|
+
headers: options?.headers,
|
|
423
|
+
responseType: options?.responseType,
|
|
424
|
+
withCredentials: options?.withCredentials
|
|
425
|
+
});
|
|
426
|
+
if (options?.retries) {
|
|
427
|
+
request$ = request$.pipe(retry(options.retries));
|
|
428
|
+
}
|
|
429
|
+
if (options?.timeoutMs) {
|
|
430
|
+
request$ = request$.pipe(timeout(options.timeoutMs));
|
|
431
|
+
}
|
|
432
|
+
return request$.pipe(catchError(this.handleError.bind(this)), finalize(() => this.loading.next(false)));
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Upload a file with optional additional data
|
|
436
|
+
* @param url The upload endpoint URL
|
|
437
|
+
* @param file The file to upload
|
|
438
|
+
* @param additionalData Additional form data to include
|
|
439
|
+
* @param options Additional request options
|
|
440
|
+
* @returns An observable of the response
|
|
441
|
+
*/
|
|
442
|
+
uploadFile(url, file, additionalData, options) {
|
|
443
|
+
this.loading.next(true);
|
|
444
|
+
const formData = new FormData();
|
|
445
|
+
formData.append('file', file, file.name);
|
|
446
|
+
if (additionalData) {
|
|
447
|
+
Object.keys(additionalData).forEach(key => {
|
|
448
|
+
formData.append(key, additionalData[key]);
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
let httpOptions = {
|
|
452
|
+
headers: options?.headers,
|
|
453
|
+
withCredentials: options?.withCredentials
|
|
454
|
+
};
|
|
455
|
+
// If progress tracking is requested
|
|
456
|
+
if (options?.onProgress) {
|
|
457
|
+
httpOptions.reportProgress = true;
|
|
458
|
+
httpOptions.observe = 'events';
|
|
459
|
+
}
|
|
460
|
+
// Create the request
|
|
461
|
+
let request$ = this.http.post(url, formData, httpOptions);
|
|
462
|
+
// Add retries if specified
|
|
463
|
+
if (options?.retries) {
|
|
464
|
+
request$ = request$.pipe(retry(options.retries));
|
|
465
|
+
}
|
|
466
|
+
// Add timeout if specified
|
|
467
|
+
if (options?.timeoutMs) {
|
|
468
|
+
request$ = request$.pipe(timeout(options.timeoutMs));
|
|
469
|
+
}
|
|
470
|
+
// Track progress if a callback is provided
|
|
471
|
+
if (options?.onProgress) {
|
|
472
|
+
return new Observable(observer => {
|
|
473
|
+
request$.subscribe({
|
|
474
|
+
next: (event) => {
|
|
475
|
+
if (event.type === 1 && event.total) { // HttpEventType.UploadProgress
|
|
476
|
+
const percentDone = Math.round(100 * event.loaded / event.total);
|
|
477
|
+
options.onProgress(percentDone);
|
|
478
|
+
}
|
|
479
|
+
else if (event.type === 4) { // HttpEventType.Response
|
|
480
|
+
observer.next(event.body);
|
|
481
|
+
observer.complete();
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
error: (error) => {
|
|
485
|
+
observer.error(this.handleError(error));
|
|
486
|
+
this.loading.next(false);
|
|
487
|
+
},
|
|
488
|
+
complete: () => {
|
|
489
|
+
this.loading.next(false);
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
// Standard request without progress tracking
|
|
495
|
+
return request$.pipe(catchError(this.handleError.bind(this)), finalize(() => this.loading.next(false)));
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Execute multiple HTTP requests in parallel
|
|
499
|
+
* @param requests Array of HTTP request observables
|
|
500
|
+
* @returns An observable that emits when all requests complete
|
|
501
|
+
*/
|
|
502
|
+
batchRequests(requests) {
|
|
503
|
+
this.loading.next(true);
|
|
504
|
+
return forkJoin(requests).pipe(catchError(this.handleError.bind(this)), finalize(() => this.loading.next(false)));
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Download a file from the server
|
|
508
|
+
* @param url The file URL
|
|
509
|
+
* @param filename Optional filename to save as
|
|
510
|
+
* @param options Additional request options
|
|
511
|
+
*/
|
|
512
|
+
downloadFile(url, filename, options) {
|
|
513
|
+
this.loading.next(true);
|
|
514
|
+
let request$ = this.http.get(url, {
|
|
515
|
+
params: options?.params,
|
|
516
|
+
headers: options?.headers,
|
|
517
|
+
responseType: 'blob',
|
|
518
|
+
withCredentials: options?.withCredentials
|
|
519
|
+
});
|
|
520
|
+
if (options?.timeoutMs) {
|
|
521
|
+
request$ = request$.pipe(timeout(options.timeoutMs));
|
|
522
|
+
}
|
|
523
|
+
return request$.pipe(catchError(this.handleError.bind(this)), finalize(() => this.loading.next(false)));
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Create parameter string from an object
|
|
527
|
+
* @param params Object containing parameters
|
|
528
|
+
* @returns HttpParams object
|
|
529
|
+
*/
|
|
530
|
+
createParams(params) {
|
|
531
|
+
let httpParams = new HttpParams();
|
|
532
|
+
Object.keys(params).forEach(key => {
|
|
533
|
+
const value = params[key];
|
|
534
|
+
if (value !== undefined && value !== null) {
|
|
535
|
+
httpParams = httpParams.append(key, value.toString());
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
return httpParams;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Standardized error handler for HTTP requests
|
|
542
|
+
* @param error The HTTP error
|
|
543
|
+
* @returns An observable that errors with the processed error object
|
|
544
|
+
*/
|
|
545
|
+
handleError(error) {
|
|
546
|
+
console.error('An HTTP error occurred:', error);
|
|
547
|
+
let errorMessage = 'An error occurred';
|
|
548
|
+
let errorCode;
|
|
549
|
+
if (error.error instanceof ErrorEvent) {
|
|
550
|
+
// Client-side error
|
|
551
|
+
errorMessage = error.error.message;
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
// Server-side error
|
|
555
|
+
errorCode = error.status.toString();
|
|
556
|
+
switch (error.status) {
|
|
557
|
+
case 400:
|
|
558
|
+
errorMessage = error.error?.message || 'Bad request';
|
|
559
|
+
break;
|
|
560
|
+
case 401:
|
|
561
|
+
errorMessage = 'Unauthorized';
|
|
562
|
+
break;
|
|
563
|
+
case 403:
|
|
564
|
+
errorMessage = 'Access denied';
|
|
565
|
+
break;
|
|
566
|
+
case 404:
|
|
567
|
+
errorMessage = 'Resource not found';
|
|
568
|
+
break;
|
|
569
|
+
case 408:
|
|
570
|
+
errorMessage = 'Request timeout';
|
|
571
|
+
break;
|
|
572
|
+
case 500:
|
|
573
|
+
errorMessage = 'Server error';
|
|
574
|
+
break;
|
|
575
|
+
case 503:
|
|
576
|
+
errorMessage = 'Service unavailable';
|
|
577
|
+
break;
|
|
578
|
+
default:
|
|
579
|
+
errorMessage = error.error?.message || `Error ${error.status}`;
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
this.showError(errorMessage);
|
|
584
|
+
return throwError(() => ({
|
|
585
|
+
error,
|
|
586
|
+
message: errorMessage,
|
|
587
|
+
code: errorCode
|
|
588
|
+
}));
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Display an error message using the message service
|
|
592
|
+
* @param message Error message to display
|
|
593
|
+
*/
|
|
594
|
+
showError(message) {
|
|
595
|
+
if (this.messageService) {
|
|
596
|
+
this.messageService.add({
|
|
597
|
+
severity: 'error',
|
|
598
|
+
summary: 'Error',
|
|
599
|
+
detail: message,
|
|
600
|
+
life: 3000
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: HttpUtilityService, deps: [{ token: i1$1.HttpClient }, { token: i1.MessageService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
605
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: HttpUtilityService, providedIn: 'root' });
|
|
606
|
+
}
|
|
607
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: HttpUtilityService, decorators: [{
|
|
608
|
+
type: Injectable,
|
|
609
|
+
args: [{
|
|
610
|
+
providedIn: 'root'
|
|
611
|
+
}]
|
|
612
|
+
}], ctorParameters: () => [{ type: i1$1.HttpClient }, { type: i1.MessageService }] });
|
|
613
|
+
|
|
614
|
+
class ReferenceDataProviderService {
|
|
615
|
+
endpoints = {};
|
|
616
|
+
// Method to set/update endpoints
|
|
617
|
+
setEndpoints(endpoints) {
|
|
618
|
+
this.endpoints = { ...this.endpoints, ...endpoints };
|
|
619
|
+
}
|
|
620
|
+
getReferenceEndpoint(entityName) {
|
|
621
|
+
console.log('getReferenceEndpoint', entityName, this.endpoints);
|
|
622
|
+
let endpoint = this.endpoints[entityName];
|
|
623
|
+
if (endpoint != null && endpoint.length > 0) {
|
|
624
|
+
if (endpoint.startsWith('http://') || endpoint.startsWith('https://')) {
|
|
625
|
+
return endpoint; // Absolute URL
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
// Relative URL, assume it's within the application's base context
|
|
629
|
+
return endpoint;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ReferenceDataProviderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
635
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ReferenceDataProviderService, providedIn: 'root' });
|
|
636
|
+
}
|
|
637
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ReferenceDataProviderService, decorators: [{
|
|
638
|
+
type: Injectable,
|
|
639
|
+
args: [{
|
|
640
|
+
providedIn: 'root'
|
|
641
|
+
}]
|
|
642
|
+
}] });
|
|
643
|
+
|
|
644
|
+
class EntityRegistryService {
|
|
645
|
+
httpUtility;
|
|
646
|
+
dataProvider;
|
|
647
|
+
constructor(httpUtility, dataProvider) {
|
|
648
|
+
this.httpUtility = httpUtility;
|
|
649
|
+
this.dataProvider = dataProvider;
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Gets reference options for an entity, suitable for select/dropdown components
|
|
653
|
+
* @param entityName The name of the entity (e.g., 'customers', 'departments')
|
|
654
|
+
* @returns Observable of options array with label/value pairs
|
|
655
|
+
*/
|
|
656
|
+
getEntityOptions(entityName) {
|
|
657
|
+
console.log('getEntityOptions', entityName);
|
|
658
|
+
const url = this.dataProvider.getReferenceEndpoint(entityName);
|
|
659
|
+
if (!url) {
|
|
660
|
+
console.warn(`No endpoint configured for entity: ${entityName}`);
|
|
661
|
+
return new Observable(subscriber => {
|
|
662
|
+
subscriber.next([]);
|
|
663
|
+
subscriber.complete();
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
return this.httpUtility.get(url, {
|
|
667
|
+
retries: 1,
|
|
668
|
+
timeoutMs: 10000,
|
|
669
|
+
}).pipe(map((items) => items?.map((item) => ({
|
|
670
|
+
label: item.displayLabel,
|
|
671
|
+
value: item.key,
|
|
672
|
+
data: item.data,
|
|
673
|
+
})) || []));
|
|
674
|
+
}
|
|
675
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EntityRegistryService, deps: [{ token: HttpUtilityService }, { token: ReferenceDataProviderService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
676
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EntityRegistryService, providedIn: 'root' });
|
|
677
|
+
}
|
|
678
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EntityRegistryService, decorators: [{
|
|
679
|
+
type: Injectable,
|
|
680
|
+
args: [{
|
|
681
|
+
providedIn: 'root'
|
|
682
|
+
}]
|
|
683
|
+
}], ctorParameters: () => [{ type: HttpUtilityService }, { type: ReferenceDataProviderService }] });
|
|
684
|
+
|
|
685
|
+
class BaseCrudService {
|
|
686
|
+
httpUtility;
|
|
687
|
+
utilsService;
|
|
688
|
+
resourcePath;
|
|
689
|
+
backendBaseUrl;
|
|
690
|
+
messageService;
|
|
691
|
+
// Use the loading state from HttpUtilityService
|
|
692
|
+
get loading$() {
|
|
693
|
+
return this.httpUtility.loading$;
|
|
694
|
+
}
|
|
695
|
+
apiUrl;
|
|
696
|
+
/**
|
|
697
|
+
* Creates an instance of BaseCrudService.
|
|
698
|
+
* @param httpUtility - Service for making HTTP requests
|
|
699
|
+
* @param utilsService - Utility service for common operations
|
|
700
|
+
* @param resourcePath - API resource path (e.g., 'customers', 'products')
|
|
701
|
+
* @param backendBaseUrl - Base URL for the backend API
|
|
702
|
+
* @param messageService - Optional service for showing messages
|
|
703
|
+
*/
|
|
704
|
+
constructor(httpUtility, utilsService, resourcePath, backendBaseUrl, messageService) {
|
|
705
|
+
this.httpUtility = httpUtility;
|
|
706
|
+
this.utilsService = utilsService;
|
|
707
|
+
this.resourcePath = resourcePath;
|
|
708
|
+
this.backendBaseUrl = backendBaseUrl;
|
|
709
|
+
this.messageService = messageService;
|
|
710
|
+
this.apiUrl = this.joinUrls(backendBaseUrl, resourcePath);
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Joins URL parts ensuring no double slashes
|
|
714
|
+
*/
|
|
715
|
+
joinUrls(...parts) {
|
|
716
|
+
return parts.map(part => part.replace(/^\/+|\/+$/g, '')).join('/');
|
|
717
|
+
}
|
|
718
|
+
getAll(pageRequest) {
|
|
719
|
+
if (pageRequest) {
|
|
720
|
+
return this.httpUtility.post(`${this.apiUrl}/page`, pageRequest);
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
return this.httpUtility.get(this.apiUrl);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
getById(uuid) {
|
|
727
|
+
if (!this.utilsService.isValidUUID(uuid)) {
|
|
728
|
+
this.utilsService.showError('Invalid UUID format', this.messageService);
|
|
729
|
+
return throwError(() => new Error('Invalid UUID format'));
|
|
730
|
+
}
|
|
731
|
+
return this.httpUtility.get(`${this.apiUrl}/${uuid}`);
|
|
732
|
+
}
|
|
733
|
+
create(entity) {
|
|
734
|
+
return this.httpUtility.post(this.apiUrl, entity);
|
|
735
|
+
}
|
|
736
|
+
update(uuid, entity) {
|
|
737
|
+
if (!this.utilsService.isValidUUID(uuid)) {
|
|
738
|
+
this.utilsService.showError('Invalid UUID format', this.messageService);
|
|
739
|
+
return throwError(() => new Error('Invalid UUID format'));
|
|
740
|
+
}
|
|
741
|
+
return this.httpUtility.put(`${this.apiUrl}/${uuid}`, entity);
|
|
742
|
+
}
|
|
743
|
+
delete(uuid) {
|
|
744
|
+
if (!this.utilsService.isValidUUID(uuid)) {
|
|
745
|
+
this.utilsService.showError('Invalid UUID format', this.messageService);
|
|
746
|
+
return throwError(() => new Error('Invalid UUID format'));
|
|
747
|
+
}
|
|
748
|
+
return this.httpUtility.delete(`${this.apiUrl}/${uuid}`);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
var ComponentContext;
|
|
753
|
+
(function (ComponentContext) {
|
|
754
|
+
ComponentContext["STANDALONE"] = "STANDALONE";
|
|
755
|
+
ComponentContext["TABBED"] = "TABBED";
|
|
756
|
+
ComponentContext["MODAL"] = "MODAL";
|
|
757
|
+
ComponentContext["EMBEDDED"] = "EMBEDDED";
|
|
758
|
+
})(ComponentContext || (ComponentContext = {}));
|
|
759
|
+
|
|
760
|
+
var TabbedFormType;
|
|
761
|
+
(function (TabbedFormType) {
|
|
762
|
+
TabbedFormType["STANDARD"] = "STANDARD";
|
|
763
|
+
TabbedFormType["LIST"] = "LIST";
|
|
764
|
+
})(TabbedFormType || (TabbedFormType = {}));
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Base component for all page components in the application.
|
|
768
|
+
* Provides common functionality like loading state and message display.
|
|
769
|
+
*/
|
|
770
|
+
class BasePageComponent {
|
|
771
|
+
utilsService;
|
|
772
|
+
notificationService;
|
|
773
|
+
loading = false;
|
|
774
|
+
constructor(utilsService, notificationService) {
|
|
775
|
+
this.utilsService = utilsService;
|
|
776
|
+
this.notificationService = notificationService;
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Display an error message to the user
|
|
780
|
+
* @param detail The error message to display
|
|
781
|
+
*/
|
|
782
|
+
showError(detail) {
|
|
783
|
+
this.notificationService.showError(detail);
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Display a success message to the user
|
|
787
|
+
* @param detail The success message to display
|
|
788
|
+
*/
|
|
789
|
+
showSuccess(detail) {
|
|
790
|
+
this.notificationService.showSuccess(detail);
|
|
791
|
+
}
|
|
792
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: BasePageComponent, deps: [{ token: UtilsService }, { token: NotificationService }], target: i0.ɵɵFactoryTarget.Directive });
|
|
793
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.4", type: BasePageComponent, isStandalone: true, ngImport: i0 });
|
|
794
|
+
}
|
|
795
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: BasePageComponent, decorators: [{
|
|
796
|
+
type: Directive
|
|
797
|
+
}], ctorParameters: () => [{ type: UtilsService }, { type: NotificationService }] });
|
|
798
|
+
|
|
799
|
+
const AppMessages = {
|
|
800
|
+
COMMON: {
|
|
801
|
+
ERROR: {
|
|
802
|
+
GENERAL: 'An error occurred',
|
|
803
|
+
INVALID_INPUT: 'Invalid input provided',
|
|
804
|
+
REQUIRED_FIELD: 'This field is required'
|
|
805
|
+
},
|
|
806
|
+
SUCCESS: {
|
|
807
|
+
SAVE: 'saved successfully',
|
|
808
|
+
UPDATE: 'updated successfully',
|
|
809
|
+
DELETE: 'deleted successfully'
|
|
810
|
+
}
|
|
811
|
+
},
|
|
812
|
+
FORM: {
|
|
813
|
+
ERROR: {
|
|
814
|
+
INIT: 'Failed to initialize form',
|
|
815
|
+
SAVE: 'Failed to save',
|
|
816
|
+
LOAD: 'Failed to load',
|
|
817
|
+
VALIDATION: 'Please fill in all required fields correctly'
|
|
818
|
+
},
|
|
819
|
+
SUCCESS: {
|
|
820
|
+
SAVE: 'saved successfully'
|
|
821
|
+
}
|
|
822
|
+
},
|
|
823
|
+
LIST: {
|
|
824
|
+
ERROR: {
|
|
825
|
+
LOAD: 'Failed to load',
|
|
826
|
+
DELETE: 'Failed to delete',
|
|
827
|
+
NAVIGATION: 'Navigation failed',
|
|
828
|
+
INVALID_ID: 'Cannot perform action without ID'
|
|
829
|
+
},
|
|
830
|
+
SUCCESS: {
|
|
831
|
+
DELETE: 'deleted successfully'
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
class AppConstants {
|
|
837
|
+
static FORM = {
|
|
838
|
+
MODE: 'mode',
|
|
839
|
+
UUID: 'uuid',
|
|
840
|
+
NEW: 'new'
|
|
841
|
+
};
|
|
842
|
+
static PAGE_MODE = {
|
|
843
|
+
CREATE: 'create',
|
|
844
|
+
EDIT: 'edit',
|
|
845
|
+
VIEW: 'view'
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
class BaseFormComponent extends BasePageComponent {
|
|
850
|
+
route;
|
|
851
|
+
router;
|
|
852
|
+
notificationService;
|
|
853
|
+
utilsService;
|
|
854
|
+
formlyConfigService;
|
|
855
|
+
form = new FormGroup({});
|
|
856
|
+
fields = [];
|
|
857
|
+
isNew = true;
|
|
858
|
+
mode = AppConstants.PAGE_MODE.CREATE;
|
|
859
|
+
currentFormData = {};
|
|
860
|
+
originalFormData = null;
|
|
861
|
+
hasChanges = false;
|
|
862
|
+
context = ComponentContext.STANDALONE;
|
|
863
|
+
constructor(route, router, notificationService, utilsService, formlyConfigService) {
|
|
864
|
+
super(utilsService, notificationService);
|
|
865
|
+
this.route = route;
|
|
866
|
+
this.router = router;
|
|
867
|
+
this.notificationService = notificationService;
|
|
868
|
+
this.utilsService = utilsService;
|
|
869
|
+
this.formlyConfigService = formlyConfigService;
|
|
870
|
+
}
|
|
871
|
+
ngOnInit() {
|
|
872
|
+
try {
|
|
873
|
+
this.fields = this.getFormlyFieldConfig();
|
|
874
|
+
this.form.valueChanges.subscribe(() => {
|
|
875
|
+
console.log('[BaseFormComponent] Form value changed:', this.form.value);
|
|
876
|
+
this.currentFormData = { ...this.form.value };
|
|
877
|
+
this.hasChanges = this.hasFormChanges(this.currentFormData);
|
|
878
|
+
});
|
|
879
|
+
// Use queryParams instead of params
|
|
880
|
+
this.route.queryParams.pipe(take(1)).subscribe({
|
|
881
|
+
next: params => this.initializeForm(params),
|
|
882
|
+
error: error => {
|
|
883
|
+
console.error('Error initializing form:', error);
|
|
884
|
+
this.showError(AppMessages.FORM.ERROR.INIT);
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
catch (error) {
|
|
889
|
+
console.error('Error in ngOnInit:', error);
|
|
890
|
+
this.showError(AppMessages.FORM.ERROR.INIT);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
initializeFormData() {
|
|
894
|
+
console.log('[BaseFormComponent] Initializing form data');
|
|
895
|
+
console.log('[BaseFormComponent] Current mode:', this.mode);
|
|
896
|
+
if (this.mode === AppConstants.PAGE_MODE.CREATE) {
|
|
897
|
+
console.log('[BaseFormComponent] Create mode, using defaults');
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
const state = {
|
|
901
|
+
...this.router.getCurrentNavigation()?.extras?.state,
|
|
902
|
+
...window.history.state,
|
|
903
|
+
...this.route.snapshot.data
|
|
904
|
+
};
|
|
905
|
+
console.log('[BaseFormComponent] Combined state:', state);
|
|
906
|
+
console.log('[BaseFormComponent] Final state being used:', state);
|
|
907
|
+
if (state && 'formData' in state) {
|
|
908
|
+
console.log('[BaseFormComponent] Found form data in state:', state['formData']);
|
|
909
|
+
this.patchFormData(state['formData']);
|
|
910
|
+
}
|
|
911
|
+
else {
|
|
912
|
+
console.log('[BaseFormComponent] No form data, loading from UUID');
|
|
913
|
+
this.loadFromUuid();
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
loadFromUuid() {
|
|
917
|
+
this.route.queryParams.pipe(take(1)).subscribe(params => {
|
|
918
|
+
const uuid = params[AppConstants.FORM.UUID];
|
|
919
|
+
if (!uuid) {
|
|
920
|
+
throw new Error(`UUID is required for ${this.mode} mode when no formData is provided`);
|
|
921
|
+
}
|
|
922
|
+
this.loadEntity(uuid);
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
initializeForm(params) {
|
|
926
|
+
console.log('[BaseFormComponent] Initializing form with params:', params);
|
|
927
|
+
const routeMode = params[AppConstants.FORM.MODE];
|
|
928
|
+
this.initializeFormMode(routeMode);
|
|
929
|
+
this.setupFormState();
|
|
930
|
+
// Check for context in router state
|
|
931
|
+
const state = window.history.state;
|
|
932
|
+
if (state?.context) {
|
|
933
|
+
this.context = state.context;
|
|
934
|
+
console.log('[BaseFormComponent] Context set from router state:', this.context);
|
|
935
|
+
}
|
|
936
|
+
this.initializeFormData();
|
|
937
|
+
}
|
|
938
|
+
patchFormData(data) {
|
|
939
|
+
console.log('[BaseFormComponent] Patching form data:', data);
|
|
940
|
+
if (data) {
|
|
941
|
+
if (!this.fields || this.fields.length === 0) {
|
|
942
|
+
console.warn('[BaseFormComponent] Fields not initialized yet, reinitializing');
|
|
943
|
+
this.fields = this.getFormlyFieldConfig();
|
|
944
|
+
}
|
|
945
|
+
this.currentFormData = { ...data };
|
|
946
|
+
this.originalFormData = data;
|
|
947
|
+
this.isNew = false;
|
|
948
|
+
console.log('[BaseFormComponent] Form data updated:', this.currentFormData);
|
|
949
|
+
console.log('[BaseFormComponent] Fields available:', this.fields);
|
|
950
|
+
setTimeout(() => {
|
|
951
|
+
if (!this.form.valid) {
|
|
952
|
+
console.warn('[BaseFormComponent] Form validation issues after data patch');
|
|
953
|
+
}
|
|
954
|
+
console.log('[BaseFormComponent] Form state after sync:', {
|
|
955
|
+
valid: this.form.valid,
|
|
956
|
+
values: this.form.value,
|
|
957
|
+
currentData: this.currentFormData
|
|
958
|
+
});
|
|
959
|
+
}, 100);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
initializeFormMode(routeMode) {
|
|
963
|
+
if (!routeMode) {
|
|
964
|
+
this.mode = AppConstants.PAGE_MODE.CREATE;
|
|
965
|
+
}
|
|
966
|
+
else if (this.isValidPageMode(routeMode)) {
|
|
967
|
+
this.mode = routeMode;
|
|
968
|
+
}
|
|
969
|
+
else {
|
|
970
|
+
throw new Error(`Invalid page mode: ${routeMode}`);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
setupFormState() {
|
|
974
|
+
console.log('[BaseFormComponent] Setting up form state, mode:', this.mode);
|
|
975
|
+
this.fields = this.getFormlyFieldConfig();
|
|
976
|
+
if (this.mode === AppConstants.PAGE_MODE.CREATE) {
|
|
977
|
+
console.log('[BaseFormComponent] Create mode setup');
|
|
978
|
+
this.isNew = true;
|
|
979
|
+
this.loading = false;
|
|
980
|
+
this.originalFormData = null;
|
|
981
|
+
}
|
|
982
|
+
if (this.mode === AppConstants.PAGE_MODE.VIEW) {
|
|
983
|
+
console.log('[BaseFormComponent] View mode setup');
|
|
984
|
+
this.form.disable();
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
getFormlyFieldConfig() {
|
|
988
|
+
return this.formlyConfigService.getFormConfig(this.getJsonFields(), this.mode, this.getDropdownOptions());
|
|
989
|
+
}
|
|
990
|
+
onSubmitRoute() {
|
|
991
|
+
return [`/${this.getEntityName().toLowerCase()}s`];
|
|
992
|
+
}
|
|
993
|
+
getEntityOperations() {
|
|
994
|
+
const entityService = this.getEntityService();
|
|
995
|
+
return {
|
|
996
|
+
create: (data) => entityService.create(data),
|
|
997
|
+
update: (uuid, data) => entityService.update(uuid, { ...data, uuid }),
|
|
998
|
+
getById: (uuid) => entityService.getById(uuid)
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
onSubmit() {
|
|
1002
|
+
console.log('[BaseFormComponent] Form submission:', {
|
|
1003
|
+
formValid: this.form.valid,
|
|
1004
|
+
formData: this.currentFormData,
|
|
1005
|
+
formState: this.form.value
|
|
1006
|
+
});
|
|
1007
|
+
if (!this.currentFormData || Object.keys(this.currentFormData).length === 0) {
|
|
1008
|
+
this.notificationService.showError('No data to save');
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
if (this.form.valid) {
|
|
1012
|
+
if (this.isNew) {
|
|
1013
|
+
console.log('[BaseFormComponent] Saving new entity');
|
|
1014
|
+
this.saveEntity();
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
const hasChanges = this.hasFormChanges(this.currentFormData);
|
|
1018
|
+
console.log('[BaseFormComponent] Change detection:', {
|
|
1019
|
+
hasChanges,
|
|
1020
|
+
currentData: this.currentFormData,
|
|
1021
|
+
originalData: this.originalFormData
|
|
1022
|
+
});
|
|
1023
|
+
if (!hasChanges) {
|
|
1024
|
+
this.notificationService.showSuccess('No changes to save');
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
console.log('[BaseFormComponent] Saving changes');
|
|
1028
|
+
this.saveEntity();
|
|
1029
|
+
}
|
|
1030
|
+
else {
|
|
1031
|
+
console.warn('[BaseFormComponent] Form validation failed:', this.form.errors);
|
|
1032
|
+
this.notificationService.showError('Please check form errors');
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
hasFormChanges(currentData) {
|
|
1036
|
+
if (!this.originalFormData || this.isNew) {
|
|
1037
|
+
return true;
|
|
1038
|
+
}
|
|
1039
|
+
console.log('[BaseFormComponent] Checking for changes:', {
|
|
1040
|
+
currentData,
|
|
1041
|
+
originalData: this.originalFormData
|
|
1042
|
+
});
|
|
1043
|
+
const hasChanges = Object.keys(currentData).some(key => {
|
|
1044
|
+
const currentValue = currentData[key];
|
|
1045
|
+
const originalValue = this.originalFormData[key];
|
|
1046
|
+
const changed = currentValue !== originalValue;
|
|
1047
|
+
if (changed) {
|
|
1048
|
+
console.log(`[BaseFormComponent] Field '${key}' changed:`, {
|
|
1049
|
+
from: originalValue,
|
|
1050
|
+
to: currentValue
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
return changed;
|
|
1054
|
+
});
|
|
1055
|
+
console.log('[BaseFormComponent] Changes detected:', hasChanges);
|
|
1056
|
+
return hasChanges;
|
|
1057
|
+
}
|
|
1058
|
+
saveEntity() {
|
|
1059
|
+
this.loading = true;
|
|
1060
|
+
const formData = this.currentFormData;
|
|
1061
|
+
const entityName = this.getEntityName();
|
|
1062
|
+
const entityOperations = this.getEntityOperations();
|
|
1063
|
+
const request$ = this.isNew
|
|
1064
|
+
? entityOperations.create(formData)
|
|
1065
|
+
: entityOperations.update(this.originalFormData.uuid, formData);
|
|
1066
|
+
request$.subscribe({
|
|
1067
|
+
next: () => {
|
|
1068
|
+
this.loading = false;
|
|
1069
|
+
this.notificationService.showSuccess(`${entityName} ${AppMessages.FORM.SUCCESS.SAVE}`);
|
|
1070
|
+
this.router.navigate(this.onSubmitRoute());
|
|
1071
|
+
},
|
|
1072
|
+
error: (error) => {
|
|
1073
|
+
const errorMessage = error.error?.message || `${AppMessages.FORM.ERROR.SAVE} ${entityName}`;
|
|
1074
|
+
this.showError(errorMessage);
|
|
1075
|
+
this.loading = false;
|
|
1076
|
+
}
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
loadEntity(uuid) {
|
|
1080
|
+
this.loading = true;
|
|
1081
|
+
this.isNew = false;
|
|
1082
|
+
const entityName = this.getEntityName();
|
|
1083
|
+
this.getEntityOperations().getById(uuid).subscribe({
|
|
1084
|
+
next: (data) => {
|
|
1085
|
+
this.originalFormData = data;
|
|
1086
|
+
this.currentFormData = { ...data };
|
|
1087
|
+
this.loading = false;
|
|
1088
|
+
console.log('[BaseFormComponent] Original data loaded:', this.originalFormData);
|
|
1089
|
+
console.log('[BaseFormComponent] Form data updated:', this.currentFormData);
|
|
1090
|
+
},
|
|
1091
|
+
error: () => {
|
|
1092
|
+
this.showError(`${AppMessages.FORM.ERROR.LOAD} ${entityName}`);
|
|
1093
|
+
this.loading = false;
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
isValidPageMode(mode) {
|
|
1098
|
+
return Object.values(AppConstants.PAGE_MODE).includes(mode);
|
|
1099
|
+
}
|
|
1100
|
+
getDropdownOptions() {
|
|
1101
|
+
return {};
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Checks if the component is running in a specific context
|
|
1105
|
+
* @param contextType The context type to check against
|
|
1106
|
+
*/
|
|
1107
|
+
isInContext(contextType) {
|
|
1108
|
+
return this.context === contextType;
|
|
1109
|
+
}
|
|
1110
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: BaseFormComponent, deps: [{ token: i1$2.ActivatedRoute }, { token: i1$2.Router }, { token: NotificationService }, { token: UtilsService }, { token: FormlyConfigService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1111
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: BaseFormComponent, isStandalone: true, selector: "ng-component", inputs: { context: "context" }, usesInheritance: true, ngImport: i0, template: '', isInline: true });
|
|
1112
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: BaseFormComponent });
|
|
1113
|
+
}
|
|
1114
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: BaseFormComponent, decorators: [{
|
|
1115
|
+
type: Injectable
|
|
1116
|
+
}, {
|
|
1117
|
+
type: Component,
|
|
1118
|
+
args: [{
|
|
1119
|
+
template: ''
|
|
1120
|
+
}]
|
|
1121
|
+
}], ctorParameters: () => [{ type: i1$2.ActivatedRoute }, { type: i1$2.Router }, { type: NotificationService }, { type: UtilsService }, { type: FormlyConfigService }], propDecorators: { context: [{
|
|
1122
|
+
type: Input
|
|
1123
|
+
}] } });
|
|
1124
|
+
|
|
1125
|
+
/**
|
|
1126
|
+
* Base component for all list page components in the application.
|
|
1127
|
+
* Provides common functionality for list operations like loading data,
|
|
1128
|
+
* pagination, sorting, and CRUD operations.
|
|
1129
|
+
*/
|
|
1130
|
+
class BaseListPageComponent extends BasePageComponent {
|
|
1131
|
+
router;
|
|
1132
|
+
notificationService;
|
|
1133
|
+
utilsService;
|
|
1134
|
+
confirmationService;
|
|
1135
|
+
table;
|
|
1136
|
+
items = [];
|
|
1137
|
+
actionLoading = false;
|
|
1138
|
+
rows = 10;
|
|
1139
|
+
totalRecords = 0;
|
|
1140
|
+
filters = [];
|
|
1141
|
+
subscriptions = [];
|
|
1142
|
+
constructor(router, notificationService, utilsService, confirmationService) {
|
|
1143
|
+
super(utilsService, notificationService);
|
|
1144
|
+
this.router = router;
|
|
1145
|
+
this.notificationService = notificationService;
|
|
1146
|
+
this.utilsService = utilsService;
|
|
1147
|
+
this.confirmationService = confirmationService;
|
|
1148
|
+
}
|
|
1149
|
+
/**
|
|
1150
|
+
* Initialize the component, subscribe to loading state if available,
|
|
1151
|
+
* and load initial data.
|
|
1152
|
+
*/
|
|
1153
|
+
ngOnInit() {
|
|
1154
|
+
// Subscribe to loading state if available
|
|
1155
|
+
const loadingObservable = this.getLoadingObservable();
|
|
1156
|
+
if (loadingObservable) {
|
|
1157
|
+
this.subscriptions.push(loadingObservable.subscribe(isLoading => this.loading = isLoading));
|
|
1158
|
+
}
|
|
1159
|
+
this.loadData();
|
|
1160
|
+
}
|
|
1161
|
+
/**
|
|
1162
|
+
* Clean up any subscriptions when the component is destroyed
|
|
1163
|
+
*/
|
|
1164
|
+
ngOnDestroy() {
|
|
1165
|
+
this.subscriptions.forEach(sub => sub.unsubscribe());
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Handle search event from the UI
|
|
1169
|
+
* @param event The search event
|
|
1170
|
+
*/
|
|
1171
|
+
onSearch(event) {
|
|
1172
|
+
const element = event.target;
|
|
1173
|
+
if (this.table) {
|
|
1174
|
+
this.table.filterGlobal(element.value, 'contains');
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Load data with pagination and sorting
|
|
1179
|
+
* @param event The lazy load event from PrimeNG table
|
|
1180
|
+
*/
|
|
1181
|
+
/**
|
|
1182
|
+
* Convert PrimeNG filter format to backend format
|
|
1183
|
+
* @param filters The PrimeNG filters object
|
|
1184
|
+
* @returns Converted filters object for backend
|
|
1185
|
+
*/
|
|
1186
|
+
/**
|
|
1187
|
+
* Convert PrimeNG filter format to backend format
|
|
1188
|
+
* @param filters The PrimeNG filters object
|
|
1189
|
+
* @returns Converted filters object for backend
|
|
1190
|
+
*/
|
|
1191
|
+
/**
|
|
1192
|
+
* Convert PrimeNG filters to backend format with proper operator mapping
|
|
1193
|
+
*/
|
|
1194
|
+
convertFilters(filters) {
|
|
1195
|
+
const backendFilters = [];
|
|
1196
|
+
if (!filters)
|
|
1197
|
+
return backendFilters;
|
|
1198
|
+
console.log('Raw PrimeNG filters:', filters);
|
|
1199
|
+
Object.entries(filters).forEach(([key, filterMeta]) => {
|
|
1200
|
+
console.log(`Processing filter for ${key}:`, filterMeta);
|
|
1201
|
+
// Handle array of filter metadata (multiple filters for same column)
|
|
1202
|
+
if (Array.isArray(filterMeta)) {
|
|
1203
|
+
const validFilters = filterMeta
|
|
1204
|
+
.filter(meta => meta.value !== null && meta.value !== undefined && meta.value !== '')
|
|
1205
|
+
.map(meta => ({
|
|
1206
|
+
field: key,
|
|
1207
|
+
value: this.transformFilterValue(String(meta.value)),
|
|
1208
|
+
operator: this.transformOperator(meta.matchMode)
|
|
1209
|
+
}));
|
|
1210
|
+
backendFilters.push(...validFilters);
|
|
1211
|
+
}
|
|
1212
|
+
// Handle single filter metadata
|
|
1213
|
+
else if (filterMeta?.value !== null && filterMeta?.value !== undefined && filterMeta?.value !== '') {
|
|
1214
|
+
backendFilters.push({
|
|
1215
|
+
field: key,
|
|
1216
|
+
value: this.transformFilterValue(String(filterMeta.value)),
|
|
1217
|
+
operator: this.transformOperator(filterMeta.matchMode)
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
});
|
|
1221
|
+
console.log('Converted filters:', backendFilters);
|
|
1222
|
+
return backendFilters;
|
|
1223
|
+
}
|
|
1224
|
+
/**
|
|
1225
|
+
* Transform filter value based on backend requirements - all values must be strings
|
|
1226
|
+
*/
|
|
1227
|
+
transformFilterValue(value) {
|
|
1228
|
+
if (value === null || value === undefined) {
|
|
1229
|
+
return '';
|
|
1230
|
+
}
|
|
1231
|
+
if (typeof value === 'string') {
|
|
1232
|
+
return value.trim();
|
|
1233
|
+
}
|
|
1234
|
+
if (Array.isArray(value)) {
|
|
1235
|
+
return value.join(',');
|
|
1236
|
+
}
|
|
1237
|
+
if (value instanceof Date) {
|
|
1238
|
+
return value.toISOString();
|
|
1239
|
+
}
|
|
1240
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
1241
|
+
return value.toString();
|
|
1242
|
+
}
|
|
1243
|
+
return String(value);
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Map PrimeNG operators to backend operators
|
|
1247
|
+
*/
|
|
1248
|
+
transformOperator(operator) {
|
|
1249
|
+
const operatorMap = {
|
|
1250
|
+
'contains': 'CONTAINS',
|
|
1251
|
+
'startsWith': 'STARTS_WITH',
|
|
1252
|
+
'endsWith': 'ENDS_WITH',
|
|
1253
|
+
'equals': 'EQUALS',
|
|
1254
|
+
'notEquals': 'NOT_EQUALS',
|
|
1255
|
+
'gt': 'GREATER_THAN',
|
|
1256
|
+
'gte': 'GREATER_THAN_EQUALS',
|
|
1257
|
+
'lt': 'LESS_THAN',
|
|
1258
|
+
'lte': 'LESS_THAN_EQUALS'
|
|
1259
|
+
};
|
|
1260
|
+
return operatorMap[operator] || 'EQUALS'; // Default to EQUALS if operator not found
|
|
1261
|
+
}
|
|
1262
|
+
loadData(event) {
|
|
1263
|
+
console.log('LoadData called with event:', event);
|
|
1264
|
+
this.loading = true;
|
|
1265
|
+
// Create page request with default values
|
|
1266
|
+
const pageRequest = {
|
|
1267
|
+
page: 0,
|
|
1268
|
+
size: 10,
|
|
1269
|
+
sorts: [],
|
|
1270
|
+
filters: []
|
|
1271
|
+
};
|
|
1272
|
+
if (event) {
|
|
1273
|
+
// Handle pagination
|
|
1274
|
+
if (event.first !== undefined && event.rows) {
|
|
1275
|
+
pageRequest.page = Math.floor(event.first / event.rows);
|
|
1276
|
+
pageRequest.size = event.rows;
|
|
1277
|
+
}
|
|
1278
|
+
// Handle sorting
|
|
1279
|
+
if (event.sortField) {
|
|
1280
|
+
pageRequest.sorts = [{
|
|
1281
|
+
field: Array.isArray(event.sortField) ? event.sortField[0] : event.sortField,
|
|
1282
|
+
direction: event.sortOrder === 1 ? 'asc' : 'desc'
|
|
1283
|
+
}];
|
|
1284
|
+
}
|
|
1285
|
+
// Handle filtering
|
|
1286
|
+
if (event.filters) {
|
|
1287
|
+
const convertedFilters = this.convertFilters(event.filters);
|
|
1288
|
+
console.log('Converted filters:', convertedFilters);
|
|
1289
|
+
pageRequest.filters = convertedFilters;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
console.log('Sending page request:', pageRequest);
|
|
1293
|
+
this.getEntityService().getAll(pageRequest)
|
|
1294
|
+
.pipe(finalize$1(() => this.loading = false))
|
|
1295
|
+
.subscribe({
|
|
1296
|
+
next: (response) => {
|
|
1297
|
+
this.items = [...response.content];
|
|
1298
|
+
this.totalRecords = response.page.totalElements;
|
|
1299
|
+
},
|
|
1300
|
+
error: (error) => {
|
|
1301
|
+
console.error('Failed to load data:', error);
|
|
1302
|
+
this.showError(`${AppMessages.LIST.ERROR.LOAD} ${this.getEntityName()}`);
|
|
1303
|
+
}
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
/**
|
|
1307
|
+
* Navigate to create a new entity
|
|
1308
|
+
*/
|
|
1309
|
+
onCreate() {
|
|
1310
|
+
this.navigateTo([this.getFormRoute(), AppConstants.PAGE_MODE.CREATE]);
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* Navigate to view an entity
|
|
1314
|
+
* @param item The entity to view
|
|
1315
|
+
*/
|
|
1316
|
+
onView(item) {
|
|
1317
|
+
if (!this.validateItemUuid(item, 'view'))
|
|
1318
|
+
return;
|
|
1319
|
+
this.navigateTo([this.getFormRoute(), AppConstants.PAGE_MODE.VIEW], { uuid: item.uuid });
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* Navigate to edit an entity
|
|
1323
|
+
* @param item The entity to edit
|
|
1324
|
+
*/
|
|
1325
|
+
onEdit(item) {
|
|
1326
|
+
if (!this.validateItemUuid(item, 'edit'))
|
|
1327
|
+
return;
|
|
1328
|
+
this.navigateTo([this.getFormRoute(), AppConstants.PAGE_MODE.EDIT], { uuid: item.uuid });
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Navigate to a route with error handling
|
|
1332
|
+
* @param path The route path
|
|
1333
|
+
* @param matrixParams Optional matrix parameters
|
|
1334
|
+
*/
|
|
1335
|
+
navigateTo(path, matrixParams) {
|
|
1336
|
+
this.router.navigate(path, { queryParams: matrixParams })
|
|
1337
|
+
.catch(error => {
|
|
1338
|
+
console.error('Navigation error:', error);
|
|
1339
|
+
this.showError(AppMessages.LIST.ERROR.NAVIGATION);
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* Delete an entity with confirmation
|
|
1344
|
+
* @param item The entity to delete
|
|
1345
|
+
*/
|
|
1346
|
+
onDelete(item) {
|
|
1347
|
+
if (!this.validateItemUuid(item, 'delete'))
|
|
1348
|
+
return;
|
|
1349
|
+
this.confirmationService.confirm({
|
|
1350
|
+
message: `Are you sure you want to delete this ${this.getEntityName()}?`,
|
|
1351
|
+
header: 'Confirm Deletion',
|
|
1352
|
+
icon: 'pi pi-exclamation-triangle',
|
|
1353
|
+
accept: () => {
|
|
1354
|
+
this.actionLoading = true;
|
|
1355
|
+
this.getEntityService().delete(item.uuid)
|
|
1356
|
+
.pipe(finalize$1(() => this.actionLoading = false))
|
|
1357
|
+
.subscribe({
|
|
1358
|
+
next: () => {
|
|
1359
|
+
this.showSuccess(`${this.getEntityName()} ${AppMessages.LIST.SUCCESS.DELETE}`);
|
|
1360
|
+
this.loadData();
|
|
1361
|
+
},
|
|
1362
|
+
error: (error) => {
|
|
1363
|
+
console.error('Delete failed:', error);
|
|
1364
|
+
this.showError(`${AppMessages.LIST.ERROR.DELETE} ${this.getEntityName()}`);
|
|
1365
|
+
}
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
/**
|
|
1371
|
+
* Validate that an entity has a UUID
|
|
1372
|
+
* @param item The entity to validate
|
|
1373
|
+
* @param action The action being performed
|
|
1374
|
+
* @returns true if valid, false otherwise
|
|
1375
|
+
*/
|
|
1376
|
+
validateItemUuid(item, action) {
|
|
1377
|
+
if (!item?.uuid) {
|
|
1378
|
+
this.showError(`${AppMessages.LIST.ERROR.INVALID_ID} ${action}`);
|
|
1379
|
+
return false;
|
|
1380
|
+
}
|
|
1381
|
+
return true;
|
|
1382
|
+
}
|
|
1383
|
+
/**
|
|
1384
|
+
* Get an observable for loading state from the entity service
|
|
1385
|
+
* @returns An observable of loading state if available, null otherwise
|
|
1386
|
+
*/
|
|
1387
|
+
getLoadingObservable() {
|
|
1388
|
+
const service = this.getEntityService();
|
|
1389
|
+
return service.loading$ || null;
|
|
1390
|
+
}
|
|
1391
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: BaseListPageComponent, deps: [{ token: i1$2.Router }, { token: NotificationService }, { token: UtilsService }, { token: i1.ConfirmationService }], target: i0.ɵɵFactoryTarget.Directive });
|
|
1392
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.4", type: BaseListPageComponent, isStandalone: true, viewQueries: [{ propertyName: "table", first: true, predicate: ["dt"], descendants: true }], usesInheritance: true, ngImport: i0 });
|
|
1393
|
+
}
|
|
1394
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: BaseListPageComponent, decorators: [{
|
|
1395
|
+
type: Directive
|
|
1396
|
+
}], ctorParameters: () => [{ type: i1$2.Router }, { type: NotificationService }, { type: UtilsService }, { type: i1.ConfirmationService }], propDecorators: { table: [{
|
|
1397
|
+
type: ViewChild,
|
|
1398
|
+
args: ['dt']
|
|
1399
|
+
}] } });
|
|
1400
|
+
|
|
1401
|
+
class BaseTabbedFormComponent extends BasePageComponent {
|
|
1402
|
+
route;
|
|
1403
|
+
router;
|
|
1404
|
+
notificationService;
|
|
1405
|
+
utilsService;
|
|
1406
|
+
// Tab specific properties
|
|
1407
|
+
activeTab = '';
|
|
1408
|
+
tabs = [];
|
|
1409
|
+
formType = '';
|
|
1410
|
+
// Form properties
|
|
1411
|
+
isNew = true;
|
|
1412
|
+
mode = AppConstants.PAGE_MODE.CREATE;
|
|
1413
|
+
currentFormData;
|
|
1414
|
+
originalFormData = null;
|
|
1415
|
+
hasChanges = false;
|
|
1416
|
+
debug(message, ...args) {
|
|
1417
|
+
// Only log in development
|
|
1418
|
+
if (process.env['NODE_ENV'] !== 'production') {
|
|
1419
|
+
console.log(`[BaseTabbedFormComponent] ${message}`, ...args);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
constructor(route, router, notificationService, utilsService) {
|
|
1423
|
+
super(utilsService, notificationService);
|
|
1424
|
+
this.route = route;
|
|
1425
|
+
this.router = router;
|
|
1426
|
+
this.notificationService = notificationService;
|
|
1427
|
+
this.utilsService = utilsService;
|
|
1428
|
+
}
|
|
1429
|
+
ngOnInit() {
|
|
1430
|
+
try {
|
|
1431
|
+
// Initialize tabs
|
|
1432
|
+
this.tabs = this.getTabs();
|
|
1433
|
+
this.activeTab = this.tabs[0]?.value || '';
|
|
1434
|
+
// Initialize form using query params
|
|
1435
|
+
this.route.queryParams.pipe(take(1)).subscribe({
|
|
1436
|
+
next: params => this.initializeForm(params),
|
|
1437
|
+
error: error => {
|
|
1438
|
+
console.error('Error initializing form:', error);
|
|
1439
|
+
this.showError(AppMessages.FORM.ERROR.INIT);
|
|
1440
|
+
}
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
catch (error) {
|
|
1444
|
+
console.error('Error in ngOnInit:', error);
|
|
1445
|
+
this.showError(AppMessages.FORM.ERROR.INIT);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
initializeFormData() {
|
|
1449
|
+
this.debug('Initializing form data');
|
|
1450
|
+
this.debug('Current mode:', this.mode);
|
|
1451
|
+
if (this.mode === AppConstants.PAGE_MODE.CREATE) {
|
|
1452
|
+
this.debug('Create mode, using defaults');
|
|
1453
|
+
this.loadDefaultTab();
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
const state = {
|
|
1457
|
+
...this.router.getCurrentNavigation()?.extras?.state,
|
|
1458
|
+
...window.history.state
|
|
1459
|
+
};
|
|
1460
|
+
this.debug('Combined state:', state);
|
|
1461
|
+
if (state?.formData) {
|
|
1462
|
+
this.debug('Found form data in state:', state.formData);
|
|
1463
|
+
this.patchFormData(state.formData);
|
|
1464
|
+
this.loadDefaultTab();
|
|
1465
|
+
}
|
|
1466
|
+
else {
|
|
1467
|
+
this.debug('No form data, loading from UUID');
|
|
1468
|
+
this.loadFromUuid();
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
loadFromUuid() {
|
|
1472
|
+
// Flatten nested subscriptions using RxJS operators
|
|
1473
|
+
this.route.queryParams.pipe(take(1), map((params) => {
|
|
1474
|
+
const uuid = params[AppConstants.FORM.UUID];
|
|
1475
|
+
if (!uuid) {
|
|
1476
|
+
throw new Error(`UUID is required for ${this.mode} mode when no formData is provided`);
|
|
1477
|
+
}
|
|
1478
|
+
return uuid;
|
|
1479
|
+
})).subscribe({
|
|
1480
|
+
next: (uuid) => this.loadEntity(uuid),
|
|
1481
|
+
error: (error) => {
|
|
1482
|
+
console.error('Error loading UUID:', error);
|
|
1483
|
+
this.showError(`${AppMessages.FORM.ERROR.LOAD} UUID`);
|
|
1484
|
+
}
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
1487
|
+
initializeForm(params) {
|
|
1488
|
+
// Remove debug logging for production
|
|
1489
|
+
const routeMode = params[AppConstants.FORM.MODE];
|
|
1490
|
+
try {
|
|
1491
|
+
this.initializeFormMode(routeMode);
|
|
1492
|
+
this.setupFormState();
|
|
1493
|
+
if (this.formType === TabbedFormType.LIST) {
|
|
1494
|
+
this.initializeListForm(params);
|
|
1495
|
+
}
|
|
1496
|
+
else {
|
|
1497
|
+
this.initializeFormData();
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
catch (error) {
|
|
1501
|
+
console.error('Error initializing form:', error);
|
|
1502
|
+
this.showError(`${AppMessages.FORM.ERROR.INIT}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
patchFormData(data) {
|
|
1506
|
+
if (data) {
|
|
1507
|
+
this.currentFormData = { ...data };
|
|
1508
|
+
this.originalFormData = { ...data }; // Create a deep copy to avoid reference issues
|
|
1509
|
+
this.isNew = false;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
initializeFormMode(routeMode) {
|
|
1513
|
+
if (!routeMode) {
|
|
1514
|
+
this.mode = AppConstants.PAGE_MODE.CREATE;
|
|
1515
|
+
return;
|
|
1516
|
+
}
|
|
1517
|
+
// Validate mode more explicitly
|
|
1518
|
+
if (!this.isValidPageMode(routeMode)) {
|
|
1519
|
+
const validModes = Object.values(AppConstants.PAGE_MODE).join(', ');
|
|
1520
|
+
throw new Error(`Invalid page mode: ${routeMode}. Valid modes are: ${validModes}`);
|
|
1521
|
+
}
|
|
1522
|
+
this.mode = routeMode;
|
|
1523
|
+
}
|
|
1524
|
+
setupFormState() {
|
|
1525
|
+
this.debug('Setting up form state, mode:', this.mode);
|
|
1526
|
+
if (this.mode === AppConstants.PAGE_MODE.CREATE) {
|
|
1527
|
+
this.debug('Create mode setup');
|
|
1528
|
+
this.isNew = true;
|
|
1529
|
+
this.loading = false;
|
|
1530
|
+
this.originalFormData = null;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
loadEntity(uuid) {
|
|
1534
|
+
this.loading = true;
|
|
1535
|
+
this.isNew = false;
|
|
1536
|
+
const entityName = this.getEntityName();
|
|
1537
|
+
this.getEntityService().getById(uuid).subscribe({
|
|
1538
|
+
next: (data) => {
|
|
1539
|
+
this.originalFormData = data;
|
|
1540
|
+
this.currentFormData = { ...data };
|
|
1541
|
+
this.loading = false;
|
|
1542
|
+
this.debug('Original data loaded:', this.originalFormData);
|
|
1543
|
+
this.debug('Form data updated:', this.currentFormData);
|
|
1544
|
+
this.loadDefaultTab();
|
|
1545
|
+
},
|
|
1546
|
+
error: (error) => {
|
|
1547
|
+
this.debug('Error loading entity:', error);
|
|
1548
|
+
this.showError(`${AppMessages.FORM.ERROR.LOAD} ${entityName}`);
|
|
1549
|
+
this.loading = false;
|
|
1550
|
+
}
|
|
1551
|
+
});
|
|
1552
|
+
}
|
|
1553
|
+
findTabByValue(value) {
|
|
1554
|
+
return this.tabs.find(tab => tab.value === value);
|
|
1555
|
+
}
|
|
1556
|
+
loadDefaultTab() {
|
|
1557
|
+
this.debug('Loading default tab');
|
|
1558
|
+
const defaultTab = this.tabs.find(tab => tab.isParentModel) || this.tabs[0];
|
|
1559
|
+
if (!defaultTab)
|
|
1560
|
+
return;
|
|
1561
|
+
if (this.formType === TabbedFormType.LIST) {
|
|
1562
|
+
const state = this.router.getCurrentNavigation()?.extras?.state || window.history.state;
|
|
1563
|
+
const formDataList = state?.formDataList || [];
|
|
1564
|
+
const selectedData = formDataList.find((data) => data.uuid === defaultTab.value);
|
|
1565
|
+
this.router.navigate([defaultTab.value], {
|
|
1566
|
+
relativeTo: this.route,
|
|
1567
|
+
state: {
|
|
1568
|
+
formData: selectedData,
|
|
1569
|
+
formDataList: formDataList,
|
|
1570
|
+
context: ComponentContext.TABBED
|
|
1571
|
+
}
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
else {
|
|
1575
|
+
this.router.navigate([defaultTab.value], {
|
|
1576
|
+
relativeTo: this.route,
|
|
1577
|
+
queryParamsHandling: 'preserve', // Keep existing query params (mode & uuid)
|
|
1578
|
+
state: {
|
|
1579
|
+
formData: this.currentFormData,
|
|
1580
|
+
context: ComponentContext.TABBED
|
|
1581
|
+
}
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
getTabFormData(tabConfig) {
|
|
1586
|
+
if (!tabConfig)
|
|
1587
|
+
return null;
|
|
1588
|
+
if (tabConfig.isParentModel) {
|
|
1589
|
+
return this.currentFormData;
|
|
1590
|
+
}
|
|
1591
|
+
const fieldName = this.utilsService.toCamelCase(tabConfig.value);
|
|
1592
|
+
// Use type assertion since we know this is a valid field
|
|
1593
|
+
return this.currentFormData[fieldName];
|
|
1594
|
+
}
|
|
1595
|
+
initializeListForm(params) {
|
|
1596
|
+
const state = this.router.getCurrentNavigation()?.extras?.state || window.history.state;
|
|
1597
|
+
const formDataList = state?.formDataList;
|
|
1598
|
+
if (!formDataList?.length) {
|
|
1599
|
+
throw new Error('formDataList is required in state for LIST type');
|
|
1600
|
+
}
|
|
1601
|
+
// Create tabs from list in state
|
|
1602
|
+
this.tabs = formDataList.map((data) => ({
|
|
1603
|
+
value: data.uuid,
|
|
1604
|
+
title: this.getTabTitle(data),
|
|
1605
|
+
isParentModel: true
|
|
1606
|
+
}));
|
|
1607
|
+
// Select tab based on UUID in params or first tab
|
|
1608
|
+
const uuid = params[AppConstants.FORM.UUID];
|
|
1609
|
+
this.activeTab = uuid && this.tabs.find(t => t.value === uuid)
|
|
1610
|
+
? uuid
|
|
1611
|
+
: this.tabs[0].value;
|
|
1612
|
+
// Set current form data from list
|
|
1613
|
+
const selectedData = formDataList.find((d) => d.uuid === this.activeTab);
|
|
1614
|
+
this.currentFormData = selectedData;
|
|
1615
|
+
this.originalFormData = selectedData;
|
|
1616
|
+
}
|
|
1617
|
+
onTabChange(value) {
|
|
1618
|
+
const tabConfig = this.findTabByValue(value);
|
|
1619
|
+
if (!tabConfig) {
|
|
1620
|
+
console.warn(`Tab not found: ${value}`);
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
this.activeTab = tabConfig.value;
|
|
1624
|
+
if (this.formType === TabbedFormType.LIST) {
|
|
1625
|
+
const state = this.router.getCurrentNavigation()?.extras?.state || window.history.state;
|
|
1626
|
+
const formDataList = state?.formDataList || [];
|
|
1627
|
+
const selectedData = formDataList.find((data) => data.uuid === value);
|
|
1628
|
+
// Update current form data
|
|
1629
|
+
this.currentFormData = selectedData;
|
|
1630
|
+
this.originalFormData = selectedData;
|
|
1631
|
+
this.router.navigate([value], {
|
|
1632
|
+
relativeTo: this.route,
|
|
1633
|
+
state: {
|
|
1634
|
+
formData: selectedData,
|
|
1635
|
+
formDataList: formDataList,
|
|
1636
|
+
context: ComponentContext.TABBED
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
else {
|
|
1641
|
+
const formData = this.getTabFormData(tabConfig);
|
|
1642
|
+
this.router.navigate([this.activeTab], {
|
|
1643
|
+
relativeTo: this.route,
|
|
1644
|
+
queryParamsHandling: 'preserve',
|
|
1645
|
+
state: {
|
|
1646
|
+
formData,
|
|
1647
|
+
context: ComponentContext.TABBED
|
|
1648
|
+
}
|
|
1649
|
+
});
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
hasFormChanges(currentData) {
|
|
1653
|
+
// For new forms or no original data, consider it changed
|
|
1654
|
+
if (this.isNew || !this.originalFormData) {
|
|
1655
|
+
return true;
|
|
1656
|
+
}
|
|
1657
|
+
// Deep comparison of objects
|
|
1658
|
+
return !this.isEqual(currentData, this.originalFormData);
|
|
1659
|
+
}
|
|
1660
|
+
isEqual(obj1, obj2) {
|
|
1661
|
+
if (obj1 === obj2)
|
|
1662
|
+
return true;
|
|
1663
|
+
if (typeof obj1 !== 'object' || obj1 === null ||
|
|
1664
|
+
typeof obj2 !== 'object' || obj2 === null)
|
|
1665
|
+
return false;
|
|
1666
|
+
const keys1 = Object.keys(obj1);
|
|
1667
|
+
const keys2 = Object.keys(obj2);
|
|
1668
|
+
if (keys1.length !== keys2.length)
|
|
1669
|
+
return false;
|
|
1670
|
+
return keys1.every(key => Object.prototype.hasOwnProperty.call(obj2, key) &&
|
|
1671
|
+
this.isEqual(obj1[key], obj2[key]));
|
|
1672
|
+
}
|
|
1673
|
+
isValidPageMode(mode) {
|
|
1674
|
+
return Object.values(AppConstants.PAGE_MODE).includes(mode);
|
|
1675
|
+
}
|
|
1676
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: BaseTabbedFormComponent, deps: [{ token: i1$2.ActivatedRoute }, { token: i1$2.Router }, { token: NotificationService }, { token: UtilsService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1677
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: BaseTabbedFormComponent, isStandalone: true, selector: "ng-component", usesInheritance: true, ngImport: i0, template: '', isInline: true });
|
|
1678
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: BaseTabbedFormComponent });
|
|
1679
|
+
}
|
|
1680
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: BaseTabbedFormComponent, decorators: [{
|
|
1681
|
+
type: Injectable
|
|
1682
|
+
}, {
|
|
1683
|
+
type: Component,
|
|
1684
|
+
args: [{
|
|
1685
|
+
template: ''
|
|
1686
|
+
}]
|
|
1687
|
+
}], ctorParameters: () => [{ type: i1$2.ActivatedRoute }, { type: i1$2.Router }, { type: NotificationService }, { type: UtilsService }] });
|
|
1688
|
+
|
|
1689
|
+
class IndianDatePipe {
|
|
1690
|
+
datePipe;
|
|
1691
|
+
constructor(datePipe) {
|
|
1692
|
+
this.datePipe = datePipe;
|
|
1693
|
+
}
|
|
1694
|
+
transform(value, format = 'default') {
|
|
1695
|
+
if (!value)
|
|
1696
|
+
return null;
|
|
1697
|
+
// First validate dd-MM-yyyy format
|
|
1698
|
+
const dateRegex = /^(0[1-9]|[12][0-9]|3[01])-(0[1-9]|1[0-2])-\d{4}$/;
|
|
1699
|
+
if (!dateRegex.test(value)) {
|
|
1700
|
+
throw new Error(`Invalid date format: ${value}. Expected format: dd-MM-yyyy`);
|
|
1701
|
+
}
|
|
1702
|
+
// Validate real date
|
|
1703
|
+
const [day, month, year] = value.split('-').map(Number);
|
|
1704
|
+
const date = new Date(year, month - 1, day);
|
|
1705
|
+
if (date.getFullYear() !== year ||
|
|
1706
|
+
date.getMonth() !== month - 1 ||
|
|
1707
|
+
date.getDate() !== day) {
|
|
1708
|
+
throw new Error(`Invalid date: ${value}`);
|
|
1709
|
+
}
|
|
1710
|
+
// Apply requested format
|
|
1711
|
+
switch (format) {
|
|
1712
|
+
case 'short':
|
|
1713
|
+
// Pad with leading zeros for consistent format
|
|
1714
|
+
const paddedDay = day.toString().padStart(2, '0');
|
|
1715
|
+
const paddedMonth = month.toString().padStart(2, '0');
|
|
1716
|
+
return `${paddedDay}-${paddedMonth}-${year}`; // 10-03-2025
|
|
1717
|
+
case 'medium':
|
|
1718
|
+
return this.datePipe.transform(date, 'dd MMM yyyy'); // 10 Jan 2019
|
|
1719
|
+
default:
|
|
1720
|
+
return value; // original dd-MM-yyyy
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: IndianDatePipe, deps: [{ token: i1$3.DatePipe }], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1724
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: IndianDatePipe, isStandalone: true, name: "indianDate" });
|
|
1725
|
+
}
|
|
1726
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: IndianDatePipe, decorators: [{
|
|
1727
|
+
type: Pipe,
|
|
1728
|
+
args: [{
|
|
1729
|
+
name: 'indianDate',
|
|
1730
|
+
standalone: true
|
|
1731
|
+
}]
|
|
1732
|
+
}], ctorParameters: () => [{ type: i1$3.DatePipe }] });
|
|
1733
|
+
|
|
1734
|
+
class TextInputType extends FieldType {
|
|
1735
|
+
defaultOptions = {
|
|
1736
|
+
props: {
|
|
1737
|
+
type: 'text',
|
|
1738
|
+
placeholder: ''
|
|
1739
|
+
}
|
|
1740
|
+
};
|
|
1741
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextInputType, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1742
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: TextInputType, isStandalone: true, selector: "formly-field-text-input", usesInheritance: true, ngImport: i0, template: `
|
|
1743
|
+
<input pInputText
|
|
1744
|
+
[type]="props['type'] || 'text'"
|
|
1745
|
+
[formControl]="$any(formControl)"
|
|
1746
|
+
[formlyAttributes]="field"
|
|
1747
|
+
[placeholder]="props['placeholder'] || ''"
|
|
1748
|
+
[attr.aria-label]="props['label'] || props['placeholder'] || ''"
|
|
1749
|
+
class="w-full"/>
|
|
1750
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$4.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2.ɵFormlyAttributes, selector: "[formlyAttributes]", inputs: ["formlyAttributes", "id"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }] });
|
|
1751
|
+
}
|
|
1752
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextInputType, decorators: [{
|
|
1753
|
+
type: Component,
|
|
1754
|
+
args: [{
|
|
1755
|
+
selector: 'formly-field-text-input',
|
|
1756
|
+
standalone: true,
|
|
1757
|
+
imports: [
|
|
1758
|
+
CommonModule,
|
|
1759
|
+
ReactiveFormsModule,
|
|
1760
|
+
FormlyModule,
|
|
1761
|
+
InputTextModule
|
|
1762
|
+
],
|
|
1763
|
+
template: `
|
|
1764
|
+
<input pInputText
|
|
1765
|
+
[type]="props['type'] || 'text'"
|
|
1766
|
+
[formControl]="$any(formControl)"
|
|
1767
|
+
[formlyAttributes]="field"
|
|
1768
|
+
[placeholder]="props['placeholder'] || ''"
|
|
1769
|
+
[attr.aria-label]="props['label'] || props['placeholder'] || ''"
|
|
1770
|
+
class="w-full"/>
|
|
1771
|
+
`
|
|
1772
|
+
}]
|
|
1773
|
+
}] });
|
|
1774
|
+
|
|
1775
|
+
class SelectType extends FieldType {
|
|
1776
|
+
get options$() {
|
|
1777
|
+
const options = this.props['options'];
|
|
1778
|
+
return isObservable(options) ? options : of(options || []);
|
|
1779
|
+
}
|
|
1780
|
+
defaultOptions = {
|
|
1781
|
+
props: {
|
|
1782
|
+
options: [],
|
|
1783
|
+
optionLabel: 'label',
|
|
1784
|
+
optionValue: 'value',
|
|
1785
|
+
placeholder: 'Select',
|
|
1786
|
+
showClear: false,
|
|
1787
|
+
filter: false,
|
|
1788
|
+
virtualScroll: false
|
|
1789
|
+
}
|
|
1790
|
+
};
|
|
1791
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SelectType, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1792
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: SelectType, isStandalone: true, selector: "formly-field-select", usesInheritance: true, ngImport: i0, template: `
|
|
1793
|
+
<ng-container *ngIf="options$ | async as options">
|
|
1794
|
+
<p-select
|
|
1795
|
+
[formControl]="$any(formControl)"
|
|
1796
|
+
[formlyAttributes]="field"
|
|
1797
|
+
[options]="options"
|
|
1798
|
+
[virtualScroll]="false"
|
|
1799
|
+
[showClear]="false"
|
|
1800
|
+
[placeholder]="props['placeholder'] || 'Select'"
|
|
1801
|
+
styleClass="w-full">
|
|
1802
|
+
</p-select>
|
|
1803
|
+
</ng-container>
|
|
1804
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$3.AsyncPipe, name: "async" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$4.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2.ɵFormlyAttributes, selector: "[formlyAttributes]", inputs: ["formlyAttributes", "id"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "size", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }] });
|
|
1805
|
+
}
|
|
1806
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SelectType, decorators: [{
|
|
1807
|
+
type: Component,
|
|
1808
|
+
args: [{
|
|
1809
|
+
selector: 'formly-field-select',
|
|
1810
|
+
standalone: true,
|
|
1811
|
+
imports: [
|
|
1812
|
+
CommonModule,
|
|
1813
|
+
ReactiveFormsModule,
|
|
1814
|
+
FormlyModule,
|
|
1815
|
+
SelectModule
|
|
1816
|
+
],
|
|
1817
|
+
template: `
|
|
1818
|
+
<ng-container *ngIf="options$ | async as options">
|
|
1819
|
+
<p-select
|
|
1820
|
+
[formControl]="$any(formControl)"
|
|
1821
|
+
[formlyAttributes]="field"
|
|
1822
|
+
[options]="options"
|
|
1823
|
+
[virtualScroll]="false"
|
|
1824
|
+
[showClear]="false"
|
|
1825
|
+
[placeholder]="props['placeholder'] || 'Select'"
|
|
1826
|
+
styleClass="w-full">
|
|
1827
|
+
</p-select>
|
|
1828
|
+
</ng-container>
|
|
1829
|
+
`
|
|
1830
|
+
}]
|
|
1831
|
+
}] });
|
|
1832
|
+
|
|
1833
|
+
class RadioType extends FieldType {
|
|
1834
|
+
get options$() {
|
|
1835
|
+
const options = this.props['options'];
|
|
1836
|
+
return isObservable(options) ? options : of(options || []);
|
|
1837
|
+
}
|
|
1838
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: RadioType, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1839
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: RadioType, isStandalone: true, selector: "formly-field-radio", usesInheritance: true, ngImport: i0, template: `
|
|
1840
|
+
<div class="flex flex-column gap-1">
|
|
1841
|
+
<ng-container *ngIf="options$ | async as options">
|
|
1842
|
+
<div *ngFor="let option of options; let i = index" class="flex align-items-center gap-2">
|
|
1843
|
+
<p-radioButton
|
|
1844
|
+
[name]="field.name || ''"
|
|
1845
|
+
[value]="option.value"
|
|
1846
|
+
[formControl]="$any(formControl)"
|
|
1847
|
+
[formlyAttributes]="field"
|
|
1848
|
+
[size]="'small'"
|
|
1849
|
+
[inputId]="field.key + '_' + i">
|
|
1850
|
+
</p-radioButton>
|
|
1851
|
+
<label [for]="field.key + '_' + i" class="p-radio-label cursor-pointer">
|
|
1852
|
+
{{option.label}}
|
|
1853
|
+
</label>
|
|
1854
|
+
</div>
|
|
1855
|
+
</ng-container>
|
|
1856
|
+
</div>
|
|
1857
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$3.AsyncPipe, name: "async" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$4.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2.ɵFormlyAttributes, selector: "[formlyAttributes]", inputs: ["formlyAttributes", "id"] }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i4$1.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "formControlName", "name", "disabled", "variant", "size", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "style", "styleClass", "autofocus", "binary"], outputs: ["onClick", "onFocus", "onBlur"] }] });
|
|
1858
|
+
}
|
|
1859
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: RadioType, decorators: [{
|
|
1860
|
+
type: Component,
|
|
1861
|
+
args: [{
|
|
1862
|
+
selector: 'formly-field-radio',
|
|
1863
|
+
standalone: true,
|
|
1864
|
+
imports: [
|
|
1865
|
+
CommonModule,
|
|
1866
|
+
ReactiveFormsModule,
|
|
1867
|
+
FormlyModule,
|
|
1868
|
+
RadioButtonModule
|
|
1869
|
+
],
|
|
1870
|
+
template: `
|
|
1871
|
+
<div class="flex flex-column gap-1">
|
|
1872
|
+
<ng-container *ngIf="options$ | async as options">
|
|
1873
|
+
<div *ngFor="let option of options; let i = index" class="flex align-items-center gap-2">
|
|
1874
|
+
<p-radioButton
|
|
1875
|
+
[name]="field.name || ''"
|
|
1876
|
+
[value]="option.value"
|
|
1877
|
+
[formControl]="$any(formControl)"
|
|
1878
|
+
[formlyAttributes]="field"
|
|
1879
|
+
[size]="'small'"
|
|
1880
|
+
[inputId]="field.key + '_' + i">
|
|
1881
|
+
</p-radioButton>
|
|
1882
|
+
<label [for]="field.key + '_' + i" class="p-radio-label cursor-pointer">
|
|
1883
|
+
{{option.label}}
|
|
1884
|
+
</label>
|
|
1885
|
+
</div>
|
|
1886
|
+
</ng-container>
|
|
1887
|
+
</div>
|
|
1888
|
+
`
|
|
1889
|
+
}]
|
|
1890
|
+
}] });
|
|
1891
|
+
|
|
1892
|
+
class NumericInputType extends FieldType {
|
|
1893
|
+
defaultOptions = {
|
|
1894
|
+
props: {
|
|
1895
|
+
min: undefined,
|
|
1896
|
+
max: undefined,
|
|
1897
|
+
minFractionDigits: 0,
|
|
1898
|
+
maxFractionDigits: 0,
|
|
1899
|
+
prefix: '',
|
|
1900
|
+
suffix: '',
|
|
1901
|
+
useGrouping: true
|
|
1902
|
+
}
|
|
1903
|
+
};
|
|
1904
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: NumericInputType, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1905
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: NumericInputType, isStandalone: true, selector: "formly-field-numeric-input", usesInheritance: true, ngImport: i0, template: `
|
|
1906
|
+
<p-inputNumber
|
|
1907
|
+
[formControl]="$any(formControl)"
|
|
1908
|
+
[formlyAttributes]="field"
|
|
1909
|
+
[placeholder]="props['placeholder'] || ''"
|
|
1910
|
+
[min]="props['min'] || 0"
|
|
1911
|
+
[max]="props['max']"
|
|
1912
|
+
[minFractionDigits]="props['minFractionDigits'] || 0"
|
|
1913
|
+
[maxFractionDigits]="props['maxFractionDigits'] || 0"
|
|
1914
|
+
[prefix]="props['prefix'] || ''"
|
|
1915
|
+
[suffix]="props['suffix'] || ''"
|
|
1916
|
+
[useGrouping]="props['useGrouping'] ?? true"
|
|
1917
|
+
[showButtons]="true"
|
|
1918
|
+
class="w-full">
|
|
1919
|
+
</p-inputNumber>
|
|
1920
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$4.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2.ɵFormlyAttributes, selector: "[formlyAttributes]", inputs: ["formlyAttributes", "id"] }, { kind: "ngmodule", type: InputNumberModule }, { kind: "component", type: i3$1.InputNumber, selector: "p-inputNumber, p-inputnumber, p-input-number", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "style", "placeholder", "size", "maxlength", "tabindex", "title", "ariaLabelledBy", "ariaLabel", "ariaRequired", "name", "required", "autocomplete", "min", "max", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "step", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "variant", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus", "disabled", "fluid"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }] });
|
|
1921
|
+
}
|
|
1922
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: NumericInputType, decorators: [{
|
|
1923
|
+
type: Component,
|
|
1924
|
+
args: [{
|
|
1925
|
+
selector: 'formly-field-numeric-input',
|
|
1926
|
+
standalone: true,
|
|
1927
|
+
imports: [
|
|
1928
|
+
CommonModule,
|
|
1929
|
+
ReactiveFormsModule,
|
|
1930
|
+
FormlyModule,
|
|
1931
|
+
InputNumberModule
|
|
1932
|
+
],
|
|
1933
|
+
template: `
|
|
1934
|
+
<p-inputNumber
|
|
1935
|
+
[formControl]="$any(formControl)"
|
|
1936
|
+
[formlyAttributes]="field"
|
|
1937
|
+
[placeholder]="props['placeholder'] || ''"
|
|
1938
|
+
[min]="props['min'] || 0"
|
|
1939
|
+
[max]="props['max']"
|
|
1940
|
+
[minFractionDigits]="props['minFractionDigits'] || 0"
|
|
1941
|
+
[maxFractionDigits]="props['maxFractionDigits'] || 0"
|
|
1942
|
+
[prefix]="props['prefix'] || ''"
|
|
1943
|
+
[suffix]="props['suffix'] || ''"
|
|
1944
|
+
[useGrouping]="props['useGrouping'] ?? true"
|
|
1945
|
+
[showButtons]="true"
|
|
1946
|
+
class="w-full">
|
|
1947
|
+
</p-inputNumber>
|
|
1948
|
+
`
|
|
1949
|
+
}]
|
|
1950
|
+
}] });
|
|
1951
|
+
|
|
1952
|
+
class MultiselectType extends FieldType {
|
|
1953
|
+
get options$() {
|
|
1954
|
+
const options = this.props['options'];
|
|
1955
|
+
return isObservable(options) ? options : of(options || []);
|
|
1956
|
+
}
|
|
1957
|
+
defaultOptions = {
|
|
1958
|
+
props: {
|
|
1959
|
+
options: [],
|
|
1960
|
+
optionLabel: 'label',
|
|
1961
|
+
optionValue: 'value',
|
|
1962
|
+
placeholder: 'Select',
|
|
1963
|
+
filter: false,
|
|
1964
|
+
maxSelectedLabels: 3,
|
|
1965
|
+
showToggleAll: true,
|
|
1966
|
+
virtualScroll: false
|
|
1967
|
+
}
|
|
1968
|
+
};
|
|
1969
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiselectType, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1970
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: MultiselectType, isStandalone: true, selector: "formly-field-multiselect", usesInheritance: true, ngImport: i0, template: `
|
|
1971
|
+
<ng-container *ngIf="options$ | async as options">
|
|
1972
|
+
<p-multiSelect
|
|
1973
|
+
[formControl]="$any(formControl)"
|
|
1974
|
+
[formlyAttributes]="field"
|
|
1975
|
+
[options]="options"
|
|
1976
|
+
[optionLabel]="props['optionLabel'] || 'label'"
|
|
1977
|
+
[optionValue]="props['optionValue'] || 'value'"
|
|
1978
|
+
[placeholder]="props['placeholder'] || 'Select'"
|
|
1979
|
+
[filter]="props['filter'] || true"
|
|
1980
|
+
[filterBy]="props['filterBy'] || 'label'"
|
|
1981
|
+
[maxSelectedLabels]="props['maxSelectedLabels'] || 2"
|
|
1982
|
+
[showToggleAll]="props['showToggleAll'] || true"
|
|
1983
|
+
[virtualScroll]="props['virtualScroll'] || false"
|
|
1984
|
+
[display]="props['display'] || 'chip'"
|
|
1985
|
+
styleClass="w-full">
|
|
1986
|
+
</p-multiSelect>
|
|
1987
|
+
</ng-container>
|
|
1988
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$3.AsyncPipe, name: "async" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$4.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2.ɵFormlyAttributes, selector: "[formlyAttributes]", inputs: ["formlyAttributes", "id"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i4$2.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "style", "styleClass", "panelStyle", "panelStyleClass", "inputId", "disabled", "fluid", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "variant", "appendTo", "dataKey", "name", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "size", "showClear", "autofocus", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "defaultLabel", "placeholder", "options", "filterValue", "itemSize", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }] });
|
|
1989
|
+
}
|
|
1990
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiselectType, decorators: [{
|
|
1991
|
+
type: Component,
|
|
1992
|
+
args: [{
|
|
1993
|
+
selector: 'formly-field-multiselect',
|
|
1994
|
+
standalone: true,
|
|
1995
|
+
imports: [
|
|
1996
|
+
CommonModule,
|
|
1997
|
+
ReactiveFormsModule,
|
|
1998
|
+
FormlyModule,
|
|
1999
|
+
MultiSelectModule
|
|
2000
|
+
],
|
|
2001
|
+
template: `
|
|
2002
|
+
<ng-container *ngIf="options$ | async as options">
|
|
2003
|
+
<p-multiSelect
|
|
2004
|
+
[formControl]="$any(formControl)"
|
|
2005
|
+
[formlyAttributes]="field"
|
|
2006
|
+
[options]="options"
|
|
2007
|
+
[optionLabel]="props['optionLabel'] || 'label'"
|
|
2008
|
+
[optionValue]="props['optionValue'] || 'value'"
|
|
2009
|
+
[placeholder]="props['placeholder'] || 'Select'"
|
|
2010
|
+
[filter]="props['filter'] || true"
|
|
2011
|
+
[filterBy]="props['filterBy'] || 'label'"
|
|
2012
|
+
[maxSelectedLabels]="props['maxSelectedLabels'] || 2"
|
|
2013
|
+
[showToggleAll]="props['showToggleAll'] || true"
|
|
2014
|
+
[virtualScroll]="props['virtualScroll'] || false"
|
|
2015
|
+
[display]="props['display'] || 'chip'"
|
|
2016
|
+
styleClass="w-full">
|
|
2017
|
+
</p-multiSelect>
|
|
2018
|
+
</ng-container>
|
|
2019
|
+
`
|
|
2020
|
+
}]
|
|
2021
|
+
}] });
|
|
2022
|
+
|
|
2023
|
+
class DateType extends FieldType {
|
|
2024
|
+
defaultOptions = {
|
|
2025
|
+
props: {
|
|
2026
|
+
dateFormat: 'dd-mm-yy',
|
|
2027
|
+
showTime: false,
|
|
2028
|
+
touchUI: false,
|
|
2029
|
+
placeholder: '',
|
|
2030
|
+
showButtonBar: true,
|
|
2031
|
+
}
|
|
2032
|
+
};
|
|
2033
|
+
onDateSelect(event) {
|
|
2034
|
+
this.formatAndSetDate(event);
|
|
2035
|
+
}
|
|
2036
|
+
onDateInput(event) {
|
|
2037
|
+
const inputValue = event.target.value.trim();
|
|
2038
|
+
if (this.isValidDateInput(inputValue, this.props['dateFormat'] || 'dd-mm-yy')) {
|
|
2039
|
+
const parsedDate = this.parseDate(inputValue, this.props['dateFormat'] || 'dd-mm-yy');
|
|
2040
|
+
if (parsedDate) {
|
|
2041
|
+
this.formatAndSetDate(parsedDate);
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
else {
|
|
2045
|
+
this.formControl?.setErrors({ invalidDate: true });
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
formatAndSetDate(date) {
|
|
2049
|
+
if (this.formControl) {
|
|
2050
|
+
const formattedDate = formatDate(date, 'dd-MM-yyyy', 'en-US');
|
|
2051
|
+
this.formControl.setValue(formattedDate);
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
parseDate(value, format) {
|
|
2055
|
+
const parts = value.split(/[-/]/); // Split by common delimiters
|
|
2056
|
+
if (parts.length === 3) {
|
|
2057
|
+
const [day, month, year] = format === 'dd-mm-yy'
|
|
2058
|
+
? [parseInt(parts[0], 10), parseInt(parts[1], 10) - 1, parseInt(parts[2], 10)]
|
|
2059
|
+
: [parseInt(parts[1], 10), parseInt(parts[0], 10) - 1, parseInt(parts[2], 10)];
|
|
2060
|
+
return new Date(year, month, day);
|
|
2061
|
+
}
|
|
2062
|
+
return null;
|
|
2063
|
+
}
|
|
2064
|
+
isValidDateInput(value, format) {
|
|
2065
|
+
const dateRegex = format === 'dd-mm-yy'
|
|
2066
|
+
? /^\d{2}-\d{2}-\d{4}$/
|
|
2067
|
+
: /^\d{2}\/\d{2}\/\d{4}$/;
|
|
2068
|
+
return dateRegex.test(value);
|
|
2069
|
+
}
|
|
2070
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DateType, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
2071
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: DateType, isStandalone: true, selector: "formly-field-date", usesInheritance: true, ngImport: i0, template: `
|
|
2072
|
+
<p-datepicker
|
|
2073
|
+
[formControl]="$any(formControl)"
|
|
2074
|
+
[formlyAttributes]="field"
|
|
2075
|
+
[showIcon]="true"
|
|
2076
|
+
[dateFormat]="props['dateFormat'] || 'dd-mm-yy'"
|
|
2077
|
+
[showTime]="props['showTime'] || false"
|
|
2078
|
+
[touchUI]="props['touchUI'] || false"
|
|
2079
|
+
[placeholder]="props['placeholder'] || ''"
|
|
2080
|
+
[minDate]="props['minDate']"
|
|
2081
|
+
[maxDate]="props['maxDate']"
|
|
2082
|
+
[showOnFocus]="false"
|
|
2083
|
+
[todayButtonStyleClass]="'p-button-secondary'"
|
|
2084
|
+
[clearButtonStyleClass]="'p-button-secondary'"
|
|
2085
|
+
styleClass="w-full"
|
|
2086
|
+
(onSelect)="onDateSelect($event)"
|
|
2087
|
+
(onInput)="onDateInput($event)">
|
|
2088
|
+
</p-datepicker>
|
|
2089
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$4.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2.ɵFormlyAttributes, selector: "[formlyAttributes]", inputs: ["formlyAttributes", "id"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i3$2.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "style", "styleClass", "inputStyle", "inputId", "name", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "disabled", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "fluid", "icon", "appendTo", "readonlyInput", "shortYearCutoff", "monthNavigator", "yearNavigator", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "required", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "variant", "size", "minDate", "maxDate", "disabledDates", "disabledDays", "yearRange", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "locale", "view", "defaultDate"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }] });
|
|
2090
|
+
}
|
|
2091
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DateType, decorators: [{
|
|
2092
|
+
type: Component,
|
|
2093
|
+
args: [{
|
|
2094
|
+
selector: 'formly-field-date',
|
|
2095
|
+
standalone: true,
|
|
2096
|
+
imports: [
|
|
2097
|
+
CommonModule,
|
|
2098
|
+
ReactiveFormsModule,
|
|
2099
|
+
FormlyModule,
|
|
2100
|
+
DatePickerModule
|
|
2101
|
+
],
|
|
2102
|
+
template: `
|
|
2103
|
+
<p-datepicker
|
|
2104
|
+
[formControl]="$any(formControl)"
|
|
2105
|
+
[formlyAttributes]="field"
|
|
2106
|
+
[showIcon]="true"
|
|
2107
|
+
[dateFormat]="props['dateFormat'] || 'dd-mm-yy'"
|
|
2108
|
+
[showTime]="props['showTime'] || false"
|
|
2109
|
+
[touchUI]="props['touchUI'] || false"
|
|
2110
|
+
[placeholder]="props['placeholder'] || ''"
|
|
2111
|
+
[minDate]="props['minDate']"
|
|
2112
|
+
[maxDate]="props['maxDate']"
|
|
2113
|
+
[showOnFocus]="false"
|
|
2114
|
+
[todayButtonStyleClass]="'p-button-secondary'"
|
|
2115
|
+
[clearButtonStyleClass]="'p-button-secondary'"
|
|
2116
|
+
styleClass="w-full"
|
|
2117
|
+
(onSelect)="onDateSelect($event)"
|
|
2118
|
+
(onInput)="onDateInput($event)">
|
|
2119
|
+
</p-datepicker>
|
|
2120
|
+
`
|
|
2121
|
+
}]
|
|
2122
|
+
}] });
|
|
2123
|
+
|
|
2124
|
+
class CheckboxType extends FieldType {
|
|
2125
|
+
defaultOptions = {
|
|
2126
|
+
props: {
|
|
2127
|
+
trueValue: true,
|
|
2128
|
+
falseValue: false
|
|
2129
|
+
}
|
|
2130
|
+
};
|
|
2131
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CheckboxType, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
2132
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: CheckboxType, isStandalone: true, selector: "formly-field-checkbox", usesInheritance: true, ngImport: i0, template: `
|
|
2133
|
+
<div class="checkbox-container">
|
|
2134
|
+
<p-checkbox
|
|
2135
|
+
[formControl]="$any(formControl)"
|
|
2136
|
+
[formlyAttributes]="field"
|
|
2137
|
+
[binary]="!props['options']"
|
|
2138
|
+
[trueValue]="props['trueValue']"
|
|
2139
|
+
[falseValue]="props['falseValue']">
|
|
2140
|
+
{{props['checkboxLabel'] || ''}}
|
|
2141
|
+
</p-checkbox>
|
|
2142
|
+
</div>
|
|
2143
|
+
`, isInline: true, styles: [".checkbox-container{display:flex;align-items:center;height:40px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$4.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2.ɵFormlyAttributes, selector: "[formlyAttributes]", inputs: ["formlyAttributes", "id"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i3$3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }] });
|
|
2144
|
+
}
|
|
2145
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CheckboxType, decorators: [{
|
|
2146
|
+
type: Component,
|
|
2147
|
+
args: [{ selector: 'formly-field-checkbox', standalone: true, imports: [
|
|
2148
|
+
CommonModule,
|
|
2149
|
+
ReactiveFormsModule,
|
|
2150
|
+
FormlyModule,
|
|
2151
|
+
CheckboxModule
|
|
2152
|
+
], template: `
|
|
2153
|
+
<div class="checkbox-container">
|
|
2154
|
+
<p-checkbox
|
|
2155
|
+
[formControl]="$any(formControl)"
|
|
2156
|
+
[formlyAttributes]="field"
|
|
2157
|
+
[binary]="!props['options']"
|
|
2158
|
+
[trueValue]="props['trueValue']"
|
|
2159
|
+
[falseValue]="props['falseValue']">
|
|
2160
|
+
{{props['checkboxLabel'] || ''}}
|
|
2161
|
+
</p-checkbox>
|
|
2162
|
+
</div>
|
|
2163
|
+
`, styles: [".checkbox-container{display:flex;align-items:center;height:40px}\n"] }]
|
|
2164
|
+
}] });
|
|
2165
|
+
|
|
2166
|
+
class TextareaType extends FieldType {
|
|
2167
|
+
defaultOptions = {
|
|
2168
|
+
props: {
|
|
2169
|
+
rows: 3,
|
|
2170
|
+
cols: undefined,
|
|
2171
|
+
autoResize: true,
|
|
2172
|
+
placeholder: ''
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextareaType, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
2176
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: TextareaType, isStandalone: true, selector: "formly-field-textarea", usesInheritance: true, ngImport: i0, template: `
|
|
2177
|
+
<textarea pInputTextarea
|
|
2178
|
+
[formControl]="$any(formControl)"
|
|
2179
|
+
[formlyAttributes]="field"
|
|
2180
|
+
[rows]="props['rows'] || 3"
|
|
2181
|
+
[cols]="props['cols']"
|
|
2182
|
+
[autoResize]="props['autoResize']"
|
|
2183
|
+
[placeholder]="props['placeholder'] || ''"
|
|
2184
|
+
class="w-full">
|
|
2185
|
+
</textarea>
|
|
2186
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$4.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2.ɵFormlyAttributes, selector: "[formlyAttributes]", inputs: ["formlyAttributes", "id"] }, { kind: "ngmodule", type: Textarea }, { kind: "directive", type: i3$4.InputTextarea, selector: "[pInputTextarea]", inputs: ["autoResize", "variant", "fluid"], outputs: ["onResize"] }] });
|
|
2187
|
+
}
|
|
2188
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextareaType, decorators: [{
|
|
2189
|
+
type: Component,
|
|
2190
|
+
args: [{
|
|
2191
|
+
selector: 'formly-field-textarea',
|
|
2192
|
+
standalone: true,
|
|
2193
|
+
imports: [
|
|
2194
|
+
CommonModule,
|
|
2195
|
+
ReactiveFormsModule,
|
|
2196
|
+
FormlyModule,
|
|
2197
|
+
Textarea
|
|
2198
|
+
],
|
|
2199
|
+
template: `
|
|
2200
|
+
<textarea pInputTextarea
|
|
2201
|
+
[formControl]="$any(formControl)"
|
|
2202
|
+
[formlyAttributes]="field"
|
|
2203
|
+
[rows]="props['rows'] || 3"
|
|
2204
|
+
[cols]="props['cols']"
|
|
2205
|
+
[autoResize]="props['autoResize']"
|
|
2206
|
+
[placeholder]="props['placeholder'] || ''"
|
|
2207
|
+
class="w-full">
|
|
2208
|
+
</textarea>
|
|
2209
|
+
`
|
|
2210
|
+
}]
|
|
2211
|
+
}] });
|
|
2212
|
+
|
|
2213
|
+
class FormFieldWrapper extends FieldWrapper {
|
|
2214
|
+
get errorMessage() {
|
|
2215
|
+
if (!this.field.formControl?.errors) {
|
|
2216
|
+
return '';
|
|
2217
|
+
}
|
|
2218
|
+
const firstErrorKey = Object.keys(this.field.formControl.errors)[0];
|
|
2219
|
+
return this.props.validation?.messages?.[firstErrorKey] || 'This field is required';
|
|
2220
|
+
}
|
|
2221
|
+
get showError() {
|
|
2222
|
+
if (!this.field.formControl) {
|
|
2223
|
+
return false;
|
|
2224
|
+
}
|
|
2225
|
+
const { touched, dirty, invalid } = this.field.formControl;
|
|
2226
|
+
return invalid && (touched || dirty);
|
|
2227
|
+
}
|
|
2228
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: FormFieldWrapper, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
2229
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: FormFieldWrapper, isStandalone: true, selector: "formly-wrapper-form-field", usesInheritance: true, ngImport: i0, template: `
|
|
2230
|
+
<div class="field mb-4">
|
|
2231
|
+
<label *ngIf="props.label" [for]="id" class="font-medium">
|
|
2232
|
+
{{ props.label }}
|
|
2233
|
+
<span class="required-star" *ngIf="props.required">*</span>
|
|
2234
|
+
<i *ngIf="props.tooltip"
|
|
2235
|
+
class="pi pi-info-circle ml-2"
|
|
2236
|
+
[pTooltip]="props.tooltip">
|
|
2237
|
+
</i>
|
|
2238
|
+
</label>
|
|
2239
|
+
|
|
2240
|
+
<div [class.p-input-error]="showError">
|
|
2241
|
+
<ng-container #fieldComponent></ng-container>
|
|
2242
|
+
</div>
|
|
2243
|
+
|
|
2244
|
+
<small *ngIf="props.description" class="block mt-1">
|
|
2245
|
+
{{ props.description }}
|
|
2246
|
+
</small>
|
|
2247
|
+
|
|
2248
|
+
<small *ngIf="showError" class="p-error block">
|
|
2249
|
+
<i class="pi pi-exclamation-circle mr-2"></i>
|
|
2250
|
+
{{ errorMessage }}
|
|
2251
|
+
</small>
|
|
2252
|
+
</div>
|
|
2253
|
+
`, isInline: true, styles: [":is() .required-star{color:var(--red-500);margin-left:.25rem}:is() .p-input-error :where(input,.p-inputtext){border-color:var(--red-500)}:is() .p-input-error :where(input:enabled:focus,.p-inputtext:enabled:focus){border-color:var(--red-500);box-shadow:0 0 0 1px var(--red-500)}:is() .p-error{color:var(--red-500);margin-top:.5rem}:is() small{color:var(--text-color-secondary)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }] });
|
|
2254
|
+
}
|
|
2255
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: FormFieldWrapper, decorators: [{
|
|
2256
|
+
type: Component,
|
|
2257
|
+
args: [{ selector: 'formly-wrapper-form-field', standalone: true, imports: [CommonModule, TooltipModule], template: `
|
|
2258
|
+
<div class="field mb-4">
|
|
2259
|
+
<label *ngIf="props.label" [for]="id" class="font-medium">
|
|
2260
|
+
{{ props.label }}
|
|
2261
|
+
<span class="required-star" *ngIf="props.required">*</span>
|
|
2262
|
+
<i *ngIf="props.tooltip"
|
|
2263
|
+
class="pi pi-info-circle ml-2"
|
|
2264
|
+
[pTooltip]="props.tooltip">
|
|
2265
|
+
</i>
|
|
2266
|
+
</label>
|
|
2267
|
+
|
|
2268
|
+
<div [class.p-input-error]="showError">
|
|
2269
|
+
<ng-container #fieldComponent></ng-container>
|
|
2270
|
+
</div>
|
|
2271
|
+
|
|
2272
|
+
<small *ngIf="props.description" class="block mt-1">
|
|
2273
|
+
{{ props.description }}
|
|
2274
|
+
</small>
|
|
2275
|
+
|
|
2276
|
+
<small *ngIf="showError" class="p-error block">
|
|
2277
|
+
<i class="pi pi-exclamation-circle mr-2"></i>
|
|
2278
|
+
{{ errorMessage }}
|
|
2279
|
+
</small>
|
|
2280
|
+
</div>
|
|
2281
|
+
`, styles: [":is() .required-star{color:var(--red-500);margin-left:.25rem}:is() .p-input-error :where(input,.p-inputtext){border-color:var(--red-500)}:is() .p-input-error :where(input:enabled:focus,.p-inputtext:enabled:focus){border-color:var(--red-500);box-shadow:0 0 0 1px var(--red-500)}:is() .p-error{color:var(--red-500);margin-top:.5rem}:is() small{color:var(--text-color-secondary)}\n"] }]
|
|
2282
|
+
}] });
|
|
2283
|
+
|
|
2284
|
+
const FORMLY_TYPES = {
|
|
2285
|
+
TEXT: 'text',
|
|
2286
|
+
NUMBER: 'number',
|
|
2287
|
+
DATE: 'date',
|
|
2288
|
+
CHECKBOX: 'checkbox',
|
|
2289
|
+
RADIO: 'radio',
|
|
2290
|
+
SELECT: 'select',
|
|
2291
|
+
MULTISELECT: 'multiselect',
|
|
2292
|
+
TEXTAREA: 'textarea'
|
|
2293
|
+
};
|
|
2294
|
+
const FORMLY_WRAPPERS = {
|
|
2295
|
+
FORM_FIELD: 'form-field'
|
|
2296
|
+
};
|
|
2297
|
+
|
|
2298
|
+
class CoreFormlyModule {
|
|
2299
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CoreFormlyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
2300
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: CoreFormlyModule, imports: [i2.FormlyModule], exports: [FormlyModule] });
|
|
2301
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CoreFormlyModule, imports: [FormlyModule.forRoot({
|
|
2302
|
+
types: [
|
|
2303
|
+
{ name: FORMLY_TYPES.TEXT, component: TextInputType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2304
|
+
{ name: FORMLY_TYPES.NUMBER, component: NumericInputType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2305
|
+
{ name: FORMLY_TYPES.DATE, component: DateType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2306
|
+
{ name: FORMLY_TYPES.CHECKBOX, component: CheckboxType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2307
|
+
{ name: FORMLY_TYPES.RADIO, component: RadioType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2308
|
+
{ name: FORMLY_TYPES.SELECT, component: SelectType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2309
|
+
{ name: FORMLY_TYPES.MULTISELECT, component: MultiselectType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2310
|
+
{ name: FORMLY_TYPES.TEXTAREA, component: TextareaType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] }, // Added textarea type
|
|
2311
|
+
],
|
|
2312
|
+
wrappers: [
|
|
2313
|
+
{ name: FORMLY_WRAPPERS.FORM_FIELD, component: FormFieldWrapper }
|
|
2314
|
+
]
|
|
2315
|
+
}), FormlyModule] });
|
|
2316
|
+
}
|
|
2317
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CoreFormlyModule, decorators: [{
|
|
2318
|
+
type: NgModule,
|
|
2319
|
+
args: [{
|
|
2320
|
+
imports: [
|
|
2321
|
+
FormlyModule.forRoot({
|
|
2322
|
+
types: [
|
|
2323
|
+
{ name: FORMLY_TYPES.TEXT, component: TextInputType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2324
|
+
{ name: FORMLY_TYPES.NUMBER, component: NumericInputType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2325
|
+
{ name: FORMLY_TYPES.DATE, component: DateType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2326
|
+
{ name: FORMLY_TYPES.CHECKBOX, component: CheckboxType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2327
|
+
{ name: FORMLY_TYPES.RADIO, component: RadioType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2328
|
+
{ name: FORMLY_TYPES.SELECT, component: SelectType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2329
|
+
{ name: FORMLY_TYPES.MULTISELECT, component: MultiselectType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2330
|
+
{ name: FORMLY_TYPES.TEXTAREA, component: TextareaType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] }, // Added textarea type
|
|
2331
|
+
],
|
|
2332
|
+
wrappers: [
|
|
2333
|
+
{ name: FORMLY_WRAPPERS.FORM_FIELD, component: FormFieldWrapper }
|
|
2334
|
+
]
|
|
2335
|
+
})
|
|
2336
|
+
],
|
|
2337
|
+
exports: [FormlyModule]
|
|
2338
|
+
}]
|
|
2339
|
+
}] });
|
|
2340
|
+
|
|
2341
|
+
// Config
|
|
2342
|
+
// Services
|
|
2343
|
+
|
|
2344
|
+
/**
|
|
2345
|
+
* Generated bundle index. Do not edit.
|
|
2346
|
+
*/
|
|
2347
|
+
|
|
2348
|
+
export { AppConstants, AppMessages, BaseCrudService, BaseFormComponent, BaseListPageComponent, BasePageComponent, BaseTabbedFormComponent, CheckboxType, ComponentContext, CoreFormlyModule, DateType, EntityRegistryService, EnumRegistryService, FormFieldWrapper, FormlyConfigService, HttpUtilityService, IndianDatePipe, MultiselectType, NotificationService, NumericInputType, RadioType, ReferenceDataProviderService, SelectType, TabbedFormType, TextInputType, UtilsService };
|
|
2349
|
+
//# sourceMappingURL=core-lib.mjs.map
|