@codex-ts/core-lib 1.0.8 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +63 -0
  2. package/fesm2022/codex-ts-core-lib.mjs +1980 -1383
  3. package/fesm2022/codex-ts-core-lib.mjs.map +1 -1
  4. package/index.d.ts +5 -29
  5. package/lib/components/base-form.component.d.ts +27 -22
  6. package/lib/components/base-list-page.component.d.ts +12 -71
  7. package/lib/components/base-list-tabbed-form.component.d.ts +40 -0
  8. package/lib/components/base-tabbed-form.component.d.ts +17 -20
  9. package/lib/components/base-table.component.d.ts +72 -0
  10. package/lib/components/base-task-details.component.d.ts +59 -0
  11. package/lib/components/formly/types/numeric-input.type.d.ts +2 -2
  12. package/lib/components/formly/types/select.type.d.ts +1 -1
  13. package/lib/components/formly/types/textarea.type.d.ts +1 -1
  14. package/lib/enums/task-role.enum.d.ts +37 -0
  15. package/lib/keycloak/keycloak-config.interface.d.ts +19 -0
  16. package/lib/keycloak/keycloak.guard.d.ts +25 -0
  17. package/lib/keycloak/keycloak.service.d.ts +24 -0
  18. package/lib/keycloak/keycloak.token.d.ts +3 -0
  19. package/lib/models/base-create-request.model.d.ts +12 -0
  20. package/lib/models/base-request.model.d.ts +9 -0
  21. package/lib/models/base-response.model.d.ts +15 -0
  22. package/lib/models/base-task.model.d.ts +17 -0
  23. package/lib/models/base-update-request.model.d.ts +16 -0
  24. package/lib/models/base.model.d.ts +59 -0
  25. package/lib/models/pagination/page.interface.d.ts +23 -6
  26. package/lib/services/base-task.service.d.ts +40 -0
  27. package/lib/services/form.service.d.ts +31 -0
  28. package/lib/services/http-utility.service.d.ts +1 -1
  29. package/lib/states/form-component.state.d.ts +12 -0
  30. package/lib/states/form.state.d.ts +21 -0
  31. package/lib/states/list-tabbed-form.state.d.ts +9 -0
  32. package/lib/states/tabbed-form.state.d.ts +21 -0
  33. package/package.json +11 -25
  34. package/public-api.d.ts +37 -0
  35. package/lib/services/entity-registry.service.d.ts +0 -26
  36. package/lib/services/reference-data-provider.service.d.ts +0 -8
@@ -1,17 +1,17 @@
1
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';
2
+ import { InjectionToken, Inject, Injectable, Component, NgModule, Directive, Input, ViewChild, Pipe } from '@angular/core';
3
+ import { map, retry, timeout, catchError, take, finalize as finalize$1, switchMap, takeUntil, mergeMap, tap, debounceTime, distinctUntilChanged } from 'rxjs/operators';
4
+ import { BehaviorSubject, finalize, Observable, forkJoin, throwError, of, isObservable, Subject, take as take$1, tap as tap$1, map as map$1, catchError as catchError$1, takeUntil as takeUntil$1, ReplaySubject, distinctUntilChanged as distinctUntilChanged$1 } from 'rxjs';
5
+ import * as i1 from '@angular/common/http';
6
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';
7
+ import * as i1$1 from '@angular/router';
8
+ import * as i2 from 'primeng/api';
9
+ import * as i1$2 from '@angular/forms';
9
10
  import { FormGroup, ReactiveFormsModule } from '@angular/forms';
10
- import * as i1$2 from '@angular/router';
11
+ import * as i2$1 from '@ngx-formly/core';
12
+ import { FieldType, FormlyModule, FieldWrapper } from '@ngx-formly/core';
11
13
  import * as i1$3 from '@angular/common';
12
14
  import { CommonModule, formatDate } from '@angular/common';
13
- import * as i2 from '@ngx-formly/core';
14
- import { FieldType, FormlyModule, FieldWrapper } from '@ngx-formly/core';
15
15
  import * as i3 from 'primeng/inputtext';
16
16
  import { InputTextModule } from 'primeng/inputtext';
17
17
  import * as i4 from 'primeng/select';
@@ -28,300 +28,269 @@ import * as i3$3 from 'primeng/checkbox';
28
28
  import { CheckboxModule } from 'primeng/checkbox';
29
29
  import * as i3$4 from 'primeng/inputtextarea';
30
30
  import { Textarea } from 'primeng/inputtextarea';
31
- import * as i2$1 from 'primeng/tooltip';
31
+ import * as i2$2 from 'primeng/tooltip';
32
32
  import { TooltipModule } from 'primeng/tooltip';
33
33
 
34
+ class AppConstants {
35
+ static FORM = {
36
+ MODE: 'mode',
37
+ UUID: 'uuid',
38
+ NEW: 'new'
39
+ };
40
+ static PAGE_MODE = {
41
+ CREATE: 'create',
42
+ EDIT: 'edit',
43
+ VIEW: 'view'
44
+ };
45
+ }
46
+
47
+ const AppMessages = {
48
+ COMMON: {
49
+ ERROR: {
50
+ GENERAL: 'An error occurred',
51
+ INVALID_INPUT: 'Invalid input provided',
52
+ REQUIRED_FIELD: 'This field is required'
53
+ },
54
+ SUCCESS: {
55
+ SAVE: 'saved successfully',
56
+ UPDATE: 'updated successfully',
57
+ DELETE: 'deleted successfully'
58
+ }
59
+ },
60
+ FORM: {
61
+ ERROR: {
62
+ INIT: 'Failed to initialize form',
63
+ SAVE: 'Failed to save',
64
+ LOAD: 'Failed to load',
65
+ VALIDATION: 'Please fill in all required fields correctly'
66
+ },
67
+ SUCCESS: {
68
+ SAVE: 'saved successfully'
69
+ }
70
+ },
71
+ LIST: {
72
+ ERROR: {
73
+ LOAD: 'Failed to load',
74
+ DELETE: 'Failed to delete',
75
+ NAVIGATION: 'Navigation failed',
76
+ INVALID_ID: 'Cannot perform action without ID'
77
+ },
78
+ SUCCESS: {
79
+ DELETE: 'deleted successfully'
80
+ }
81
+ }
82
+ };
83
+
84
+ var ComponentContext;
85
+ (function (ComponentContext) {
86
+ ComponentContext["STANDALONE"] = "STANDALONE";
87
+ ComponentContext["TABBED"] = "TABBED";
88
+ ComponentContext["MODAL"] = "MODAL";
89
+ ComponentContext["EMBEDDED"] = "EMBEDDED";
90
+ })(ComponentContext || (ComponentContext = {}));
91
+
92
+ var TabbedFormType;
93
+ (function (TabbedFormType) {
94
+ TabbedFormType["STANDARD"] = "STANDARD";
95
+ TabbedFormType["LIST"] = "LIST";
96
+ })(TabbedFormType || (TabbedFormType = {}));
97
+
34
98
  /**
35
- * A utility service that provides common reusable functionality
36
- * such as UUID validation and error message display
99
+ * Standard task roles that define user permissions within a task workflow.
100
+ * These roles control what operations a user can perform on tasks.
37
101
  */
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
- }
102
+ var TaskRole;
103
+ (function (TaskRole) {
48
104
  /**
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
105
+ * Can create new tasks. This could be a user initiating a task manually
106
+ * or a system process creating tasks automatically.
52
107
  */
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
- }
108
+ TaskRole["TASK_CREATOR"] = "TASK_CREATOR";
63
109
  /**
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
110
+ * Can assign or reassign tasks to other users. Typically used by
111
+ * team leads or workflow managers to distribute work.
67
112
  */
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
- }
113
+ TaskRole["TASK_ASSIGNER"] = "TASK_ASSIGNER";
78
114
  /**
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
115
+ * Can pick tasks for self-assignment from a task pool.
116
+ * Useful in scenarios where tasks can be claimed by available workers.
83
117
  */
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
- }
118
+ TaskRole["TASK_PICKER"] = "TASK_PICKER";
94
119
  /**
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
120
+ * Can review and approve completed tasks. This role is responsible for
121
+ * quality assurance or approval steps in the workflow.
98
122
  */
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
- }
123
+ TaskRole["TASK_REVIEWER"] = "TASK_REVIEWER";
107
124
  /**
108
- * Converts a camelCase string to Title Case with spaces
109
- * @param camelCase The camelCase string to convert
110
- * @returns The Title Case string
125
+ * Can view task details but cannot modify them. Useful for stakeholders
126
+ * who need to monitor task progress without making changes.
111
127
  */
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
- }
128
+ TaskRole["TASK_VIEWER"] = "TASK_VIEWER";
118
129
  /**
119
- * Converts a string to kebab-case
120
- * @param input The string to convert
121
- * @returns The kebab-case string
130
+ * Has full administrative control over tasks. Can override or force
131
+ * state changes, bypass normal workflow restrictions, and manage
132
+ * task configuration.
122
133
  */
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();
134
+ TaskRole["TASK_ADMIN"] = "TASK_ADMIN";
135
+ })(TaskRole || (TaskRole = {}));
136
+
137
+ const KEYCLOAK_CONFIG = new InjectionToken('KEYCLOAK_CONFIG');
138
+
139
+ class KeycloakService {
140
+ config;
141
+ http;
142
+ keycloak;
143
+ isAuthenticated$ = new BehaviorSubject(false);
144
+ userProfile$ = new BehaviorSubject(null);
145
+ constructor(config, http) {
146
+ this.config = config;
147
+ this.http = http;
128
148
  }
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());
149
+ async initialize() {
150
+ try {
151
+ this.keycloak = new Keycloak(this.config);
152
+ const authenticated = await this.keycloak.init({
153
+ onLoad: 'check-sso',
154
+ silentCheckSsoRedirectUri: this.config.silentCheckSsoRedirectUri
155
+ });
156
+ this.isAuthenticated$.next(authenticated);
157
+ if (authenticated) {
158
+ await this.loadUserProfile();
146
159
  }
160
+ return authenticated;
161
+ }
162
+ catch (error) {
163
+ console.error('Failed to initialize Keycloak:', error);
164
+ return false;
147
165
  }
148
- return result.join('');
149
166
  }
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, '/');
167
+ login(options = {}) {
168
+ const loginOptions = {
169
+ ...this.config.loginOptions,
170
+ ...options
171
+ };
172
+ return this.keycloak.login(loginOptions);
173
+ }
174
+ logout() {
175
+ return this.keycloak.logout();
176
+ }
177
+ isLoggedIn() {
178
+ return this.isAuthenticated$.asObservable();
179
+ }
180
+ async getToken() {
181
+ try {
182
+ await this.updateToken(this.config.tokenValidityInSeconds || 60);
183
+ return this.keycloak.token;
164
184
  }
165
- // Clean the path part (after protocol) and reconstruct the URL
166
- const cleanPath = rest.join('://').replace(/\/+/g, '/');
167
- return `${protocol}://${cleanPath}`;
185
+ catch (error) {
186
+ console.error('Failed to get token:', error);
187
+ return null;
188
+ }
189
+ }
190
+ getRoles() {
191
+ if (!this.keycloak?.realmAccess?.roles) {
192
+ return [];
193
+ }
194
+ return this.keycloak.realmAccess.roles;
195
+ }
196
+ hasRole(role) {
197
+ return this.getRoles().includes(role);
198
+ }
199
+ getUserProfile() {
200
+ return this.userProfile$.asObservable();
201
+ }
202
+ updateToken(minValidity = 60) {
203
+ return new Promise((resolve, reject) => {
204
+ if (!this.keycloak?.token) {
205
+ resolve(false);
206
+ return;
207
+ }
208
+ this.keycloak.updateToken(minValidity)
209
+ .then((refreshed) => {
210
+ resolve(refreshed);
211
+ })
212
+ .catch((error) => {
213
+ console.error('Failed to update token:', error);
214
+ reject(error);
215
+ });
216
+ });
168
217
  }
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' });
218
+ async loadUserProfile() {
219
+ try {
220
+ const profile = await this.keycloak.loadUserProfile();
221
+ this.userProfile$.next(profile);
222
+ }
223
+ catch (error) {
224
+ console.error('Failed to load user profile:', error);
225
+ }
226
+ }
227
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: KeycloakService, deps: [{ token: KEYCLOAK_CONFIG }, { token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
228
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: KeycloakService, providedIn: 'root' });
171
229
  }
172
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: UtilsService, decorators: [{
230
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: KeycloakService, decorators: [{
173
231
  type: Injectable,
174
- args: [{
175
- providedIn: 'root'
176
- }]
177
- }] });
232
+ args: [{ providedIn: 'root' }]
233
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
234
+ type: Inject,
235
+ args: [KEYCLOAK_CONFIG]
236
+ }] }, { type: i1.HttpClient }] });
178
237
 
179
- class EnumRegistryService {
180
- utilsService;
181
- enumMap = new Map();
182
- constructor(utilsService) {
183
- this.utilsService = utilsService;
238
+ class KeycloakAuthGuard {
239
+ keycloakService;
240
+ router;
241
+ constructor(keycloakService, router) {
242
+ this.keycloakService = keycloakService;
243
+ this.router = router;
184
244
  }
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);
245
+ canActivate(route, state) {
246
+ return this.keycloakService.isLoggedIn().pipe(map(authenticated => {
247
+ // Handle authentication check
248
+ if (!authenticated) {
249
+ this.keycloakService.login({
250
+ redirectUri: window.location.origin + state.url
195
251
  });
196
- return service;
197
- },
198
- deps: [UtilsService]
199
- };
252
+ return false;
253
+ }
254
+ // Handle role-based access
255
+ const requiredRoles = route.data['roles'];
256
+ if (requiredRoles && !this.hasRequiredRoles(requiredRoles)) {
257
+ // Redirect to unauthorized page if roles don't match
258
+ return this.router.createUrlTree(['/unauthorized']);
259
+ }
260
+ return true;
261
+ }));
262
+ }
263
+ hasRequiredRoles(requiredRoles) {
264
+ const userRoles = this.keycloakService.getRoles();
265
+ return requiredRoles.every(role => userRoles.includes(role));
200
266
  }
201
267
  /**
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
268
+ * Checks if all required roles are present
269
+ * @param route Route being activated
270
+ * @returns true if no roles are required or if all required roles are present
205
271
  */
206
- register(name, enumType) {
207
- this.enumMap.set(name, enumType);
272
+ checkRoles(route) {
273
+ const requiredRoles = route.data['roles'];
274
+ if (!requiredRoles || requiredRoles.length === 0) {
275
+ return true;
276
+ }
277
+ return this.hasRequiredRoles(requiredRoles);
208
278
  }
209
279
  /**
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
280
+ * Gets redirect URL from route if present
281
+ * @param state Router state
282
+ * @returns URL to redirect to after login or undefined
213
283
  */
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
- });
284
+ getLoginRedirectUrl(state) {
285
+ return window.location.origin + state.url;
280
286
  }
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' });
287
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: KeycloakAuthGuard, deps: [{ token: KeycloakService }, { token: i1$1.Router }], target: i0.ɵɵFactoryTarget.Injectable });
288
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: KeycloakAuthGuard, providedIn: 'root' });
283
289
  }
284
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: FormlyConfigService, decorators: [{
290
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: KeycloakAuthGuard, decorators: [{
285
291
  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 }] });
292
+ args: [{ providedIn: 'root' }]
293
+ }], ctorParameters: () => [{ type: KeycloakService }, { type: i1$1.Router }] });
325
294
 
326
295
  /**
327
296
  * A general-purpose HTTP utility service that provides enhanced HTTP operations
@@ -601,1182 +570,563 @@ class HttpUtilityService {
601
570
  });
602
571
  }
603
572
  }
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' });
573
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: HttpUtilityService, deps: [{ token: i1.HttpClient }, { token: i2.MessageService }], target: i0.ɵɵFactoryTarget.Injectable });
574
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: HttpUtilityService, providedIn: 'root' });
636
575
  }
637
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ReferenceDataProviderService, decorators: [{
576
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: HttpUtilityService, decorators: [{
638
577
  type: Injectable,
639
578
  args: [{
640
579
  providedIn: 'root'
641
580
  }]
642
- }] });
581
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: i2.MessageService }] });
643
582
 
644
- class EntityRegistryService {
645
- httpUtility;
646
- dataProvider;
647
- constructor(httpUtility, dataProvider) {
648
- this.httpUtility = httpUtility;
649
- this.dataProvider = dataProvider;
583
+ /**
584
+ * A utility service that provides common reusable functionality
585
+ * such as UUID validation and error message display
586
+ */
587
+ class UtilsService {
588
+ /**
589
+ * Validates if a string is a valid UUID
590
+ * @param uuid The string to validate
591
+ * @returns boolean indicating if the string is a valid UUID
592
+ */
593
+ isValidUUID(uuid) {
594
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
595
+ return uuidRegex.test(uuid);
650
596
  }
651
597
  /**
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
598
+ * Shows an error message using PrimeNG's MessageService
599
+ * @param message The error message to display
600
+ * @param messageService The MessageService instance to use
655
601
  */
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();
602
+ showError(message, messageService) {
603
+ if (messageService) {
604
+ messageService.add({
605
+ severity: 'error',
606
+ summary: 'Error',
607
+ detail: message,
608
+ life: 3000
664
609
  });
665
610
  }
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
611
  }
695
- apiUrl;
696
612
  /**
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
613
+ * Shows a success message using PrimeNG's MessageService
614
+ * @param message The success message to display
615
+ * @param messageService The MessageService instance to use
703
616
  */
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);
617
+ showSuccess(message, messageService) {
618
+ if (messageService) {
619
+ messageService.add({
620
+ severity: 'success',
621
+ summary: 'Success',
622
+ detail: message,
623
+ life: 3000
624
+ });
625
+ }
711
626
  }
712
627
  /**
713
- * Joins URL parts ensuring no double slashes
628
+ * Gets the display value for an enum key or array of enum keys
629
+ * @param enumType The enum type
630
+ * @param key The enum key or array of enum keys
631
+ * @returns The display value for the enum key or comma-separated list of display values for array of keys
714
632
  */
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'));
633
+ getEnumValue(enumType, key) {
634
+ if (!key)
635
+ return '';
636
+ if (Array.isArray(key)) {
637
+ // Handle array of keys - map each to its display value and join with comma
638
+ return key.map(k => enumType[k] || k).join(', ');
730
639
  }
731
- return this.httpUtility.get(`${this.apiUrl}/${uuid}`);
640
+ // Handle single key
641
+ return enumType[key] || key;
732
642
  }
733
- create(entity) {
734
- return this.httpUtility.post(this.apiUrl, entity);
643
+ /**
644
+ * Gets all values from an enum type for use in formly select fields
645
+ * @param enumType The enum type to get values from
646
+ * @returns Array of {label, value} pairs for formly select options
647
+ */
648
+ getEnumValues(enumType) {
649
+ return Object.entries(enumType)
650
+ .filter(([key]) => isNaN(Number(key))) // Filter out reverse mappings
651
+ .map(([key, value]) => ({
652
+ value: key, // enum key as the value
653
+ label: value // enum value as the label
654
+ }));
735
655
  }
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);
656
+ /**
657
+ * Converts a camelCase string to Title Case with spaces
658
+ * @param camelCase The camelCase string to convert
659
+ * @returns The Title Case string
660
+ */
661
+ camelCaseToTitleCase(camelCase) {
662
+ if (!camelCase)
663
+ return '';
664
+ const spacedString = camelCase.replace(/([a-z])([A-Z])/g, '$1 $2');
665
+ return spacedString.charAt(0).toUpperCase() + spacedString.slice(1);
742
666
  }
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;
667
+ /**
668
+ * Converts a string to kebab-case
669
+ * @param input The string to convert
670
+ * @returns The kebab-case string
671
+ */
672
+ toKebabCase(input) {
673
+ return input
674
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
675
+ .replace(/([A-Z])([A-Z])(?=[a-z])/g, '$1-$2')
676
+ .toLowerCase();
777
677
  }
778
678
  /**
779
- * Display an error message to the user
780
- * @param detail The error message to display
679
+ * Converts a string to camelCase.
680
+ * Example: "loan-application" becomes "loanApplication"
681
+ * @param input The string to convert
682
+ * @returns The camelCase string
781
683
  */
782
- showError(detail) {
783
- this.notificationService.showError(detail);
684
+ toCamelCase(input) {
685
+ if (!input)
686
+ return '';
687
+ // Split by non-alphanumeric chars
688
+ const words = input.split(/[^a-zA-Z0-9]/);
689
+ const result = [words[0].toLowerCase()];
690
+ // Capitalize first letter of remaining words
691
+ for (let i = 1; i < words.length; i++) {
692
+ const word = words[i];
693
+ if (word) {
694
+ result.push(word.charAt(0).toUpperCase() + word.substring(1).toLowerCase());
695
+ }
696
+ }
697
+ return result.join('');
784
698
  }
785
699
  /**
786
- * Display a success message to the user
787
- * @param detail The success message to display
700
+ * Removes double forward slashes from an endpoint string while preserving the protocol
701
+ * e.g. "http://localhost:8080//api//v1//users" -> "http://localhost:8080/api/v1/users"
702
+ * @param endpoint The endpoint string to clean
703
+ * @returns The cleaned endpoint string without double slashes
788
704
  */
789
- showSuccess(detail) {
790
- this.notificationService.showSuccess(detail);
705
+ removeDoubleSlashes(endpoint) {
706
+ if (!endpoint)
707
+ return '';
708
+ // Split the URL into protocol and the rest to preserve protocol slashes
709
+ const [protocol, ...rest] = endpoint.split('://');
710
+ if (!rest.length) {
711
+ // If no protocol is present, just clean the entire string
712
+ return endpoint.replace(/\/+/g, '/');
713
+ }
714
+ // Clean the path part (after protocol) and reconstruct the URL
715
+ const cleanPath = rest.join('://').replace(/\/+/g, '/');
716
+ return `${protocol}://${cleanPath}`;
791
717
  }
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 });
718
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: UtilsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
719
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: UtilsService, providedIn: 'root' });
794
720
  }
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 }] });
721
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: UtilsService, decorators: [{
722
+ type: Injectable,
723
+ args: [{
724
+ providedIn: 'root'
725
+ }]
726
+ }] });
798
727
 
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
- }
728
+ class NotificationService {
729
+ messageService;
730
+ messageSubject = new BehaviorSubject(null);
731
+ message$ = this.messageSubject.asObservable();
732
+ constructor(messageService) {
733
+ this.messageService = messageService;
833
734
  }
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
- };
735
+ showSuccess(message) {
736
+ const msgObj = {
737
+ severity: 'success',
738
+ summary: 'Success',
739
+ detail: message,
740
+ life: 3000
741
+ };
742
+ this.messageSubject.next(msgObj);
743
+ }
744
+ showError(message) {
745
+ const msgObj = {
746
+ severity: 'error',
747
+ summary: 'Error',
748
+ detail: message,
749
+ life: 3000
750
+ };
751
+ this.messageSubject.next(msgObj);
752
+ }
753
+ clearMessage() {
754
+ this.messageSubject.next(null);
755
+ }
756
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: NotificationService, deps: [{ token: i2.MessageService }], target: i0.ɵɵFactoryTarget.Injectable });
757
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: NotificationService });
847
758
  }
759
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: NotificationService, decorators: [{
760
+ type: Injectable
761
+ }], ctorParameters: () => [{ type: i2.MessageService }] });
848
762
 
849
- class BaseFormComponent extends BasePageComponent {
850
- route;
851
- router;
852
- notificationService;
763
+ class BaseCrudService {
764
+ httpUtility;
853
765
  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;
766
+ resourcePath;
767
+ backendBaseUrl;
768
+ messageService;
769
+ // Use the loading state from HttpUtilityService
770
+ get loading$() {
771
+ return this.httpUtility.loading$;
772
+ }
773
+ apiUrl;
774
+ /**
775
+ * Creates an instance of BaseCrudService.
776
+ * @param httpUtility - Service for making HTTP requests
777
+ * @param utilsService - Utility service for common operations
778
+ * @param resourcePath - API resource path (e.g., 'customers', 'products')
779
+ * @param backendBaseUrl - Base URL for the backend API
780
+ * @param messageService - Optional service for showing messages
781
+ */
782
+ constructor(httpUtility, utilsService, resourcePath, backendBaseUrl, messageService) {
783
+ this.httpUtility = httpUtility;
868
784
  this.utilsService = utilsService;
869
- this.formlyConfigService = formlyConfigService;
785
+ this.resourcePath = resourcePath;
786
+ this.backendBaseUrl = backendBaseUrl;
787
+ this.messageService = messageService;
788
+ this.apiUrl = this.joinUrls(backendBaseUrl, resourcePath);
870
789
  }
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
- }
790
+ /**
791
+ * Joins URL parts ensuring no double slashes
792
+ */
793
+ joinUrls(...parts) {
794
+ return parts.map(part => part.replace(/^\/+|\/+$/g, '')).join('/');
892
795
  }
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']);
796
+ getAll(pageRequest) {
797
+ if (pageRequest) {
798
+ return this.httpUtility.post(`${this.apiUrl}/page`, pageRequest);
910
799
  }
911
800
  else {
912
- console.log('[BaseFormComponent] No form data, loading from UUID');
913
- this.loadFromUuid();
801
+ return this.httpUtility.get(this.apiUrl);
914
802
  }
915
803
  }
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);
804
+ getById(uuid) {
805
+ if (!this.utilsService.isValidUUID(uuid)) {
806
+ this.utilsService.showError('Invalid UUID format', this.messageService);
807
+ return throwError(() => new Error('Invalid UUID format'));
935
808
  }
936
- this.initializeFormData();
809
+ return this.httpUtility.get(`${this.apiUrl}/${uuid}`);
937
810
  }
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
- }
811
+ create(entity) {
812
+ return this.httpUtility.post(this.apiUrl, entity);
961
813
  }
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}`);
814
+ update(uuid, entity) {
815
+ if (!this.utilsService.isValidUUID(uuid)) {
816
+ this.utilsService.showError('Invalid UUID format', this.messageService);
817
+ return throwError(() => new Error('Invalid UUID format'));
971
818
  }
819
+ return this.httpUtility.put(`${this.apiUrl}/${uuid}`, entity);
972
820
  }
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();
821
+ delete(uuid) {
822
+ if (!this.utilsService.isValidUUID(uuid)) {
823
+ this.utilsService.showError('Invalid UUID format', this.messageService);
824
+ return throwError(() => new Error('Invalid UUID format'));
985
825
  }
826
+ return this.httpUtility.delete(`${this.apiUrl}/${uuid}`);
986
827
  }
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
- };
828
+ }
829
+
830
+ /**
831
+ * Base service for task-related operations that extends the BaseCrudService.
832
+ * Implements endpoints defined in Java BaseTaskController.
833
+ */
834
+ class BaseTaskService extends BaseCrudService {
835
+ httpUtility;
836
+ utilsService;
837
+ resourcePath;
838
+ backendBaseUrl;
839
+ messageService;
840
+ constructor(httpUtility, utilsService, resourcePath, backendBaseUrl, messageService) {
841
+ super(httpUtility, utilsService, resourcePath, backendBaseUrl, messageService);
842
+ this.httpUtility = httpUtility;
843
+ this.utilsService = utilsService;
844
+ this.resourcePath = resourcePath;
845
+ this.backendBaseUrl = backendBaseUrl;
846
+ this.messageService = messageService;
1000
847
  }
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');
848
+ /**
849
+ * Find tasks by department with pagination
850
+ * Maps to GET /department/{department} endpoint
851
+ */
852
+ findByDepartment(department, pageRequest) {
853
+ if (pageRequest) {
854
+ return this.httpUtility.post(`${this.apiUrl}/department/${department}/page`, pageRequest);
1033
855
  }
856
+ return this.httpUtility.get(`${this.apiUrl}/department/${department}`);
1034
857
  }
1035
- hasFormChanges(currentData) {
1036
- if (!this.originalFormData || this.isNew) {
1037
- return true;
858
+ /**
859
+ * Find tasks by assigned user with pagination
860
+ * Maps to GET /user/{userId} endpoint
861
+ */
862
+ findByAssignedUser(userId, pageRequest) {
863
+ if (pageRequest) {
864
+ return this.httpUtility.post(`${this.apiUrl}/user/${userId}/page`, pageRequest);
1038
865
  }
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 {};
866
+ return this.httpUtility.get(`${this.apiUrl}/user/${userId}`);
1102
867
  }
1103
868
  /**
1104
- * Checks if the component is running in a specific context
1105
- * @param contextType The context type to check against
869
+ * Send an event for a task
870
+ * Maps to PUT /{uuid}/event/{event} endpoint
871
+ * For ASSIGN events, userId and department are required
1106
872
  */
1107
- isInContext(contextType) {
1108
- return this.context === contextType;
873
+ sendEvent(uuid, event, userId, department) {
874
+ if (!this.utilsService.isValidUUID(uuid)) {
875
+ this.utilsService.showError('Invalid UUID format', this.messageService);
876
+ throw new Error('Invalid UUID format');
877
+ }
878
+ let params = new HttpParams();
879
+ if (userId) {
880
+ params = params.set('userId', userId);
881
+ }
882
+ if (department) {
883
+ params = params.set('department', department);
884
+ }
885
+ return this.httpUtility.put(`${this.apiUrl}/${uuid}/event/${event}`, null, { params });
1109
886
  }
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 });
887
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTaskService, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
888
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTaskService });
1113
889
  }
1114
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: BaseFormComponent, decorators: [{
890
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTaskService, decorators: [{
1115
891
  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
- }] } });
892
+ }], ctorParameters: () => [{ type: HttpUtilityService }, { type: UtilsService }, { type: undefined }, { type: undefined }, { type: undefined }] });
1124
893
 
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;
894
+ class EnumRegistryService {
1133
895
  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;
896
+ enumMap = new Map();
897
+ constructor(utilsService) {
1146
898
  this.utilsService = utilsService;
1147
- this.confirmationService = confirmationService;
1148
899
  }
1149
900
  /**
1150
- * Initialize the component, subscribe to loading state if available,
1151
- * and load initial data.
901
+ * Initialize global enums
1152
902
  */
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();
903
+ static registerGlobalEnums(enums) {
904
+ return {
905
+ provide: EnumRegistryService,
906
+ useFactory: (utilsService) => {
907
+ const service = new EnumRegistryService(utilsService);
908
+ Object.entries(enums).forEach(([name, enumType]) => {
909
+ service.register(name, enumType);
910
+ });
911
+ return service;
912
+ },
913
+ deps: [UtilsService]
914
+ };
1160
915
  }
1161
916
  /**
1162
- * Clean up any subscriptions when the component is destroyed
917
+ * Register an enum type with the service
918
+ * @param name Name to register the enum under
919
+ * @param enumType The enum type to register
1163
920
  */
1164
- ngOnDestroy() {
1165
- this.subscriptions.forEach(sub => sub.unsubscribe());
921
+ register(name, enumType) {
922
+ this.enumMap.set(name, enumType);
1166
923
  }
1167
924
  /**
1168
- * Handle search event from the UI
1169
- * @param event The search event
925
+ * Get formly-compatible options array for a registered enum
926
+ * @param name Name of the registered enum
927
+ * @returns Array of {label, value} pairs suitable for formly select fields
1170
928
  */
1171
- onSearch(event) {
1172
- const element = event.target;
1173
- if (this.table) {
1174
- this.table.filterGlobal(element.value, 'contains');
929
+ getEnumValues(name) {
930
+ const enumType = this.enumMap.get(name);
931
+ if (!enumType) {
932
+ console.warn(`Enum type "${name}" not found in registry`);
933
+ return [];
1175
934
  }
935
+ return this.utilsService.getEnumValues(enumType);
1176
936
  }
1177
937
  /**
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
938
+ * Check if an enum type is registered
939
+ * @param name Name of the enum to check
1190
940
  */
941
+ hasEnum(name) {
942
+ return this.enumMap.has(name);
943
+ }
1191
944
  /**
1192
- * Convert PrimeNG filters to backend format with proper operator mapping
945
+ * Clear all registered enums
1193
946
  */
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);
947
+ clear() {
948
+ this.enumMap.clear();
949
+ }
950
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: EnumRegistryService, deps: [{ token: UtilsService }], target: i0.ɵɵFactoryTarget.Injectable });
951
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: EnumRegistryService, providedIn: 'root' });
952
+ }
953
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: EnumRegistryService, decorators: [{
954
+ type: Injectable,
955
+ args: [{
956
+ providedIn: 'root'
957
+ }]
958
+ }], ctorParameters: () => [{ type: UtilsService }] });
959
+
960
+ class FormlyConfigService {
961
+ enumRegistry;
962
+ constructor(enumRegistry) {
963
+ this.enumRegistry = enumRegistry;
964
+ }
965
+ getFormConfig(fields, mode, dynamicOptions) {
966
+ const commonConfig = {
967
+ props: {
968
+ disabled: mode === 'view'
1211
969
  }
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
- });
970
+ };
971
+ return fields.map((field) => {
972
+ // Apply dynamic options from child component
973
+ if (dynamicOptions && field.key && typeof field.key === 'string') {
974
+ const fieldOptions = dynamicOptions[field.key];
975
+ if (fieldOptions) {
976
+ field.props = {
977
+ ...field.props,
978
+ ...fieldOptions
979
+ };
980
+ }
981
+ }
982
+ // Handle field groups recursively
983
+ if (field.fieldGroup) {
984
+ field.fieldGroup = this.getFormConfig(field.fieldGroup, mode, dynamicOptions);
1219
985
  }
986
+ return {
987
+ ...field,
988
+ ...commonConfig,
989
+ props: {
990
+ ...field.props,
991
+ ...commonConfig.props
992
+ }
993
+ };
1220
994
  });
1221
- console.log('Converted filters:', backendFilters);
1222
- return backendFilters;
1223
995
  }
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 '';
996
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: FormlyConfigService, deps: [{ token: EnumRegistryService }], target: i0.ɵɵFactoryTarget.Injectable });
997
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: FormlyConfigService, providedIn: 'root' });
998
+ }
999
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: FormlyConfigService, decorators: [{
1000
+ type: Injectable,
1001
+ args: [{
1002
+ providedIn: 'root'
1003
+ }]
1004
+ }], ctorParameters: () => [{ type: EnumRegistryService }] });
1005
+
1006
+ class FormService {
1007
+ entityService;
1008
+ formlyConfigService;
1009
+ notificationService;
1010
+ entityName;
1011
+ jsonFields;
1012
+ dropdownOptions;
1013
+ formState;
1014
+ constructor(entityService, formlyConfigService, notificationService, entityName, jsonFields, dropdownOptions = {}) {
1015
+ this.entityService = entityService;
1016
+ this.formlyConfigService = formlyConfigService;
1017
+ this.notificationService = notificationService;
1018
+ this.entityName = entityName;
1019
+ this.jsonFields = jsonFields;
1020
+ this.dropdownOptions = dropdownOptions;
1021
+ this.formState = {
1022
+ form: new FormGroup({}),
1023
+ fields: [],
1024
+ isNew: true,
1025
+ mode: AppConstants.PAGE_MODE.CREATE,
1026
+ currentData: {},
1027
+ originalData: null,
1028
+ hasChanges: false,
1029
+ loading: false,
1030
+ };
1031
+ this.initializeForm();
1032
+ }
1033
+ initializeForm() {
1034
+ this.formState.fields = this.getFormlyFieldConfig();
1035
+ if (this.formState.mode === AppConstants.PAGE_MODE.VIEW) {
1036
+ this.formState.form.disable();
1230
1037
  }
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
1038
  }
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'
1039
+ getFormlyFieldConfig() {
1040
+ return this.formlyConfigService.getFormConfig(this.jsonFields, this.formState.mode, this.dropdownOptions);
1041
+ }
1042
+ loadData(uuid) {
1043
+ if (!uuid || this.formState.mode === AppConstants.PAGE_MODE.CREATE) {
1044
+ return of(void 0);
1045
+ }
1046
+ this.formState.loading = true;
1047
+ return this.entityService.getById(uuid).pipe(take(1), map(data => {
1048
+ this.updateFormState(data);
1049
+ }), finalize$1(() => this.formState.loading = false));
1050
+ }
1051
+ updateFormState(data) {
1052
+ this.formState = {
1053
+ ...this.formState,
1054
+ currentData: { ...data },
1055
+ originalData: data,
1056
+ isNew: false,
1057
+ loading: false
1259
1058
  };
1260
- return operatorMap[operator] || 'EQUALS'; // Default to EQUALS if operator not found
1059
+ this.formState.form.patchValue(data, { emitEvent: false });
1261
1060
  }
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
- }
1061
+ submit() {
1062
+ if (!this.validateSubmission()) {
1063
+ return of(null);
1291
1064
  }
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
- // Navigate to the form route and pass the mode as a query parameter
1311
- this.navigateTo([this.getFormRoute()], { [AppConstants.FORM.MODE]: AppConstants.PAGE_MODE.CREATE });
1312
- }
1313
- /**
1314
- * Navigate to view an entity
1315
- * @param item The entity to view
1316
- */
1317
- onView(item) {
1318
- if (!this.validateItemUuid(item, 'view'))
1319
- return;
1320
- // Correct: Navigate to form route, pass mode and uuid as query params
1321
- this.navigateTo([this.getFormRoute()], {
1322
- [AppConstants.FORM.MODE]: AppConstants.PAGE_MODE.VIEW,
1323
- [AppConstants.FORM.UUID]: item.uuid
1324
- });
1325
- }
1326
- /**
1327
- * Navigate to edit an entity
1328
- * @param item The entity to edit
1329
- */
1330
- onEdit(item) {
1331
- if (!this.validateItemUuid(item, 'edit'))
1332
- return;
1333
- // Correct: Navigate to form route, pass mode and uuid as query params
1334
- this.navigateTo([this.getFormRoute()], {
1335
- [AppConstants.FORM.MODE]: AppConstants.PAGE_MODE.EDIT,
1336
- [AppConstants.FORM.UUID]: item.uuid
1337
- });
1338
- }
1339
- /**
1340
- * Navigate to a route with error handling
1341
- * @param path The route path
1342
- * @param matrixParams Optional matrix parameters
1343
- */
1344
- navigateTo(path, matrixParams) {
1345
- this.router.navigate(path, { queryParams: matrixParams })
1346
- .catch(error => {
1347
- console.error('Navigation error:', error);
1348
- this.showError(AppMessages.LIST.ERROR.NAVIGATION);
1349
- });
1350
- }
1351
- /**
1352
- * Delete an entity with confirmation
1353
- * @param item The entity to delete
1354
- */
1355
- onDelete(item) {
1356
- if (!this.validateItemUuid(item, 'delete'))
1357
- return;
1358
- this.confirmationService.confirm({
1359
- message: `Are you sure you want to delete this ${this.getEntityName()}?`,
1360
- header: 'Confirm Deletion',
1361
- icon: 'pi pi-exclamation-triangle',
1362
- accept: () => {
1363
- this.actionLoading = true;
1364
- this.getEntityService().delete(item.uuid)
1365
- .pipe(finalize$1(() => this.actionLoading = false))
1366
- .subscribe({
1367
- next: () => {
1368
- this.showSuccess(`${this.getEntityName()} ${AppMessages.LIST.SUCCESS.DELETE}`);
1369
- this.loadData();
1370
- },
1371
- error: (error) => {
1372
- console.error('Delete failed:', error);
1373
- this.showError(`${AppMessages.LIST.ERROR.DELETE} ${this.getEntityName()}`);
1374
- }
1375
- });
1376
- }
1377
- });
1065
+ this.formState.loading = true;
1066
+ const operation$ = this.getSubmitOperation();
1067
+ return operation$.pipe(finalize$1(() => this.formState.loading = false), map(result => {
1068
+ this.notificationService.showSuccess(`${this.entityName} ${AppMessages.FORM.SUCCESS.SAVE}`);
1069
+ return result;
1070
+ }));
1378
1071
  }
1379
- /**
1380
- * Validate that an entity has a UUID
1381
- * @param item The entity to validate
1382
- * @param action The action being performed
1383
- * @returns true if valid, false otherwise
1384
- */
1385
- validateItemUuid(item, action) {
1386
- if (!item?.uuid) {
1387
- this.showError(`${AppMessages.LIST.ERROR.INVALID_ID} ${action}`);
1072
+ validateSubmission() {
1073
+ if (!this.formState.form.valid) {
1074
+ this.notificationService.showError('Please check form errors');
1388
1075
  return false;
1389
1076
  }
1390
- return true;
1391
- }
1392
- /**
1393
- * Get an observable for loading state from the entity service
1394
- * @returns An observable of loading state if available, null otherwise
1395
- */
1396
- getLoadingObservable() {
1397
- const service = this.getEntityService();
1398
- return service.loading$ || null;
1399
- }
1400
- 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 });
1401
- 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 });
1402
- }
1403
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: BaseListPageComponent, decorators: [{
1404
- type: Directive
1405
- }], ctorParameters: () => [{ type: i1$2.Router }, { type: NotificationService }, { type: UtilsService }, { type: i1.ConfirmationService }], propDecorators: { table: [{
1406
- type: ViewChild,
1407
- args: ['dt']
1408
- }] } });
1409
-
1410
- class BaseTabbedFormComponent extends BasePageComponent {
1411
- route;
1412
- router;
1413
- notificationService;
1414
- utilsService;
1415
- // Tab specific properties
1416
- activeTab = '';
1417
- tabs = [];
1418
- formType = '';
1419
- // Form properties
1420
- isNew = true;
1421
- mode = AppConstants.PAGE_MODE.CREATE;
1422
- currentFormData;
1423
- originalFormData = null;
1424
- hasChanges = false;
1425
- // debug method removed
1426
- constructor(route, router, notificationService, utilsService) {
1427
- super(utilsService, notificationService);
1428
- this.route = route;
1429
- this.router = router;
1430
- this.notificationService = notificationService;
1431
- this.utilsService = utilsService;
1432
- }
1433
- ngOnInit() {
1434
- try {
1435
- // Initialize tabs
1436
- this.tabs = this.getTabs();
1437
- this.activeTab = this.tabs[0]?.value || '';
1438
- // Initialize form using query params
1439
- this.route.queryParams.pipe(take(1)).subscribe({
1440
- next: params => this.initializeForm(params),
1441
- error: error => {
1442
- console.error('Error initializing form:', error);
1443
- this.showError(AppMessages.FORM.ERROR.INIT);
1444
- }
1445
- });
1077
+ if (!this.formState.hasChanges && !this.formState.isNew) {
1078
+ this.notificationService.showSuccess('No changes to save');
1079
+ return false;
1446
1080
  }
1447
- catch (error) {
1448
- console.error('Error in ngOnInit:', error);
1449
- this.showError(AppMessages.FORM.ERROR.INIT);
1081
+ if (!this.formState.currentData || Object.keys(this.formState.currentData).length === 0) {
1082
+ this.notificationService.showError('No data to save');
1083
+ return false;
1450
1084
  }
1085
+ return true;
1451
1086
  }
1452
- initializeFormData() {
1453
- console.log('[BaseTabbedFormComponent] Initializing form data');
1454
- console.log('[BaseTabbedFormComponent] Current mode:', this.mode);
1455
- if (this.mode === AppConstants.PAGE_MODE.CREATE) {
1456
- console.log('[BaseTabbedFormComponent] Create mode, using defaults');
1457
- this.loadDefaultTab();
1458
- return;
1459
- }
1460
- const state = {
1461
- ...this.router.getCurrentNavigation()?.extras?.state,
1462
- ...window.history.state
1463
- };
1464
- console.log('[BaseTabbedFormComponent] Combined state:', state);
1465
- if (state?.formData) {
1466
- console.log('[BaseTabbedFormComponent] Found form data in state:', state.formData);
1467
- this.patchFormData(state.formData);
1468
- this.loadDefaultTab();
1087
+ getSubmitOperation() {
1088
+ const formData = this.formState.currentData;
1089
+ return this.formState.isNew
1090
+ ? this.entityService.create(formData)
1091
+ : this.entityService.update(this.formState.originalData.uuid, formData);
1092
+ }
1093
+ setMode(mode) {
1094
+ this.formState.mode = mode;
1095
+ if (mode === AppConstants.PAGE_MODE.VIEW) {
1096
+ this.formState.form.disable();
1469
1097
  }
1470
1098
  else {
1471
- console.log('[BaseTabbedFormComponent] No form data, loading from UUID');
1472
- this.loadFromUuid();
1099
+ this.formState.form.enable();
1473
1100
  }
1474
1101
  }
1475
- loadFromUuid() {
1476
- // Flatten nested subscriptions using RxJS operators
1477
- this.route.queryParams.pipe(take(1), map((params) => {
1478
- const uuid = params[AppConstants.FORM.UUID];
1479
- if (!uuid) {
1480
- throw new Error(`UUID is required for ${this.mode} mode when no formData is provided`);
1481
- }
1482
- return uuid;
1483
- })).subscribe({
1484
- next: (uuid) => this.loadEntity(uuid),
1485
- error: (error) => {
1486
- console.error('Error loading UUID:', error);
1487
- this.showError(`${AppMessages.FORM.ERROR.LOAD} UUID`);
1488
- }
1489
- });
1102
+ getState() {
1103
+ return this.formState;
1490
1104
  }
1491
- initializeForm(params) {
1492
- const routeMode = params[AppConstants.FORM.MODE];
1493
- try {
1494
- this.initializeFormMode(routeMode);
1495
- this.setupFormState();
1496
- if (this.formType === TabbedFormType.LIST) {
1497
- this.initializeListForm(params);
1498
- }
1499
- else {
1500
- // Existing logic for STANDARD form type
1501
- this.initializeFormData();
1502
- }
1503
- }
1504
- catch (error) {
1505
- console.error('Error initializing form:', error);
1506
- this.showError(`${AppMessages.FORM.ERROR.INIT}: ${error instanceof Error ? error.message : 'Unknown error'}`);
1507
- }
1105
+ patchValue(value) {
1106
+ this.formState.form.patchValue(value);
1107
+ this.updateCurrentData();
1108
+ this.checkFormChanges();
1508
1109
  }
1509
- patchFormData(data) {
1510
- if (data) {
1511
- this.currentFormData = { ...data };
1512
- this.originalFormData = { ...data }; // Create a deep copy to avoid reference issues
1513
- this.isNew = false;
1514
- }
1110
+ updateCurrentData() {
1111
+ this.formState.currentData = {
1112
+ ...this.formState.form.value,
1113
+ uuid: this.formState.originalData?.uuid
1114
+ };
1515
1115
  }
1516
- initializeFormMode(routeMode) {
1517
- if (!routeMode) {
1518
- this.mode = AppConstants.PAGE_MODE.CREATE;
1519
- return;
1520
- }
1521
- // Validate mode more explicitly
1522
- if (!this.isValidPageMode(routeMode)) {
1523
- const validModes = Object.values(AppConstants.PAGE_MODE).join(', ');
1524
- throw new Error(`Invalid page mode: ${routeMode}. Valid modes are: ${validModes}`);
1525
- }
1526
- this.mode = routeMode;
1116
+ checkFormChanges() {
1117
+ this.formState.hasChanges = this.hasFormChanges(this.formState.currentData);
1527
1118
  }
1528
- setupFormState() {
1529
- console.log('[BaseTabbedFormComponent] Setting up form state, mode:', this.mode);
1530
- if (this.mode === AppConstants.PAGE_MODE.CREATE) {
1531
- console.log('[BaseTabbedFormComponent] Create mode setup');
1532
- this.isNew = true;
1533
- this.loading = false;
1534
- this.originalFormData = null;
1119
+ hasFormChanges(currentData) {
1120
+ if (!this.formState.originalData || this.formState.isNew) {
1121
+ return true;
1535
1122
  }
1536
- }
1537
- loadEntity(uuid) {
1538
- this.loading = true;
1539
- this.isNew = false;
1540
- const entityName = this.getEntityName();
1541
- this.getEntityService().getById(uuid).subscribe({
1542
- next: (data) => {
1543
- this.originalFormData = data;
1544
- this.currentFormData = { ...data };
1545
- this.loading = false;
1546
- console.log('[BaseTabbedFormComponent] Original data loaded:', this.originalFormData);
1547
- console.log('[BaseTabbedFormComponent] Form data updated:', this.currentFormData);
1548
- this.loadDefaultTab();
1549
- },
1550
- error: (error) => {
1551
- console.log('[BaseTabbedFormComponent] Error loading entity:', error);
1552
- this.showError(`${AppMessages.FORM.ERROR.LOAD} ${entityName}`);
1553
- this.loading = false;
1554
- }
1123
+ return Object.keys(currentData).some(key => {
1124
+ const currentValue = currentData[key];
1125
+ const originalValue = this.formState.originalData[key];
1126
+ return currentValue !== originalValue;
1555
1127
  });
1556
1128
  }
1557
- findTabByValue(value) {
1558
- return this.tabs.find(tab => tab.value === value);
1559
- }
1560
- loadDefaultTab() {
1561
- console.log('[BaseTabbedFormComponent] Loading default tab');
1562
- const defaultTab = this.tabs.find(tab => tab.isParentModel) || this.tabs[0];
1563
- if (!defaultTab)
1564
- return;
1565
- if (this.formType === TabbedFormType.LIST) {
1566
- const state = this.router.getCurrentNavigation()?.extras?.state || window.history.state;
1567
- const formDataList = state?.formDataList || [];
1568
- const selectedData = formDataList.find((data) => data.uuid === defaultTab.value);
1569
- if (selectedData) {
1570
- this.currentFormData = selectedData;
1571
- this.originalFormData = { ...selectedData };
1572
- this.patchFormData(selectedData);
1573
- }
1574
- this.router.navigate([defaultTab.value], {
1575
- relativeTo: this.route,
1576
- state: {
1577
- formData: selectedData,
1578
- formDataList: formDataList,
1579
- context: ComponentContext.TABBED
1580
- }
1581
- });
1582
- }
1583
- else if (this.formType === TabbedFormType.STANDARD) {
1584
- // For STANDARD type, just navigate to the tab route with the parent form data
1585
- this.router.navigate([defaultTab.value], {
1586
- relativeTo: this.route,
1587
- queryParamsHandling: 'preserve', // Keep existing query params (mode & uuid)
1588
- state: {
1589
- formData: defaultTab.isParentModel ? this.currentFormData : null,
1590
- parentFormData: this.currentFormData, // Pass the parent form data for child forms
1591
- context: ComponentContext.TABBED
1592
- }
1593
- });
1594
- }
1595
- else {
1596
- // Default behavior for other form types
1597
- this.router.navigate([defaultTab.value], {
1598
- relativeTo: this.route,
1599
- queryParamsHandling: 'preserve', // Keep existing query params (mode & uuid)
1600
- state: {
1601
- formData: this.currentFormData,
1602
- context: ComponentContext.TABBED
1603
- }
1604
- });
1605
- }
1606
- }
1607
- getTabFormData(tabConfig) {
1608
- if (!tabConfig)
1609
- return null;
1610
- if (tabConfig.isParentModel) {
1611
- return this.currentFormData;
1612
- }
1613
- const fieldName = this.utilsService.toCamelCase(tabConfig.value);
1614
- // Use type assertion since we know this is a valid field
1615
- return this.currentFormData[fieldName];
1616
- }
1617
- initializeListForm(params) {
1618
- const state = this.router.getCurrentNavigation()?.extras?.state || window.history.state;
1619
- const formDataList = state?.formDataList;
1620
- if (!formDataList?.length) {
1621
- throw new Error('formDataList is required in state for LIST type');
1622
- }
1623
- this.tabs = this.createTabsFromFormDataList(formDataList);
1624
- // Select the first tab by default
1625
- this.activeTab = this.tabs[0].value;
1626
- // Set current form data from the first item in the list
1627
- this.currentFormData = formDataList[0];
1628
- this.originalFormData = formDataList[0];
1629
- // Navigate to the first tab
1630
- this.router.navigate([this.activeTab], {
1631
- relativeTo: this.route,
1632
- state: {
1633
- formData: this.currentFormData,
1634
- formDataList: formDataList,
1635
- context: ComponentContext.TABBED
1636
- }
1637
- });
1638
- // Ensure the form data is updated after navigation
1639
- this.patchFormData(this.currentFormData);
1640
- }
1641
- createTabsFromFormDataList(formDataList) {
1642
- return formDataList.map((data) => ({
1643
- value: data.uuid,
1644
- title: this.getTabTitle(data),
1645
- isParentModel: true
1646
- }));
1647
- }
1648
- onTabChange(value) {
1649
- const tabConfig = this.findTabByValue(value);
1650
- if (!tabConfig) {
1651
- console.warn(`Tab not found: ${value}`);
1652
- return;
1653
- }
1654
- this.activeTab = tabConfig.value;
1655
- if (this.formType === TabbedFormType.LIST) {
1656
- const state = this.router.getCurrentNavigation()?.extras?.state || window.history.state;
1657
- const formDataList = state?.formDataList || [];
1658
- const selectedData = formDataList.find((data) => data.uuid === value);
1659
- if (selectedData) {
1660
- // Update current form data
1661
- this.currentFormData = selectedData;
1662
- this.originalFormData = { ...selectedData };
1663
- this.patchFormData(selectedData);
1664
- this.router.navigate([value], {
1665
- relativeTo: this.route,
1666
- state: {
1667
- formData: selectedData,
1668
- formDataList: formDataList,
1669
- context: ComponentContext.TABBED
1670
- }
1671
- });
1672
- }
1673
- }
1674
- else if (this.formType === TabbedFormType.STANDARD) {
1675
- // For STANDARD type, navigate to the tab route with the parent form data
1676
- this.router.navigate([this.activeTab], {
1677
- relativeTo: this.route,
1678
- queryParamsHandling: 'preserve',
1679
- state: {
1680
- formData: tabConfig.isParentModel ? this.currentFormData : null,
1681
- parentFormData: this.currentFormData,
1682
- context: ComponentContext.TABBED
1683
- }
1684
- });
1685
- }
1686
- else {
1687
- // Default behavior for other form types
1688
- const formData = this.getTabFormData(tabConfig);
1689
- this.router.navigate([this.activeTab], {
1690
- relativeTo: this.route,
1691
- queryParamsHandling: 'preserve',
1692
- state: {
1693
- formData,
1694
- context: ComponentContext.TABBED
1695
- }
1696
- });
1697
- }
1698
- }
1699
- hasFormChanges(currentData) {
1700
- // For new forms or no original data, consider it changed
1701
- if (this.isNew || !this.originalFormData) {
1702
- return true;
1703
- }
1704
- // Deep comparison of objects
1705
- return !this.isEqual(currentData, this.originalFormData);
1706
- }
1707
- isEqual(obj1, obj2) {
1708
- if (obj1 === obj2)
1709
- return true;
1710
- if (typeof obj1 !== 'object' || obj1 === null ||
1711
- typeof obj2 !== 'object' || obj2 === null)
1712
- return false;
1713
- const keys1 = Object.keys(obj1);
1714
- const keys2 = Object.keys(obj2);
1715
- if (keys1.length !== keys2.length)
1716
- return false;
1717
- return keys1.every(key => Object.prototype.hasOwnProperty.call(obj2, key) &&
1718
- this.isEqual(obj1[key], obj2[key]));
1719
- }
1720
- isValidPageMode(mode) {
1721
- return Object.values(AppConstants.PAGE_MODE).includes(mode);
1722
- }
1723
- 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 });
1724
- 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 });
1725
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: BaseTabbedFormComponent });
1726
- }
1727
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: BaseTabbedFormComponent, decorators: [{
1728
- type: Injectable
1729
- }, {
1730
- type: Component,
1731
- args: [{
1732
- template: ''
1733
- }]
1734
- }], ctorParameters: () => [{ type: i1$2.ActivatedRoute }, { type: i1$2.Router }, { type: NotificationService }, { type: UtilsService }] });
1735
-
1736
- class IndianDatePipe {
1737
- datePipe;
1738
- constructor(datePipe) {
1739
- this.datePipe = datePipe;
1740
- }
1741
- transform(value, format = 'default') {
1742
- if (!value)
1743
- return null;
1744
- // First validate dd-MM-yyyy format
1745
- const dateRegex = /^(0[1-9]|[12][0-9]|3[01])-(0[1-9]|1[0-2])-\d{4}$/;
1746
- if (!dateRegex.test(value)) {
1747
- throw new Error(`Invalid date format: ${value}. Expected format: dd-MM-yyyy`);
1748
- }
1749
- // Validate real date
1750
- const [day, month, year] = value.split('-').map(Number);
1751
- const date = new Date(year, month - 1, day);
1752
- if (date.getFullYear() !== year ||
1753
- date.getMonth() !== month - 1 ||
1754
- date.getDate() !== day) {
1755
- throw new Error(`Invalid date: ${value}`);
1756
- }
1757
- // Apply requested format
1758
- switch (format) {
1759
- case 'short':
1760
- // Pad with leading zeros for consistent format
1761
- const paddedDay = day.toString().padStart(2, '0');
1762
- const paddedMonth = month.toString().padStart(2, '0');
1763
- return `${paddedDay}-${paddedMonth}-${year}`; // 10-03-2025
1764
- case 'medium':
1765
- return this.datePipe.transform(date, 'dd MMM yyyy'); // 10 Jan 2019
1766
- default:
1767
- return value; // original dd-MM-yyyy
1768
- }
1769
- }
1770
- 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 });
1771
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: IndianDatePipe, isStandalone: true, name: "indianDate" });
1772
1129
  }
1773
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: IndianDatePipe, decorators: [{
1774
- type: Pipe,
1775
- args: [{
1776
- name: 'indianDate',
1777
- standalone: true
1778
- }]
1779
- }], ctorParameters: () => [{ type: i1$3.DatePipe }] });
1780
1130
 
1781
1131
  class TextInputType extends FieldType {
1782
1132
  defaultOptions = {
@@ -1785,8 +1135,8 @@ class TextInputType extends FieldType {
1785
1135
  placeholder: ''
1786
1136
  }
1787
1137
  };
1788
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextInputType, deps: null, target: i0.ɵɵFactoryTarget.Component });
1789
- 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: `
1138
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TextInputType, deps: null, target: i0.ɵɵFactoryTarget.Component });
1139
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: TextInputType, isStandalone: true, selector: "formly-field-text-input", usesInheritance: true, ngImport: i0, template: `
1790
1140
  <input pInputText
1791
1141
  [type]="props['type'] || 'text'"
1792
1142
  [formControl]="$any(formControl)"
@@ -1794,9 +1144,9 @@ class TextInputType extends FieldType {
1794
1144
  [placeholder]="props['placeholder'] || ''"
1795
1145
  [attr.aria-label]="props['label'] || props['placeholder'] || ''"
1796
1146
  class="w-full"/>
1797
- `, 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"] }] });
1147
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2$1.ɵFormlyAttributes, selector: "[formlyAttributes]", inputs: ["formlyAttributes", "id"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }] });
1798
1148
  }
1799
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextInputType, decorators: [{
1149
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TextInputType, decorators: [{
1800
1150
  type: Component,
1801
1151
  args: [{
1802
1152
  selector: 'formly-field-text-input',
@@ -1835,8 +1185,8 @@ class SelectType extends FieldType {
1835
1185
  virtualScroll: false
1836
1186
  }
1837
1187
  };
1838
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SelectType, deps: null, target: i0.ɵɵFactoryTarget.Component });
1839
- 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: `
1188
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: SelectType, deps: null, target: i0.ɵɵFactoryTarget.Component });
1189
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: SelectType, isStandalone: true, selector: "formly-field-select", usesInheritance: true, ngImport: i0, template: `
1840
1190
  <ng-container *ngIf="options$ | async as options">
1841
1191
  <p-select
1842
1192
  [formControl]="$any(formControl)"
@@ -1848,9 +1198,9 @@ class SelectType extends FieldType {
1848
1198
  styleClass="w-full">
1849
1199
  </p-select>
1850
1200
  </ng-container>
1851
- `, 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"] }] });
1201
+ `, 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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2$1.ɵ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"] }] });
1852
1202
  }
1853
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SelectType, decorators: [{
1203
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: SelectType, decorators: [{
1854
1204
  type: Component,
1855
1205
  args: [{
1856
1206
  selector: 'formly-field-select',
@@ -1882,8 +1232,8 @@ class RadioType extends FieldType {
1882
1232
  const options = this.props['options'];
1883
1233
  return isObservable(options) ? options : of(options || []);
1884
1234
  }
1885
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: RadioType, deps: null, target: i0.ɵɵFactoryTarget.Component });
1886
- 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: `
1235
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: RadioType, deps: null, target: i0.ɵɵFactoryTarget.Component });
1236
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: RadioType, isStandalone: true, selector: "formly-field-radio", usesInheritance: true, ngImport: i0, template: `
1887
1237
  <div class="flex flex-column gap-1">
1888
1238
  <ng-container *ngIf="options$ | async as options">
1889
1239
  <div *ngFor="let option of options; let i = index" class="flex align-items-center gap-2">
@@ -1901,9 +1251,9 @@ class RadioType extends FieldType {
1901
1251
  </div>
1902
1252
  </ng-container>
1903
1253
  </div>
1904
- `, 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"] }] });
1254
+ `, 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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2$1.ɵ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"] }] });
1905
1255
  }
1906
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: RadioType, decorators: [{
1256
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: RadioType, decorators: [{
1907
1257
  type: Component,
1908
1258
  args: [{
1909
1259
  selector: 'formly-field-radio',
@@ -1948,8 +1298,8 @@ class NumericInputType extends FieldType {
1948
1298
  useGrouping: true
1949
1299
  }
1950
1300
  };
1951
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: NumericInputType, deps: null, target: i0.ɵɵFactoryTarget.Component });
1952
- 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: `
1301
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: NumericInputType, deps: null, target: i0.ɵɵFactoryTarget.Component });
1302
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: NumericInputType, isStandalone: true, selector: "formly-field-numeric-input", usesInheritance: true, ngImport: i0, template: `
1953
1303
  <p-inputNumber
1954
1304
  [formControl]="$any(formControl)"
1955
1305
  [formlyAttributes]="field"
@@ -1964,9 +1314,9 @@ class NumericInputType extends FieldType {
1964
1314
  [showButtons]="true"
1965
1315
  class="w-full">
1966
1316
  </p-inputNumber>
1967
- `, 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"] }] });
1317
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2$1.ɵ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"] }] });
1968
1318
  }
1969
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: NumericInputType, decorators: [{
1319
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: NumericInputType, decorators: [{
1970
1320
  type: Component,
1971
1321
  args: [{
1972
1322
  selector: 'formly-field-numeric-input',
@@ -2013,8 +1363,8 @@ class MultiselectType extends FieldType {
2013
1363
  virtualScroll: false
2014
1364
  }
2015
1365
  };
2016
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiselectType, deps: null, target: i0.ɵɵFactoryTarget.Component });
2017
- 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: `
1366
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: MultiselectType, deps: null, target: i0.ɵɵFactoryTarget.Component });
1367
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: MultiselectType, isStandalone: true, selector: "formly-field-multiselect", usesInheritance: true, ngImport: i0, template: `
2018
1368
  <ng-container *ngIf="options$ | async as options">
2019
1369
  <p-multiSelect
2020
1370
  [formControl]="$any(formControl)"
@@ -2032,9 +1382,9 @@ class MultiselectType extends FieldType {
2032
1382
  styleClass="w-full">
2033
1383
  </p-multiSelect>
2034
1384
  </ng-container>
2035
- `, 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"] }] });
1385
+ `, 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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2$1.ɵ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"] }] });
2036
1386
  }
2037
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiselectType, decorators: [{
1387
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: MultiselectType, decorators: [{
2038
1388
  type: Component,
2039
1389
  args: [{
2040
1390
  selector: 'formly-field-multiselect',
@@ -2114,8 +1464,8 @@ class DateType extends FieldType {
2114
1464
  : /^\d{2}\/\d{2}\/\d{4}$/;
2115
1465
  return dateRegex.test(value);
2116
1466
  }
2117
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DateType, deps: null, target: i0.ɵɵFactoryTarget.Component });
2118
- 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: `
1467
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: DateType, deps: null, target: i0.ɵɵFactoryTarget.Component });
1468
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: DateType, isStandalone: true, selector: "formly-field-date", usesInheritance: true, ngImport: i0, template: `
2119
1469
  <p-datepicker
2120
1470
  [formControl]="$any(formControl)"
2121
1471
  [formlyAttributes]="field"
@@ -2133,9 +1483,9 @@ class DateType extends FieldType {
2133
1483
  (onSelect)="onDateSelect($event)"
2134
1484
  (onInput)="onDateInput($event)">
2135
1485
  </p-datepicker>
2136
- `, 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"] }] });
1486
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2$1.ɵ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"] }] });
2137
1487
  }
2138
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DateType, decorators: [{
1488
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: DateType, decorators: [{
2139
1489
  type: Component,
2140
1490
  args: [{
2141
1491
  selector: 'formly-field-date',
@@ -2175,8 +1525,8 @@ class CheckboxType extends FieldType {
2175
1525
  falseValue: false
2176
1526
  }
2177
1527
  };
2178
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CheckboxType, deps: null, target: i0.ɵɵFactoryTarget.Component });
2179
- 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: `
1528
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: CheckboxType, deps: null, target: i0.ɵɵFactoryTarget.Component });
1529
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: CheckboxType, isStandalone: true, selector: "formly-field-checkbox", usesInheritance: true, ngImport: i0, template: `
2180
1530
  <div class="checkbox-container">
2181
1531
  <p-checkbox
2182
1532
  [formControl]="$any(formControl)"
@@ -2187,9 +1537,9 @@ class CheckboxType extends FieldType {
2187
1537
  {{props['checkboxLabel'] || ''}}
2188
1538
  </p-checkbox>
2189
1539
  </div>
2190
- `, 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"] }] });
1540
+ `, 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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2$1.ɵ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"] }] });
2191
1541
  }
2192
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CheckboxType, decorators: [{
1542
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: CheckboxType, decorators: [{
2193
1543
  type: Component,
2194
1544
  args: [{ selector: 'formly-field-checkbox', standalone: true, imports: [
2195
1545
  CommonModule,
@@ -2219,8 +1569,8 @@ class TextareaType extends FieldType {
2219
1569
  placeholder: ''
2220
1570
  }
2221
1571
  };
2222
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextareaType, deps: null, target: i0.ɵɵFactoryTarget.Component });
2223
- 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: `
1572
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TextareaType, deps: null, target: i0.ɵɵFactoryTarget.Component });
1573
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: TextareaType, isStandalone: true, selector: "formly-field-textarea", usesInheritance: true, ngImport: i0, template: `
2224
1574
  <textarea pInputTextarea
2225
1575
  [formControl]="$any(formControl)"
2226
1576
  [formlyAttributes]="field"
@@ -2230,9 +1580,9 @@ class TextareaType extends FieldType {
2230
1580
  [placeholder]="props['placeholder'] || ''"
2231
1581
  class="w-full">
2232
1582
  </textarea>
2233
- `, 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"] }] });
1583
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "directive", type: i2$1.ɵFormlyAttributes, selector: "[formlyAttributes]", inputs: ["formlyAttributes", "id"] }, { kind: "ngmodule", type: Textarea }, { kind: "directive", type: i3$4.InputTextarea, selector: "[pInputTextarea]", inputs: ["autoResize", "variant", "fluid"], outputs: ["onResize"] }] });
2234
1584
  }
2235
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextareaType, decorators: [{
1585
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TextareaType, decorators: [{
2236
1586
  type: Component,
2237
1587
  args: [{
2238
1588
  selector: 'formly-field-textarea',
@@ -2272,8 +1622,8 @@ class FormFieldWrapper extends FieldWrapper {
2272
1622
  const { touched, dirty, invalid } = this.field.formControl;
2273
1623
  return invalid && (touched || dirty);
2274
1624
  }
2275
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: FormFieldWrapper, deps: null, target: i0.ɵɵFactoryTarget.Component });
2276
- 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: `
1625
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: FormFieldWrapper, deps: null, target: i0.ɵɵFactoryTarget.Component });
1626
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: FormFieldWrapper, isStandalone: true, selector: "formly-wrapper-form-field", usesInheritance: true, ngImport: i0, template: `
2277
1627
  <div class="field mb-4">
2278
1628
  <label *ngIf="props.label" [for]="id" class="font-medium">
2279
1629
  {{ props.label }}
@@ -2297,9 +1647,9 @@ class FormFieldWrapper extends FieldWrapper {
2297
1647
  {{ errorMessage }}
2298
1648
  </small>
2299
1649
  </div>
2300
- `, 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"] }] });
1650
+ `, 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$2.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }] });
2301
1651
  }
2302
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: FormFieldWrapper, decorators: [{
1652
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: FormFieldWrapper, decorators: [{
2303
1653
  type: Component,
2304
1654
  args: [{ selector: 'formly-wrapper-form-field', standalone: true, imports: [CommonModule, TooltipModule], template: `
2305
1655
  <div class="field mb-4">
@@ -2343,9 +1693,9 @@ const FORMLY_WRAPPERS = {
2343
1693
  };
2344
1694
 
2345
1695
  class CoreFormlyModule {
2346
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CoreFormlyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2347
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: CoreFormlyModule, imports: [i2.FormlyModule], exports: [FormlyModule] });
2348
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CoreFormlyModule, imports: [FormlyModule.forRoot({
1696
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: CoreFormlyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1697
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.6", ngImport: i0, type: CoreFormlyModule, imports: [i2$1.FormlyModule], exports: [FormlyModule] });
1698
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: CoreFormlyModule, imports: [FormlyModule.forRoot({
2349
1699
  types: [
2350
1700
  { name: FORMLY_TYPES.TEXT, component: TextInputType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
2351
1701
  { name: FORMLY_TYPES.NUMBER, component: NumericInputType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
@@ -2361,7 +1711,7 @@ class CoreFormlyModule {
2361
1711
  ]
2362
1712
  }), FormlyModule] });
2363
1713
  }
2364
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CoreFormlyModule, decorators: [{
1714
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: CoreFormlyModule, decorators: [{
2365
1715
  type: NgModule,
2366
1716
  args: [{
2367
1717
  imports: [
@@ -2385,12 +1735,1259 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
2385
1735
  }]
2386
1736
  }] });
2387
1737
 
2388
- // Config
2389
- // Services
1738
+ /**
1739
+ * Base component for all page components in the application.
1740
+ * Provides common functionality like loading state and message display.
1741
+ */
1742
+ class BasePageComponent {
1743
+ utilsService;
1744
+ notificationService;
1745
+ loading = false;
1746
+ constructor(utilsService, notificationService) {
1747
+ this.utilsService = utilsService;
1748
+ this.notificationService = notificationService;
1749
+ }
1750
+ /**
1751
+ * Display an error message to the user
1752
+ * @param detail The error message to display
1753
+ */
1754
+ showError(detail) {
1755
+ this.notificationService.showError(detail);
1756
+ }
1757
+ /**
1758
+ * Display a success message to the user
1759
+ * @param detail The success message to display
1760
+ */
1761
+ showSuccess(detail) {
1762
+ this.notificationService.showSuccess(detail);
1763
+ }
1764
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BasePageComponent, deps: [{ token: UtilsService }, { token: NotificationService }], target: i0.ɵɵFactoryTarget.Directive });
1765
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.6", type: BasePageComponent, isStandalone: true, ngImport: i0 });
1766
+ }
1767
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BasePageComponent, decorators: [{
1768
+ type: Directive
1769
+ }], ctorParameters: () => [{ type: UtilsService }, { type: NotificationService }] });
1770
+
1771
+ class BaseFormComponent extends BasePageComponent {
1772
+ route;
1773
+ router;
1774
+ notificationService;
1775
+ utilsService;
1776
+ formlyConfigService;
1777
+ destroyed$ = new Subject();
1778
+ state = {
1779
+ form: new FormGroup({}),
1780
+ fields: [],
1781
+ isNew: true,
1782
+ mode: AppConstants.PAGE_MODE.CREATE,
1783
+ currentData: {},
1784
+ originalData: null,
1785
+ hasChanges: false,
1786
+ loading: false,
1787
+ context: ComponentContext.STANDALONE
1788
+ };
1789
+ constructor(route, router, notificationService, utilsService, formlyConfigService) {
1790
+ super(utilsService, notificationService);
1791
+ this.route = route;
1792
+ this.router = router;
1793
+ this.notificationService = notificationService;
1794
+ this.utilsService = utilsService;
1795
+ this.formlyConfigService = formlyConfigService;
1796
+ }
1797
+ ngOnInit() {
1798
+ this.initializeForm()
1799
+ .pipe(switchMap(() => this.loadFormData()), takeUntil(this.destroyed$))
1800
+ .subscribe({
1801
+ next: () => this.onFormReady(),
1802
+ error: error => this.handleInitError(error)
1803
+ });
1804
+ }
1805
+ initializeForm() {
1806
+ return this.route.queryParams.pipe(take(1), map(params => {
1807
+ this.initializeFormMode(params[AppConstants.FORM.MODE]);
1808
+ this.setupFormState();
1809
+ this.initializeFormSubscriptions();
1810
+ }));
1811
+ }
1812
+ loadFormData() {
1813
+ return this.route.queryParams.pipe(take(1), mergeMap(params => {
1814
+ const state = this.getRouterState();
1815
+ if (this.state.mode === AppConstants.PAGE_MODE.CREATE) {
1816
+ return of(void 0);
1817
+ }
1818
+ if (state?.formData) {
1819
+ this.updateFormState(state.formData);
1820
+ return of(void 0);
1821
+ }
1822
+ const uuid = params[AppConstants.FORM.UUID];
1823
+ if (!uuid) {
1824
+ throw new Error(`UUID is required for ${this.state.mode} mode`);
1825
+ }
1826
+ return this.getEntityOperations().getById(uuid)
1827
+ .pipe(tap(data => this.updateFormState(data)), map(() => void 0));
1828
+ }));
1829
+ }
1830
+ getRouterState() {
1831
+ return {
1832
+ ...this.router.getCurrentNavigation()?.extras?.state,
1833
+ ...window.history.state,
1834
+ ...this.route.snapshot.data
1835
+ };
1836
+ }
1837
+ initializeFormSubscriptions() {
1838
+ this.state.form.valueChanges
1839
+ .pipe(debounceTime(300), distinctUntilChanged(), takeUntil(this.destroyed$))
1840
+ .subscribe(() => {
1841
+ this.updateCurrentData();
1842
+ this.checkFormChanges();
1843
+ });
1844
+ // Check for context in router state
1845
+ const routerState = window.history.state;
1846
+ if (routerState?.context) {
1847
+ this.state.context = routerState.context;
1848
+ console.log('[BaseFormComponent] Context set from router state:', this.state.context);
1849
+ }
1850
+ }
1851
+ set formData(data) {
1852
+ console.log('[BaseFormComponent] Form data input changed:', data);
1853
+ if (data) {
1854
+ // Reset form with new data
1855
+ this.state.fields = this.getFormlyFieldConfig();
1856
+ this.state.form.reset();
1857
+ this.state.currentData = { ...data };
1858
+ this.state.originalData = data;
1859
+ this.state.isNew = false;
1860
+ // Update form values after reset
1861
+ this.state.form.patchValue(data);
1862
+ console.log('[BaseFormComponent] Form state updated:', {
1863
+ fields: this.state.fields,
1864
+ formValue: this.state.form.value,
1865
+ currentData: this.state.currentData
1866
+ });
1867
+ }
1868
+ }
1869
+ updateCurrentData() {
1870
+ this.state.currentData = {
1871
+ ...this.state.form.value,
1872
+ uuid: this.state.originalData?.uuid
1873
+ };
1874
+ }
1875
+ checkFormChanges() {
1876
+ this.state.hasChanges = this.hasFormChanges(this.state.currentData);
1877
+ }
1878
+ updateFormState(data) {
1879
+ this.state = {
1880
+ ...this.state,
1881
+ currentData: { ...data },
1882
+ originalData: data,
1883
+ isNew: false,
1884
+ loading: false
1885
+ };
1886
+ this.state.form.patchValue(data, { emitEvent: false });
1887
+ }
1888
+ handleInitError(error) {
1889
+ console.error('Form initialization error:', error);
1890
+ this.notificationService.showError(AppMessages.FORM.ERROR.INIT);
1891
+ this.state.loading = false;
1892
+ }
1893
+ handleLoadError(error) {
1894
+ const entityName = this.getEntityName();
1895
+ console.error(`Error loading ${entityName}:`, error);
1896
+ this.notificationService.showError(`${AppMessages.FORM.ERROR.LOAD} ${entityName}`);
1897
+ this.state.loading = false;
1898
+ }
1899
+ patchFormData(data) {
1900
+ this.formData = data;
1901
+ }
1902
+ initializeFormMode(routeMode) {
1903
+ if (!routeMode) {
1904
+ this.state.mode = AppConstants.PAGE_MODE.CREATE;
1905
+ }
1906
+ else if (this.isValidPageMode(routeMode)) {
1907
+ this.state.mode = routeMode;
1908
+ }
1909
+ else {
1910
+ throw new Error(`Invalid page mode: ${routeMode}`);
1911
+ }
1912
+ }
1913
+ setupFormState() {
1914
+ this.state.fields = this.getFormlyFieldConfig();
1915
+ if (this.state.mode === AppConstants.PAGE_MODE.CREATE) {
1916
+ this.state.isNew = true;
1917
+ this.state.loading = false;
1918
+ this.state.originalData = null;
1919
+ }
1920
+ if (this.state.mode === AppConstants.PAGE_MODE.VIEW) {
1921
+ this.state.form.disable();
1922
+ }
1923
+ }
1924
+ onFormReady() {
1925
+ console.log('[BaseFormComponent] Form initialized successfully');
1926
+ }
1927
+ getFormlyFieldConfig() {
1928
+ return this.formlyConfigService.getFormConfig(this.getJsonFields(), this.state.mode, this.getDropdownOptions());
1929
+ }
1930
+ onSubmitRoute() {
1931
+ return [`/${this.getEntityName().toLowerCase()}s`];
1932
+ }
1933
+ getEntityOperations() {
1934
+ const entityService = this.getEntityService();
1935
+ return {
1936
+ create: (data) => entityService.create(data),
1937
+ update: (uuid, data) => entityService.update(uuid, { ...data, uuid }),
1938
+ getById: (uuid) => entityService.getById(uuid)
1939
+ };
1940
+ }
1941
+ onSubmit() {
1942
+ if (!this.validateSubmission()) {
1943
+ return;
1944
+ }
1945
+ this.state.loading = true;
1946
+ const operation$ = this.getSubmitOperation();
1947
+ operation$
1948
+ .pipe(finalize$1(() => this.state.loading = false), takeUntil(this.destroyed$))
1949
+ .subscribe({
1950
+ next: () => this.handleSubmitSuccess(),
1951
+ error: error => this.handleSubmitError(error)
1952
+ });
1953
+ }
1954
+ validateSubmission() {
1955
+ if (!this.state.form.valid) {
1956
+ this.notificationService.showError('Please check form errors');
1957
+ return false;
1958
+ }
1959
+ if (!this.state.hasChanges && !this.state.isNew) {
1960
+ this.notificationService.showSuccess('No changes to save');
1961
+ return false;
1962
+ }
1963
+ if (!this.state.currentData || Object.keys(this.state.currentData).length === 0) {
1964
+ this.notificationService.showError('No data to save');
1965
+ return false;
1966
+ }
1967
+ return true;
1968
+ }
1969
+ getSubmitOperation() {
1970
+ const formData = this.state.currentData;
1971
+ const operations = this.getEntityOperations();
1972
+ return this.state.isNew
1973
+ ? operations.create(formData)
1974
+ : operations.update(this.state.originalData.uuid, formData);
1975
+ }
1976
+ handleSubmitSuccess() {
1977
+ const entityName = this.getEntityName();
1978
+ this.notificationService.showSuccess(`${entityName} ${AppMessages.FORM.SUCCESS.SAVE}`);
1979
+ this.router.navigate(this.onSubmitRoute());
1980
+ }
1981
+ handleSubmitError(error) {
1982
+ const entityName = this.getEntityName();
1983
+ const errorMessage = error.error?.message || `${AppMessages.FORM.ERROR.SAVE} ${entityName}`;
1984
+ this.showError(errorMessage);
1985
+ }
1986
+ hasFormChanges(currentData) {
1987
+ if (!this.state.originalData || this.state.isNew) {
1988
+ return true;
1989
+ }
1990
+ console.log('[BaseFormComponent] Checking for changes:', {
1991
+ currentData,
1992
+ originalData: this.state.originalData
1993
+ });
1994
+ const hasChanges = Object.keys(currentData).some(key => {
1995
+ const currentValue = currentData[key];
1996
+ const originalValue = this.state.originalData[key];
1997
+ const changed = currentValue !== originalValue;
1998
+ if (changed) {
1999
+ console.log(`[BaseFormComponent] Field '${key}' changed:`, {
2000
+ from: originalValue,
2001
+ to: currentValue
2002
+ });
2003
+ }
2004
+ return changed;
2005
+ });
2006
+ console.log('[BaseFormComponent] Changes detected:', hasChanges);
2007
+ return hasChanges;
2008
+ }
2009
+ ngOnDestroy() {
2010
+ this.destroyed$.next();
2011
+ this.destroyed$.complete();
2012
+ }
2013
+ isValidPageMode(mode) {
2014
+ return Object.values(AppConstants.PAGE_MODE).includes(mode);
2015
+ }
2016
+ getDropdownOptions() {
2017
+ return {};
2018
+ }
2019
+ /**
2020
+ * Checks if the component is running in a specific context
2021
+ * @param contextType The context type to check against
2022
+ */
2023
+ isInContext(contextType) {
2024
+ return this.state.context === contextType;
2025
+ }
2026
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseFormComponent, deps: [{ token: i1$1.ActivatedRoute }, { token: i1$1.Router }, { token: NotificationService }, { token: UtilsService }, { token: FormlyConfigService }], target: i0.ɵɵFactoryTarget.Component });
2027
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: BaseFormComponent, isStandalone: true, selector: "ng-component", inputs: { formData: "formData" }, usesInheritance: true, ngImport: i0, template: '', isInline: true });
2028
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseFormComponent });
2029
+ }
2030
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseFormComponent, decorators: [{
2031
+ type: Injectable
2032
+ }, {
2033
+ type: Component,
2034
+ args: [{
2035
+ template: ''
2036
+ }]
2037
+ }], ctorParameters: () => [{ type: i1$1.ActivatedRoute }, { type: i1$1.Router }, { type: NotificationService }, { type: UtilsService }, { type: FormlyConfigService }], propDecorators: { formData: [{
2038
+ type: Input
2039
+ }] } });
2040
+
2041
+ /**
2042
+ * Base component for all table components in the application.
2043
+ * Provides common functionality for table operations like loading data,
2044
+ * pagination, sorting, and filtering.
2045
+ */
2046
+ class BaseTableComponent extends BasePageComponent {
2047
+ utilsService;
2048
+ notificationService;
2049
+ table;
2050
+ items = [];
2051
+ rows = 10;
2052
+ totalRecords = 0;
2053
+ filters = [];
2054
+ subscriptions = [];
2055
+ constructor(utilsService, notificationService) {
2056
+ super(utilsService, notificationService);
2057
+ this.utilsService = utilsService;
2058
+ this.notificationService = notificationService;
2059
+ }
2060
+ /**
2061
+ * Initialize the component and load initial data
2062
+ */
2063
+ ngOnInit() {
2064
+ // Subscribe to loading state if available
2065
+ const loadingObservable = this.getLoadingObservable();
2066
+ if (loadingObservable) {
2067
+ this.subscriptions.push(loadingObservable.subscribe(isLoading => this.loading = isLoading));
2068
+ }
2069
+ this.loadData();
2070
+ }
2071
+ /**
2072
+ * Clean up any subscriptions when the component is destroyed
2073
+ */
2074
+ ngOnDestroy() {
2075
+ this.subscriptions.forEach(sub => sub.unsubscribe());
2076
+ }
2077
+ /**
2078
+ * Handle search event from the UI
2079
+ * @param event The search event
2080
+ */
2081
+ onSearch(event) {
2082
+ const element = event.target;
2083
+ if (this.table) {
2084
+ this.table.filterGlobal(element.value, 'contains');
2085
+ }
2086
+ }
2087
+ /**
2088
+ * Load data with pagination and sorting
2089
+ * @param event The lazy load event from PrimeNG table
2090
+ */
2091
+ loadData(event) {
2092
+ console.log('LoadData called with event:', event);
2093
+ this.loading = true;
2094
+ // Create page request with default values
2095
+ const pageRequest = {
2096
+ page: 0,
2097
+ size: 10,
2098
+ sorts: [],
2099
+ filters: []
2100
+ };
2101
+ if (event) {
2102
+ // Handle pagination
2103
+ if (event.first !== undefined && event.rows) {
2104
+ pageRequest.page = Math.floor(event.first / event.rows);
2105
+ pageRequest.size = event.rows;
2106
+ }
2107
+ // Handle sorting
2108
+ if (event.sortField) {
2109
+ pageRequest.sorts = [{
2110
+ field: Array.isArray(event.sortField) ? event.sortField[0] : event.sortField,
2111
+ direction: event.sortOrder === 1 ? 'asc' : 'desc'
2112
+ }];
2113
+ }
2114
+ // Handle filtering
2115
+ if (event.filters) {
2116
+ const convertedFilters = this.convertFilters(event.filters);
2117
+ console.log('Converted filters:', convertedFilters);
2118
+ pageRequest.filters = convertedFilters;
2119
+ }
2120
+ }
2121
+ console.log('Sending page request:', pageRequest);
2122
+ this.getEntityService().getAll(pageRequest)
2123
+ .pipe(finalize$1(() => this.loading = false))
2124
+ .subscribe({
2125
+ next: (response) => {
2126
+ this.items = [...response.content];
2127
+ this.totalRecords = response.totalElements;
2128
+ this.rows = response.pageable.pageSize;
2129
+ },
2130
+ error: (error) => {
2131
+ console.error('Failed to load data:', error);
2132
+ this.showError(`Failed to load data`);
2133
+ }
2134
+ });
2135
+ }
2136
+ /**
2137
+ * Convert PrimeNG filters to backend format with proper operator mapping
2138
+ */
2139
+ convertFilters(filters) {
2140
+ const backendFilters = [];
2141
+ if (!filters)
2142
+ return backendFilters;
2143
+ console.log('Raw PrimeNG filters:', filters);
2144
+ Object.entries(filters).forEach(([key, filterMeta]) => {
2145
+ console.log(`Processing filter for ${key}:`, filterMeta);
2146
+ // Handle array of filter metadata (multiple filters for same column)
2147
+ if (Array.isArray(filterMeta)) {
2148
+ const validFilters = filterMeta
2149
+ .filter(meta => meta.value !== null && meta.value !== undefined && meta.value !== '')
2150
+ .map(meta => ({
2151
+ field: key,
2152
+ value: this.transformFilterValue(String(meta.value)),
2153
+ operator: this.transformOperator(meta.matchMode)
2154
+ }));
2155
+ backendFilters.push(...validFilters);
2156
+ }
2157
+ // Handle single filter metadata
2158
+ else if (filterMeta?.value !== null && filterMeta?.value !== undefined && filterMeta?.value !== '') {
2159
+ backendFilters.push({
2160
+ field: key,
2161
+ value: this.transformFilterValue(String(filterMeta.value)),
2162
+ operator: this.transformOperator(filterMeta.matchMode)
2163
+ });
2164
+ }
2165
+ });
2166
+ console.log('Converted filters:', backendFilters);
2167
+ return backendFilters;
2168
+ }
2169
+ /**
2170
+ * Transform filter value based on backend requirements - all values must be strings
2171
+ */
2172
+ transformFilterValue(value) {
2173
+ if (value === null || value === undefined) {
2174
+ return '';
2175
+ }
2176
+ if (typeof value === 'string') {
2177
+ return value.trim();
2178
+ }
2179
+ if (Array.isArray(value)) {
2180
+ return value.join(',');
2181
+ }
2182
+ if (value instanceof Date) {
2183
+ return value.toISOString();
2184
+ }
2185
+ if (typeof value === 'number' || typeof value === 'boolean') {
2186
+ return value.toString();
2187
+ }
2188
+ return String(value);
2189
+ }
2190
+ /**
2191
+ * Map PrimeNG operators to backend operators
2192
+ */
2193
+ transformOperator(operator) {
2194
+ const operatorMap = {
2195
+ 'contains': 'CONTAINS',
2196
+ 'startsWith': 'STARTS_WITH',
2197
+ 'endsWith': 'ENDS_WITH',
2198
+ 'equals': 'EQUALS',
2199
+ 'notEquals': 'NOT_EQUALS',
2200
+ 'gt': 'GREATER_THAN',
2201
+ 'gte': 'GREATER_THAN_EQUALS',
2202
+ 'lt': 'LESS_THAN',
2203
+ 'lte': 'LESS_THAN_EQUALS'
2204
+ };
2205
+ return operatorMap[operator] || 'EQUALS'; // Default to EQUALS if operator not found
2206
+ }
2207
+ /**
2208
+ * Get an observable for loading state from the entity service
2209
+ * @returns An observable of loading state if available, null otherwise
2210
+ */
2211
+ getLoadingObservable() {
2212
+ const service = this.getEntityService();
2213
+ return service.loading$ || null;
2214
+ }
2215
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTableComponent, deps: [{ token: UtilsService }, { token: NotificationService }], target: i0.ɵɵFactoryTarget.Directive });
2216
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.6", type: BaseTableComponent, isStandalone: true, viewQueries: [{ propertyName: "table", first: true, predicate: ["dt"], descendants: true }], usesInheritance: true, ngImport: i0 });
2217
+ }
2218
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTableComponent, decorators: [{
2219
+ type: Directive
2220
+ }], ctorParameters: () => [{ type: UtilsService }, { type: NotificationService }], propDecorators: { table: [{
2221
+ type: ViewChild,
2222
+ args: ['dt']
2223
+ }] } });
2224
+
2225
+ /**
2226
+ * Base component for all list page components in the application.
2227
+ * Provides common functionality for list operations like CRUD operations
2228
+ * while inheriting table functionality from BaseTableComponent.
2229
+ */
2230
+ class BaseListPageComponent extends BaseTableComponent {
2231
+ router;
2232
+ notificationService;
2233
+ utilsService;
2234
+ confirmationService;
2235
+ actionLoading = false;
2236
+ constructor(router, notificationService, utilsService, confirmationService) {
2237
+ super(utilsService, notificationService);
2238
+ this.router = router;
2239
+ this.notificationService = notificationService;
2240
+ this.utilsService = utilsService;
2241
+ this.confirmationService = confirmationService;
2242
+ }
2243
+ /**
2244
+ * Navigate to create a new entity
2245
+ */
2246
+ onCreate() {
2247
+ // Navigate to the form route and pass the mode as a query parameter
2248
+ this.navigateTo([this.getFormRoute()], { [AppConstants.FORM.MODE]: AppConstants.PAGE_MODE.CREATE });
2249
+ }
2250
+ /**
2251
+ * Navigate to view an entity
2252
+ * @param item The entity to view
2253
+ */
2254
+ onView(item) {
2255
+ if (!this.validateItemUuid(item, 'view'))
2256
+ return;
2257
+ // Navigate to form route, pass mode and uuid as query params
2258
+ this.navigateTo([this.getFormRoute()], {
2259
+ [AppConstants.FORM.MODE]: AppConstants.PAGE_MODE.VIEW,
2260
+ [AppConstants.FORM.UUID]: item.uuid
2261
+ });
2262
+ }
2263
+ /**
2264
+ * Navigate to edit an entity
2265
+ * @param item The entity to edit
2266
+ */
2267
+ onEdit(item) {
2268
+ if (!this.validateItemUuid(item, 'edit'))
2269
+ return;
2270
+ // Navigate to form route, pass mode and uuid as query params
2271
+ this.navigateTo([this.getFormRoute()], {
2272
+ [AppConstants.FORM.MODE]: AppConstants.PAGE_MODE.EDIT,
2273
+ [AppConstants.FORM.UUID]: item.uuid
2274
+ });
2275
+ }
2276
+ /**
2277
+ * Delete an entity with confirmation
2278
+ * @param item The entity to delete
2279
+ */
2280
+ onDelete(item) {
2281
+ if (!this.validateItemUuid(item, 'delete'))
2282
+ return;
2283
+ this.confirmationService.confirm({
2284
+ message: `Are you sure you want to delete this ${this.getEntityName()}?`,
2285
+ header: 'Confirm Deletion',
2286
+ icon: 'pi pi-exclamation-triangle',
2287
+ accept: () => {
2288
+ this.actionLoading = true;
2289
+ this.getEntityService().delete(item.uuid)
2290
+ .pipe(finalize$1(() => this.actionLoading = false))
2291
+ .subscribe({
2292
+ next: () => {
2293
+ this.showSuccess(`${this.getEntityName()} ${AppMessages.LIST.SUCCESS.DELETE}`);
2294
+ this.loadData();
2295
+ },
2296
+ error: (error) => {
2297
+ console.error('Delete failed:', error);
2298
+ this.showError(`${AppMessages.LIST.ERROR.DELETE} ${this.getEntityName()}`);
2299
+ }
2300
+ });
2301
+ }
2302
+ });
2303
+ }
2304
+ /**
2305
+ * Navigate to a route with error handling
2306
+ * @param path The route path
2307
+ * @param matrixParams Optional matrix parameters
2308
+ */
2309
+ navigateTo(path, matrixParams) {
2310
+ this.router.navigate(path, { queryParams: matrixParams })
2311
+ .catch(error => {
2312
+ console.error('Navigation error:', error);
2313
+ this.showError(AppMessages.LIST.ERROR.NAVIGATION);
2314
+ });
2315
+ }
2316
+ /**
2317
+ * Validate that an entity has a UUID
2318
+ * @param item The entity to validate
2319
+ * @param action The action being performed
2320
+ * @returns true if valid, false otherwise
2321
+ */
2322
+ validateItemUuid(item, action) {
2323
+ if (!item?.uuid) {
2324
+ this.showError(`${AppMessages.LIST.ERROR.INVALID_ID} ${action}`);
2325
+ return false;
2326
+ }
2327
+ return true;
2328
+ }
2329
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseListPageComponent, deps: [{ token: i1$1.Router }, { token: NotificationService }, { token: UtilsService }, { token: i2.ConfirmationService }], target: i0.ɵɵFactoryTarget.Directive });
2330
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.6", type: BaseListPageComponent, isStandalone: true, usesInheritance: true, ngImport: i0 });
2331
+ }
2332
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseListPageComponent, decorators: [{
2333
+ type: Directive
2334
+ }], ctorParameters: () => [{ type: i1$1.Router }, { type: NotificationService }, { type: UtilsService }, { type: i2.ConfirmationService }] });
2335
+
2336
+ class BaseTabbedFormComponent extends BasePageComponent {
2337
+ route;
2338
+ router;
2339
+ notificationService;
2340
+ utilsService;
2341
+ destroyed$ = new Subject();
2342
+ stateManager$ = new BehaviorSubject(this.getInitialState());
2343
+ getInitialState() {
2344
+ return {
2345
+ isNew: true,
2346
+ mode: AppConstants.PAGE_MODE.CREATE,
2347
+ currentData: {},
2348
+ originalData: null,
2349
+ hasChanges: false,
2350
+ loading: false,
2351
+ context: ComponentContext.TABBED,
2352
+ activeTab: '',
2353
+ tabs: []
2354
+ };
2355
+ }
2356
+ get state() {
2357
+ return this.stateManager$.value;
2358
+ }
2359
+ set state(value) {
2360
+ // Only emit if there are actual changes
2361
+ if (this.hasStateChanged(this.state, value)) {
2362
+ this.stateManager$.next(value);
2363
+ }
2364
+ }
2365
+ hasStateChanged(current, next) {
2366
+ // Quick reference check
2367
+ if (current === next)
2368
+ return false;
2369
+ // Check essential properties that affect rendering
2370
+ return current.loading !== next.loading ||
2371
+ current.activeTab !== next.activeTab ||
2372
+ current.hasChanges !== next.hasChanges ||
2373
+ current.mode !== next.mode ||
2374
+ current.isNew !== next.isNew ||
2375
+ !this.areTabsEqual(current.tabs, next.tabs) ||
2376
+ this.detectChanges(current.currentData, next.currentData);
2377
+ }
2378
+ areTabsEqual(current, next) {
2379
+ if (current === next)
2380
+ return true;
2381
+ if (current.length !== next.length)
2382
+ return false;
2383
+ return current.every((tab, index) => tab.value === next[index].value &&
2384
+ tab.isParentModel === next[index].isParentModel);
2385
+ }
2386
+ constructor(route, router, notificationService, utilsService) {
2387
+ super(utilsService, notificationService);
2388
+ this.route = route;
2389
+ this.router = router;
2390
+ this.notificationService = notificationService;
2391
+ this.utilsService = utilsService;
2392
+ }
2393
+ ngOnInit() {
2394
+ this.initializeFormWithErrorHandling();
2395
+ this.setupStateSubscription();
2396
+ }
2397
+ initializeFormWithErrorHandling() {
2398
+ // Initialize tabs first
2399
+ const tabs = this.getTabs();
2400
+ this.state = {
2401
+ ...this.state,
2402
+ tabs,
2403
+ activeTab: tabs[0]?.value || ''
2404
+ };
2405
+ // Then initialize form
2406
+ this.route.queryParams.pipe(take$1(1), tap$1(() => this.state = { ...this.state, loading: true }), tap$1(params => this.initializeForm(params)), map$1(() => void 0), catchError$1(error => {
2407
+ console.error('Error initializing form:', error);
2408
+ this.showError(AppMessages.FORM.ERROR.INIT);
2409
+ return of(void 0);
2410
+ }), finalize(() => this.state = { ...this.state, loading: false }), takeUntil$1(this.destroyed$)).subscribe();
2411
+ }
2412
+ setupStateSubscription() {
2413
+ this.stateManager$.pipe(distinctUntilChanged((prev, curr) => !this.hasStateChanged(prev, curr)), takeUntil$1(this.destroyed$)).subscribe(state => {
2414
+ this.handleStateChange(state);
2415
+ });
2416
+ }
2417
+ handleStateChange(state) {
2418
+ // Track significant state changes
2419
+ const stateUpdates = [];
2420
+ if (state.loading) {
2421
+ stateUpdates.push('loading');
2422
+ }
2423
+ if (state.hasChanges) {
2424
+ stateUpdates.push('hasChanges');
2425
+ }
2426
+ if (state.activeTab) {
2427
+ stateUpdates.push('activeTab');
2428
+ }
2429
+ if (stateUpdates.length > 0) {
2430
+ console.log('[BaseTabbedFormComponent] State updated:', {
2431
+ updates: stateUpdates,
2432
+ activeTab: state.activeTab,
2433
+ loading: state.loading,
2434
+ hasChanges: state.hasChanges
2435
+ });
2436
+ }
2437
+ }
2438
+ initializeFormData() {
2439
+ console.log('[BaseTabbedFormComponent] Initializing form data');
2440
+ console.log('[BaseTabbedFormComponent] Current mode:', this.state.mode);
2441
+ if (this.state.mode === AppConstants.PAGE_MODE.CREATE) {
2442
+ console.log('[BaseTabbedFormComponent] Create mode, using defaults');
2443
+ this.loadDefaultTab();
2444
+ return;
2445
+ }
2446
+ const state = {
2447
+ ...this.router.getCurrentNavigation()?.extras?.state,
2448
+ ...window.history.state
2449
+ };
2450
+ console.log('[BaseTabbedFormComponent] Combined state:', state);
2451
+ if (state?.formData) {
2452
+ console.log('[BaseTabbedFormComponent] Found form data in state:', state.formData);
2453
+ this.patchFormData(state.formData);
2454
+ this.loadDefaultTab();
2455
+ }
2456
+ else {
2457
+ console.log('[BaseTabbedFormComponent] No form data, loading from UUID');
2458
+ this.loadFromUuid();
2459
+ }
2460
+ }
2461
+ loadFromUuid() {
2462
+ this.route.queryParams.pipe(take$1(1), map$1((params) => {
2463
+ const uuid = params[AppConstants.FORM.UUID];
2464
+ if (!uuid) {
2465
+ throw new Error(`UUID is required for ${this.state.mode} mode when no formData is provided`);
2466
+ }
2467
+ return uuid;
2468
+ }), tap$1(() => this.state = { ...this.state, loading: true }), tap$1(uuid => this.loadEntity(uuid)), catchError$1(error => {
2469
+ console.error('Error loading UUID:', error);
2470
+ this.showError(`${AppMessages.FORM.ERROR.LOAD} UUID`);
2471
+ return of(void 0);
2472
+ }), finalize(() => this.state = { ...this.state, loading: false }), takeUntil$1(this.destroyed$)).subscribe();
2473
+ }
2474
+ initializeForm(params) {
2475
+ const routeMode = params[AppConstants.FORM.MODE];
2476
+ try {
2477
+ this.initializeFormMode(routeMode);
2478
+ this.setupFormState();
2479
+ this.initializeFormData();
2480
+ }
2481
+ catch (error) {
2482
+ console.error('Error initializing form:', error);
2483
+ this.showError(`${AppMessages.FORM.ERROR.INIT}: ${error instanceof Error ? error.message : 'Unknown error'}`);
2484
+ }
2485
+ }
2486
+ patchFormData(data) {
2487
+ if (data) {
2488
+ this.state.currentData = { ...data };
2489
+ this.state.originalData = { ...data }; // Create a deep copy to avoid reference issues
2490
+ this.state.isNew = false;
2491
+ }
2492
+ }
2493
+ initializeFormMode(routeMode) {
2494
+ if (!routeMode) {
2495
+ this.state.mode = AppConstants.PAGE_MODE.CREATE;
2496
+ return;
2497
+ }
2498
+ // Validate mode more explicitly
2499
+ if (!this.isValidPageMode(routeMode)) {
2500
+ const validModes = Object.values(AppConstants.PAGE_MODE).join(', ');
2501
+ throw new Error(`Invalid page mode: ${routeMode}. Valid modes are: ${validModes}`);
2502
+ }
2503
+ this.state.mode = routeMode;
2504
+ }
2505
+ setupFormState() {
2506
+ console.log('[BaseTabbedFormComponent] Setting up form state, mode:', this.state.mode);
2507
+ if (this.state.mode === AppConstants.PAGE_MODE.CREATE) {
2508
+ console.log('[BaseTabbedFormComponent] Create mode setup');
2509
+ this.state.isNew = true;
2510
+ this.state.loading = false;
2511
+ this.state.originalData = null;
2512
+ }
2513
+ }
2514
+ loadEntity(uuid) {
2515
+ const entityName = this.getEntityName();
2516
+ this.getEntityService().getById(uuid).pipe(tap$1(() => this.state = {
2517
+ ...this.state,
2518
+ loading: true,
2519
+ isNew: false
2520
+ }), takeUntil$1(this.destroyed$)).subscribe({
2521
+ next: (data) => {
2522
+ this.state = {
2523
+ ...this.state,
2524
+ originalData: data,
2525
+ currentData: { ...data },
2526
+ loading: false
2527
+ };
2528
+ console.log('[BaseTabbedFormComponent] Data loaded:', { original: data, current: this.state.currentData });
2529
+ this.loadDefaultTab();
2530
+ },
2531
+ error: (error) => {
2532
+ console.error('[BaseTabbedFormComponent] Error loading entity:', error);
2533
+ this.showError(`${AppMessages.FORM.ERROR.LOAD} ${entityName}`);
2534
+ this.state = { ...this.state, loading: false };
2535
+ }
2536
+ });
2537
+ }
2538
+ findTabByValue(value) {
2539
+ return this.state.tabs.find((tab) => tab.value === value);
2540
+ }
2541
+ loadDefaultTab() {
2542
+ console.log('[BaseTabbedFormComponent] Loading default tab');
2543
+ const defaultTab = this.state.tabs.find((tab) => tab.isParentModel) || this.state.tabs[0];
2544
+ if (!defaultTab)
2545
+ return;
2546
+ const formData = this.getTabFormData(defaultTab);
2547
+ this.router.navigate([defaultTab.value], {
2548
+ relativeTo: this.route,
2549
+ queryParamsHandling: 'preserve',
2550
+ state: {
2551
+ formData,
2552
+ parentFormData: this.state.currentData,
2553
+ context: ComponentContext.TABBED
2554
+ }
2555
+ });
2556
+ }
2557
+ getTabFormData(tabConfig) {
2558
+ if (!tabConfig)
2559
+ return null;
2560
+ if (tabConfig.isParentModel) {
2561
+ return this.state.currentData;
2562
+ }
2563
+ const fieldName = this.utilsService.toCamelCase(tabConfig.value);
2564
+ const nestedData = this.state.currentData[fieldName];
2565
+ if (this.isTabFormData(nestedData)) {
2566
+ return nestedData;
2567
+ }
2568
+ console.warn(`Invalid tab form data for field: ${fieldName}`, nestedData);
2569
+ return null;
2570
+ }
2571
+ isTabFormData(data) {
2572
+ return data != null &&
2573
+ typeof data === 'object' &&
2574
+ 'uuid' in data &&
2575
+ typeof data.uuid === 'string';
2576
+ }
2577
+ onTabChange(value) {
2578
+ const tabConfig = this.findTabByValue(value);
2579
+ if (!tabConfig) {
2580
+ console.warn(`Tab not found: ${value}`);
2581
+ return;
2582
+ }
2583
+ this.state.activeTab = tabConfig.value;
2584
+ const formData = this.getTabFormData(tabConfig);
2585
+ this.router.navigate([this.state.activeTab], {
2586
+ relativeTo: this.route,
2587
+ queryParamsHandling: 'preserve',
2588
+ state: {
2589
+ formData: formData,
2590
+ parentFormData: this.state.currentData,
2591
+ context: ComponentContext.TABBED
2592
+ }
2593
+ });
2594
+ }
2595
+ hasFormChanges(currentData) {
2596
+ if (this.state.isNew || !this.state.originalData) {
2597
+ return true;
2598
+ }
2599
+ return this.detectChanges(currentData, this.state.originalData);
2600
+ }
2601
+ detectChanges(current, original) {
2602
+ try {
2603
+ return JSON.stringify(current) !== JSON.stringify(original);
2604
+ }
2605
+ catch (error) {
2606
+ console.error('[BaseTabbedFormComponent] Error comparing objects:', error);
2607
+ // Fallback to basic comparison if JSON.stringify fails
2608
+ return Object.keys(current).some(key => current[key] !== original[key]);
2609
+ }
2610
+ }
2611
+ ngOnDestroy() {
2612
+ this.destroyed$.next();
2613
+ this.destroyed$.complete();
2614
+ this.stateManager$.complete();
2615
+ }
2616
+ isValidPageMode(mode) {
2617
+ return Object.values(AppConstants.PAGE_MODE).includes(mode);
2618
+ }
2619
+ getTabTitle(data) {
2620
+ throw new Error('Method not implemented.');
2621
+ }
2622
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTabbedFormComponent, deps: [{ token: i1$1.ActivatedRoute }, { token: i1$1.Router }, { token: NotificationService }, { token: UtilsService }], target: i0.ɵɵFactoryTarget.Component });
2623
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: BaseTabbedFormComponent, isStandalone: true, selector: "ng-component", usesInheritance: true, ngImport: i0, template: '', isInline: true });
2624
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTabbedFormComponent });
2625
+ }
2626
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTabbedFormComponent, decorators: [{
2627
+ type: Injectable
2628
+ }, {
2629
+ type: Component,
2630
+ args: [{
2631
+ template: ''
2632
+ }]
2633
+ }], ctorParameters: () => [{ type: i1$1.ActivatedRoute }, { type: i1$1.Router }, { type: NotificationService }, { type: UtilsService }] });
2634
+
2635
+ class BaseListTabbedFormComponent extends BasePageComponent {
2636
+ route;
2637
+ router;
2638
+ notificationService;
2639
+ utilsService;
2640
+ destroyed$ = new Subject();
2641
+ tabState$ = new ReplaySubject(1);
2642
+ state = {
2643
+ form: new FormGroup({}),
2644
+ fields: [],
2645
+ isNew: true,
2646
+ mode: AppConstants.PAGE_MODE.CREATE,
2647
+ currentData: {},
2648
+ originalData: null,
2649
+ hasChanges: false,
2650
+ loading: false,
2651
+ context: ComponentContext.TABBED,
2652
+ activeTab: '',
2653
+ tabs: [],
2654
+ formDataList: []
2655
+ };
2656
+ constructor(route, router, notificationService, utilsService) {
2657
+ super(utilsService, notificationService);
2658
+ this.route = route;
2659
+ this.router = router;
2660
+ this.notificationService = notificationService;
2661
+ this.utilsService = utilsService;
2662
+ }
2663
+ ngOnInit() {
2664
+ this.initializeFormFromQueryParams();
2665
+ this.setupTabStateSubscription();
2666
+ this.setupRouteParamsSubscription();
2667
+ }
2668
+ initializeFormFromQueryParams() {
2669
+ this.route.queryParams.pipe(take$1(1), catchError$1(error => {
2670
+ console.error('Error initializing form:', error);
2671
+ this.showError(AppMessages.FORM.ERROR.INIT);
2672
+ throw error;
2673
+ }), takeUntil$1(this.destroyed$)).subscribe(params => this.initializeForm(params));
2674
+ }
2675
+ setupTabStateSubscription() {
2676
+ this.tabState$.pipe(distinctUntilChanged$1((prev, curr) => prev.activeTab === curr.activeTab &&
2677
+ prev.currentData?.uuid === curr.currentData?.uuid), takeUntil$1(this.destroyed$)).subscribe(state => {
2678
+ this.updateComponentState(state);
2679
+ });
2680
+ }
2681
+ setupRouteParamsSubscription() {
2682
+ this.route.params.pipe(takeUntil$1(this.destroyed$)).subscribe(params => {
2683
+ const uuid = params['id'];
2684
+ if (uuid && this.state.formDataList?.length) {
2685
+ this.handleTabChange(uuid);
2686
+ }
2687
+ });
2688
+ }
2689
+ handleTabChange(uuid) {
2690
+ const selectedData = this.findTabData(uuid);
2691
+ if (selectedData) {
2692
+ this.tabState$.next({
2693
+ activeTab: uuid,
2694
+ currentData: selectedData,
2695
+ originalData: { ...selectedData }
2696
+ });
2697
+ }
2698
+ else {
2699
+ console.warn(`[BaseListTabbedFormComponent] Tab data not found for uuid: ${uuid}`);
2700
+ }
2701
+ }
2702
+ findTabData(uuid) {
2703
+ return this.state.formDataList.find(data => data.uuid === uuid);
2704
+ }
2705
+ updateComponentState(tabState) {
2706
+ this.state = {
2707
+ ...this.state,
2708
+ activeTab: tabState.activeTab,
2709
+ currentData: tabState.currentData,
2710
+ originalData: tabState.originalData
2711
+ };
2712
+ }
2713
+ initializeForm(params) {
2714
+ try {
2715
+ const routeMode = params[AppConstants.FORM.MODE];
2716
+ this.initializeFormMode(routeMode);
2717
+ this.setupFormState();
2718
+ this.initializeListForm();
2719
+ }
2720
+ catch (error) {
2721
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
2722
+ console.error('Error initializing form:', error);
2723
+ this.showError(`${AppMessages.FORM.ERROR.INIT}: ${errorMessage}`);
2724
+ throw error; // Re-throw to trigger error handling in the subscription
2725
+ }
2726
+ }
2727
+ initializeFormMode(routeMode) {
2728
+ if (!routeMode) {
2729
+ this.state.mode = AppConstants.PAGE_MODE.CREATE;
2730
+ return;
2731
+ }
2732
+ if (!this.isValidPageMode(routeMode)) {
2733
+ const validModes = Object.values(AppConstants.PAGE_MODE).join(', ');
2734
+ throw new Error(`Invalid page mode: ${routeMode}. Valid modes are: ${validModes}`);
2735
+ }
2736
+ this.state.mode = routeMode;
2737
+ }
2738
+ setupFormState() {
2739
+ if (this.state.mode === AppConstants.PAGE_MODE.CREATE) {
2740
+ this.state.isNew = true;
2741
+ this.state.loading = false;
2742
+ this.state.originalData = null;
2743
+ }
2744
+ }
2745
+ initializeListForm() {
2746
+ const routerState = this.router.getCurrentNavigation()?.extras?.state || window.history.state;
2747
+ const formDataList = routerState?.formDataList;
2748
+ if (!formDataList?.length) {
2749
+ throw new Error('formDataList is required in state for LIST type');
2750
+ }
2751
+ this.state.formDataList = [...formDataList]; // Create a new array to avoid reference issues
2752
+ this.state.tabs = this.createTabsFromFormDataList(this.state.formDataList);
2753
+ const firstTab = this.state.tabs[0].value;
2754
+ const firstItem = this.state.formDataList[0];
2755
+ // Initialize tab state
2756
+ this.tabState$.next({
2757
+ activeTab: firstTab,
2758
+ currentData: firstItem,
2759
+ originalData: { ...firstItem }
2760
+ });
2761
+ // Navigate to the first tab
2762
+ this.navigateToTab(firstTab);
2763
+ }
2764
+ createTabsFromFormDataList(formDataList) {
2765
+ return formDataList.map((data) => ({
2766
+ value: data.uuid,
2767
+ title: this.getTabTitle(data)
2768
+ }));
2769
+ }
2770
+ onTabChange(event) {
2771
+ const uuid = event?.toString() || '';
2772
+ console.log('[BaseListTabbedFormComponent] Tab change event:', uuid);
2773
+ const selectedData = this.findTabData(uuid);
2774
+ if (selectedData) {
2775
+ this.handleTabChange(uuid);
2776
+ this.navigateToTab(uuid);
2777
+ }
2778
+ else {
2779
+ console.warn(`[BaseListTabbedFormComponent] Tab data not found for uuid: ${uuid}`);
2780
+ }
2781
+ }
2782
+ navigateToTab(uuid) {
2783
+ const selectedData = this.findTabData(uuid);
2784
+ if (!selectedData) {
2785
+ console.error(`[BaseListTabbedFormComponent] Cannot navigate: No data found for uuid ${uuid}`);
2786
+ return;
2787
+ }
2788
+ this.router.navigate([uuid], {
2789
+ relativeTo: this.route,
2790
+ queryParamsHandling: 'preserve',
2791
+ state: {
2792
+ formData: selectedData,
2793
+ context: ComponentContext.TABBED
2794
+ }
2795
+ });
2796
+ }
2797
+ ngOnDestroy() {
2798
+ this.destroyed$.next();
2799
+ this.destroyed$.complete();
2800
+ this.tabState$.complete();
2801
+ }
2802
+ isValidPageMode(mode) {
2803
+ return Object.values(AppConstants.PAGE_MODE).includes(mode);
2804
+ }
2805
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseListTabbedFormComponent, deps: [{ token: i1$1.ActivatedRoute }, { token: i1$1.Router }, { token: NotificationService }, { token: UtilsService }], target: i0.ɵɵFactoryTarget.Component });
2806
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: BaseListTabbedFormComponent, isStandalone: true, selector: "ng-component", usesInheritance: true, ngImport: i0, template: '', isInline: true });
2807
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseListTabbedFormComponent });
2808
+ }
2809
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseListTabbedFormComponent, decorators: [{
2810
+ type: Injectable
2811
+ }, {
2812
+ type: Component,
2813
+ args: [{
2814
+ template: ''
2815
+ }]
2816
+ }], ctorParameters: () => [{ type: i1$1.ActivatedRoute }, { type: i1$1.Router }, { type: NotificationService }, { type: UtilsService }] });
2817
+
2818
+ /**
2819
+ * Base component for all task detail components in the application.
2820
+ * Uses composition with FormService for form functionality.
2821
+ */
2822
+ class BaseTaskDetailsComponent extends BasePageComponent {
2823
+ route;
2824
+ router;
2825
+ notificationService;
2826
+ utilsService;
2827
+ formlyConfigService;
2828
+ destroyed$ = new Subject();
2829
+ formService;
2830
+ state = {
2831
+ form: new FormGroup({}),
2832
+ fields: [],
2833
+ isNew: true,
2834
+ mode: AppConstants.PAGE_MODE.CREATE,
2835
+ currentData: {},
2836
+ originalData: null,
2837
+ hasChanges: false,
2838
+ loading: false,
2839
+ context: ComponentContext.STANDALONE
2840
+ };
2841
+ constructor(route, router, notificationService, utilsService, formlyConfigService) {
2842
+ super(utilsService, notificationService);
2843
+ this.route = route;
2844
+ this.router = router;
2845
+ this.notificationService = notificationService;
2846
+ this.utilsService = utilsService;
2847
+ this.formlyConfigService = formlyConfigService;
2848
+ }
2849
+ initializeServices() {
2850
+ return of(void 0).pipe(map(() => {
2851
+ this.formService = new FormService(this.getEntityService(), this.formlyConfigService, this.notificationService, this.getEntityName(), this.getJsonFields(), this.getDropdownOptions());
2852
+ this.state = this.formService.getState();
2853
+ this.loading = this.state.loading;
2854
+ }));
2855
+ }
2856
+ ngOnInit() {
2857
+ this.initializeServices()
2858
+ .pipe(switchMap(() => this.initializeFromRoute()), takeUntil(this.destroyed$))
2859
+ .subscribe({
2860
+ next: () => this.onInitialized(),
2861
+ error: error => this.handleInitError(error)
2862
+ });
2863
+ }
2864
+ initializeFromRoute() {
2865
+ return this.route.queryParams.pipe(map(params => {
2866
+ const mode = params[AppConstants.FORM.MODE];
2867
+ if (mode) {
2868
+ this.formService.setMode(mode);
2869
+ this.state = this.formService.getState();
2870
+ }
2871
+ const uuid = params[AppConstants.FORM.UUID];
2872
+ if (uuid) {
2873
+ this.loadData(uuid);
2874
+ }
2875
+ // Check for context in router state
2876
+ const routerState = window.history.state;
2877
+ if (routerState?.context) {
2878
+ this.state.context = routerState.context;
2879
+ }
2880
+ }));
2881
+ }
2882
+ loadData(uuid) {
2883
+ this.state.loading = true;
2884
+ this.formService.loadData(uuid)
2885
+ .pipe(takeUntil(this.destroyed$), finalize$1(() => {
2886
+ this.state.loading = false;
2887
+ this.state = this.formService.getState();
2888
+ }))
2889
+ .subscribe({
2890
+ error: (error) => this.handleLoadError(error)
2891
+ });
2892
+ }
2893
+ onSubmit() {
2894
+ this.formService.submit()
2895
+ .pipe(takeUntil(this.destroyed$))
2896
+ .subscribe({
2897
+ next: () => this.navigateAfterSubmit(),
2898
+ error: (error) => this.handleSubmitError(error)
2899
+ });
2900
+ }
2901
+ navigateAfterSubmit() {
2902
+ if (this.isInContext(ComponentContext.TABBED)) {
2903
+ return; // Stay on current route when in tab
2904
+ }
2905
+ this.router.navigate([`/${this.getEntityName().toLowerCase()}s`]);
2906
+ }
2907
+ onInitialized() {
2908
+ console.log('[BaseTaskDetailsComponent] Initialized successfully');
2909
+ }
2910
+ handleInitError(error) {
2911
+ console.error('Initialization error:', error);
2912
+ this.notificationService.showError(AppMessages.FORM.ERROR.INIT);
2913
+ this.state.loading = false;
2914
+ }
2915
+ handleLoadError(error) {
2916
+ console.error('Load error:', error);
2917
+ this.notificationService.showError(`Failed to load ${this.getEntityName()}`);
2918
+ }
2919
+ handleSubmitError(error) {
2920
+ console.error('Submit error:', error);
2921
+ const errorMessage = error.error?.message || `Failed to save ${this.getEntityName()}`;
2922
+ this.showError(errorMessage);
2923
+ }
2924
+ isInContext(contextType) {
2925
+ return this.state.context === contextType;
2926
+ }
2927
+ ngOnDestroy() {
2928
+ this.destroyed$.next();
2929
+ this.destroyed$.complete();
2930
+ }
2931
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTaskDetailsComponent, deps: [{ token: i1$1.ActivatedRoute }, { token: i1$1.Router }, { token: NotificationService }, { token: UtilsService }, { token: FormlyConfigService }], target: i0.ɵɵFactoryTarget.Directive });
2932
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.6", type: BaseTaskDetailsComponent, isStandalone: true, usesInheritance: true, ngImport: i0 });
2933
+ }
2934
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTaskDetailsComponent, decorators: [{
2935
+ type: Directive
2936
+ }], ctorParameters: () => [{ type: i1$1.ActivatedRoute }, { type: i1$1.Router }, { type: NotificationService }, { type: UtilsService }, { type: FormlyConfigService }] });
2937
+
2938
+ class IndianDatePipe {
2939
+ datePipe;
2940
+ constructor(datePipe) {
2941
+ this.datePipe = datePipe;
2942
+ }
2943
+ transform(value, format = 'default') {
2944
+ if (!value)
2945
+ return null;
2946
+ // First validate dd-MM-yyyy format
2947
+ const dateRegex = /^(0[1-9]|[12][0-9]|3[01])-(0[1-9]|1[0-2])-\d{4}$/;
2948
+ if (!dateRegex.test(value)) {
2949
+ throw new Error(`Invalid date format: ${value}. Expected format: dd-MM-yyyy`);
2950
+ }
2951
+ // Validate real date
2952
+ const [day, month, year] = value.split('-').map(Number);
2953
+ const date = new Date(year, month - 1, day);
2954
+ if (date.getFullYear() !== year ||
2955
+ date.getMonth() !== month - 1 ||
2956
+ date.getDate() !== day) {
2957
+ throw new Error(`Invalid date: ${value}`);
2958
+ }
2959
+ // Apply requested format
2960
+ switch (format) {
2961
+ case 'short':
2962
+ // Pad with leading zeros for consistent format
2963
+ const paddedDay = day.toString().padStart(2, '0');
2964
+ const paddedMonth = month.toString().padStart(2, '0');
2965
+ return `${paddedDay}-${paddedMonth}-${year}`; // 10-03-2025
2966
+ case 'medium':
2967
+ return this.datePipe.transform(date, 'dd MMM yyyy'); // 10 Jan 2019
2968
+ default:
2969
+ return value; // original dd-MM-yyyy
2970
+ }
2971
+ }
2972
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: IndianDatePipe, deps: [{ token: i1$3.DatePipe }], target: i0.ɵɵFactoryTarget.Pipe });
2973
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.6", ngImport: i0, type: IndianDatePipe, isStandalone: true, name: "indianDate" });
2974
+ }
2975
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: IndianDatePipe, decorators: [{
2976
+ type: Pipe,
2977
+ args: [{
2978
+ name: 'indianDate',
2979
+ standalone: true
2980
+ }]
2981
+ }], ctorParameters: () => [{ type: i1$3.DatePipe }] });
2982
+
2983
+ /*
2984
+ * Public API Surface of core-lib
2985
+ */
2986
+ // Constants
2390
2987
 
2391
2988
  /**
2392
2989
  * Generated bundle index. Do not edit.
2393
2990
  */
2394
2991
 
2395
- 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 };
2992
+ export { AppConstants, AppMessages, BaseCrudService, BaseFormComponent, BaseListPageComponent, BaseListTabbedFormComponent, BasePageComponent, BaseTabbedFormComponent, BaseTaskDetailsComponent, BaseTaskService, ComponentContext, CoreFormlyModule, FormService, FormlyConfigService, HttpUtilityService, IndianDatePipe, KEYCLOAK_CONFIG, KeycloakAuthGuard, KeycloakService, NotificationService, TabbedFormType, TaskRole, UtilsService };
2396
2993
  //# sourceMappingURL=codex-ts-core-lib.mjs.map