@codex-ts/core-lib 1.0.9 → 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.
- package/fesm2022/codex-ts-core-lib.mjs +2388 -1794
- package/fesm2022/codex-ts-core-lib.mjs.map +1 -1
- package/lib/components/base-form.component.d.ts +27 -22
- package/lib/components/base-list-page.component.d.ts +12 -71
- package/lib/components/base-list-tabbed-form.component.d.ts +40 -0
- package/lib/components/base-tabbed-form.component.d.ts +17 -20
- package/lib/components/base-table.component.d.ts +72 -0
- package/lib/components/base-task-details.component.d.ts +59 -0
- package/lib/enums/task-role.enum.d.ts +37 -0
- package/lib/keycloak/keycloak-config.interface.d.ts +19 -0
- package/lib/keycloak/keycloak.guard.d.ts +25 -0
- package/lib/keycloak/keycloak.service.d.ts +24 -0
- package/lib/keycloak/keycloak.token.d.ts +3 -0
- package/lib/models/base-create-request.model.d.ts +12 -0
- package/lib/models/base-request.model.d.ts +9 -0
- package/lib/models/base-response.model.d.ts +15 -0
- package/lib/models/base-task.model.d.ts +17 -0
- package/lib/models/base-update-request.model.d.ts +16 -0
- package/lib/models/base.model.d.ts +59 -0
- package/lib/models/pagination/page.interface.d.ts +23 -6
- package/lib/services/base-task.service.d.ts +40 -0
- package/lib/services/form.service.d.ts +31 -0
- package/lib/states/form-component.state.d.ts +12 -0
- package/lib/states/form.state.d.ts +21 -0
- package/lib/states/list-tabbed-form.state.d.ts +9 -0
- package/lib/states/tabbed-form.state.d.ts +21 -0
- package/package.json +7 -2
- package/public-api.d.ts +29 -21
- package/lib/services/entity-registry.service.d.ts +0 -26
- 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,
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import * as i1
|
|
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
|
|
8
|
-
import * as
|
|
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
|
|
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$
|
|
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
|
-
*
|
|
36
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
50
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
65
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
80
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
96
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
109
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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;
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
console.error('Failed to get token:', error);
|
|
187
|
+
return null;
|
|
164
188
|
}
|
|
165
|
-
// Clean the path part (after protocol) and reconstruct the URL
|
|
166
|
-
const cleanPath = rest.join('://').replace(/\/+/g, '/');
|
|
167
|
-
return `${protocol}://${cleanPath}`;
|
|
168
189
|
}
|
|
169
|
-
|
|
170
|
-
|
|
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
|
+
});
|
|
217
|
+
}
|
|
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.6", ngImport: i0, type:
|
|
230
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: KeycloakService, decorators: [{
|
|
173
231
|
type: Injectable,
|
|
174
|
-
args: [{
|
|
175
|
-
|
|
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
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
constructor(
|
|
183
|
-
this.
|
|
238
|
+
class KeycloakAuthGuard {
|
|
239
|
+
keycloakService;
|
|
240
|
+
router;
|
|
241
|
+
constructor(keycloakService, router) {
|
|
242
|
+
this.keycloakService = keycloakService;
|
|
243
|
+
this.router = router;
|
|
184
244
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
|
197
|
-
}
|
|
198
|
-
|
|
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
|
-
*
|
|
203
|
-
* @param
|
|
204
|
-
* @
|
|
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
|
-
|
|
207
|
-
|
|
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
|
-
*
|
|
211
|
-
* @param
|
|
212
|
-
* @returns
|
|
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
|
-
|
|
215
|
-
|
|
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.6", ngImport: i0, type: EnumRegistryService, deps: [{ token: UtilsService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
236
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: EnumRegistryService, providedIn: 'root' });
|
|
237
|
-
}
|
|
238
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", 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.6", ngImport: i0, type:
|
|
282
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type:
|
|
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.6", ngImport: i0, type:
|
|
290
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: KeycloakAuthGuard, decorators: [{
|
|
285
291
|
type: Injectable,
|
|
286
|
-
args: [{
|
|
287
|
-
|
|
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.6", ngImport: i0, type: NotificationService, deps: [{ token: i1.MessageService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
320
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: NotificationService });
|
|
321
|
-
}
|
|
322
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", 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,7 +570,7 @@ class HttpUtilityService {
|
|
|
601
570
|
});
|
|
602
571
|
}
|
|
603
572
|
}
|
|
604
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: HttpUtilityService, deps: [{ token: i1
|
|
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 });
|
|
605
574
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: HttpUtilityService, providedIn: 'root' });
|
|
606
575
|
}
|
|
607
576
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: HttpUtilityService, decorators: [{
|
|
@@ -609,547 +578,1487 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImpor
|
|
|
609
578
|
args: [{
|
|
610
579
|
providedIn: 'root'
|
|
611
580
|
}]
|
|
612
|
-
}], ctorParameters: () => [{ type: i1
|
|
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.6", ngImport: i0, type: ReferenceDataProviderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
635
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ReferenceDataProviderService, providedIn: 'root' });
|
|
636
|
-
}
|
|
637
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ReferenceDataProviderService, decorators: [{
|
|
638
|
-
type: Injectable,
|
|
639
|
-
args: [{
|
|
640
|
-
providedIn: 'root'
|
|
641
|
-
}]
|
|
642
|
-
}] });
|
|
581
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: i2.MessageService }] });
|
|
643
582
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
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
|
-
*
|
|
653
|
-
* @param
|
|
654
|
-
* @
|
|
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
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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.6", 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.6", ngImport: i0, type: EntityRegistryService, providedIn: 'root' });
|
|
677
|
-
}
|
|
678
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", 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
|
-
*
|
|
698
|
-
* @param
|
|
699
|
-
* @param
|
|
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
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
return
|
|
721
|
-
}
|
|
722
|
-
else {
|
|
723
|
-
return this.httpUtility.get(this.apiUrl);
|
|
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(', ');
|
|
724
639
|
}
|
|
640
|
+
// Handle single key
|
|
641
|
+
return enumType[key] || key;
|
|
725
642
|
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
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
|
+
}));
|
|
732
655
|
}
|
|
733
|
-
|
|
734
|
-
|
|
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);
|
|
735
666
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
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();
|
|
742
677
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
678
|
+
/**
|
|
679
|
+
* Converts a string to camelCase.
|
|
680
|
+
* Example: "loan-application" becomes "loanApplication"
|
|
681
|
+
* @param input The string to convert
|
|
682
|
+
* @returns The camelCase string
|
|
683
|
+
*/
|
|
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
|
+
}
|
|
747
696
|
}
|
|
748
|
-
return
|
|
697
|
+
return result.join('');
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
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
|
|
704
|
+
*/
|
|
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}`;
|
|
749
717
|
}
|
|
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' });
|
|
750
720
|
}
|
|
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
|
+
}] });
|
|
751
727
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
728
|
+
class NotificationService {
|
|
729
|
+
messageService;
|
|
730
|
+
messageSubject = new BehaviorSubject(null);
|
|
731
|
+
message$ = this.messageSubject.asObservable();
|
|
732
|
+
constructor(messageService) {
|
|
733
|
+
this.messageService = messageService;
|
|
734
|
+
}
|
|
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 });
|
|
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 }] });
|
|
765
762
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
* Provides common functionality like loading state and message display.
|
|
769
|
-
*/
|
|
770
|
-
class BasePageComponent {
|
|
763
|
+
class BaseCrudService {
|
|
764
|
+
httpUtility;
|
|
771
765
|
utilsService;
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
766
|
+
resourcePath;
|
|
767
|
+
backendBaseUrl;
|
|
768
|
+
messageService;
|
|
769
|
+
// Use the loading state from HttpUtilityService
|
|
770
|
+
get loading$() {
|
|
771
|
+
return this.httpUtility.loading$;
|
|
777
772
|
}
|
|
773
|
+
apiUrl;
|
|
778
774
|
/**
|
|
779
|
-
*
|
|
780
|
-
* @param
|
|
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
781
|
*/
|
|
782
|
-
|
|
783
|
-
this.
|
|
782
|
+
constructor(httpUtility, utilsService, resourcePath, backendBaseUrl, messageService) {
|
|
783
|
+
this.httpUtility = httpUtility;
|
|
784
|
+
this.utilsService = utilsService;
|
|
785
|
+
this.resourcePath = resourcePath;
|
|
786
|
+
this.backendBaseUrl = backendBaseUrl;
|
|
787
|
+
this.messageService = messageService;
|
|
788
|
+
this.apiUrl = this.joinUrls(backendBaseUrl, resourcePath);
|
|
784
789
|
}
|
|
785
790
|
/**
|
|
786
|
-
*
|
|
787
|
-
* @param detail The success message to display
|
|
791
|
+
* Joins URL parts ensuring no double slashes
|
|
788
792
|
*/
|
|
789
|
-
|
|
790
|
-
|
|
793
|
+
joinUrls(...parts) {
|
|
794
|
+
return parts.map(part => part.replace(/^\/+|\/+$/g, '')).join('/');
|
|
791
795
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
}
|
|
795
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BasePageComponent, decorators: [{
|
|
796
|
-
type: Directive
|
|
797
|
-
}], ctorParameters: () => [{ type: UtilsService }, { type: NotificationService }] });
|
|
798
|
-
|
|
799
|
-
const AppMessages = {
|
|
800
|
-
COMMON: {
|
|
801
|
-
ERROR: {
|
|
802
|
-
GENERAL: 'An error occurred',
|
|
803
|
-
INVALID_INPUT: 'Invalid input provided',
|
|
804
|
-
REQUIRED_FIELD: 'This field is required'
|
|
805
|
-
},
|
|
806
|
-
SUCCESS: {
|
|
807
|
-
SAVE: 'saved successfully',
|
|
808
|
-
UPDATE: 'updated successfully',
|
|
809
|
-
DELETE: 'deleted successfully'
|
|
796
|
+
getAll(pageRequest) {
|
|
797
|
+
if (pageRequest) {
|
|
798
|
+
return this.httpUtility.post(`${this.apiUrl}/page`, pageRequest);
|
|
810
799
|
}
|
|
811
|
-
|
|
812
|
-
|
|
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'
|
|
800
|
+
else {
|
|
801
|
+
return this.httpUtility.get(this.apiUrl);
|
|
821
802
|
}
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
NAVIGATION: 'Navigation failed',
|
|
828
|
-
INVALID_ID: 'Cannot perform action without ID'
|
|
829
|
-
},
|
|
830
|
-
SUCCESS: {
|
|
831
|
-
DELETE: 'deleted successfully'
|
|
803
|
+
}
|
|
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'));
|
|
832
808
|
}
|
|
809
|
+
return this.httpUtility.get(`${this.apiUrl}/${uuid}`);
|
|
810
|
+
}
|
|
811
|
+
create(entity) {
|
|
812
|
+
return this.httpUtility.post(this.apiUrl, entity);
|
|
813
|
+
}
|
|
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'));
|
|
818
|
+
}
|
|
819
|
+
return this.httpUtility.put(`${this.apiUrl}/${uuid}`, entity);
|
|
820
|
+
}
|
|
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'));
|
|
825
|
+
}
|
|
826
|
+
return this.httpUtility.delete(`${this.apiUrl}/${uuid}`);
|
|
833
827
|
}
|
|
834
|
-
};
|
|
835
|
-
|
|
836
|
-
class AppConstants {
|
|
837
|
-
static FORM = {
|
|
838
|
-
MODE: 'mode',
|
|
839
|
-
UUID: 'uuid',
|
|
840
|
-
NEW: 'new'
|
|
841
|
-
};
|
|
842
|
-
static PAGE_MODE = {
|
|
843
|
-
CREATE: 'create',
|
|
844
|
-
EDIT: 'edit',
|
|
845
|
-
VIEW: 'view'
|
|
846
|
-
};
|
|
847
828
|
}
|
|
848
829
|
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
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;
|
|
853
836
|
utilsService;
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
currentFormData = {};
|
|
861
|
-
originalFormData = null;
|
|
862
|
-
hasChanges = false;
|
|
863
|
-
context = ComponentContext.STANDALONE;
|
|
864
|
-
constructor(route, router, notificationService, utilsService, formlyConfigService) {
|
|
865
|
-
super(utilsService, notificationService);
|
|
866
|
-
this.route = route;
|
|
867
|
-
this.router = router;
|
|
868
|
-
this.notificationService = notificationService;
|
|
837
|
+
resourcePath;
|
|
838
|
+
backendBaseUrl;
|
|
839
|
+
messageService;
|
|
840
|
+
constructor(httpUtility, utilsService, resourcePath, backendBaseUrl, messageService) {
|
|
841
|
+
super(httpUtility, utilsService, resourcePath, backendBaseUrl, messageService);
|
|
842
|
+
this.httpUtility = httpUtility;
|
|
869
843
|
this.utilsService = utilsService;
|
|
870
|
-
this.
|
|
844
|
+
this.resourcePath = resourcePath;
|
|
845
|
+
this.backendBaseUrl = backendBaseUrl;
|
|
846
|
+
this.messageService = messageService;
|
|
871
847
|
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
});
|
|
880
|
-
// Use queryParams instead of params
|
|
881
|
-
this.route.queryParams.pipe(take(1)).subscribe({
|
|
882
|
-
next: params => this.initializeForm(params),
|
|
883
|
-
error: error => {
|
|
884
|
-
console.error('Error initializing form:', error);
|
|
885
|
-
this.showError(AppMessages.FORM.ERROR.INIT);
|
|
886
|
-
}
|
|
887
|
-
});
|
|
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);
|
|
888
855
|
}
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
856
|
+
return this.httpUtility.get(`${this.apiUrl}/department/${department}`);
|
|
857
|
+
}
|
|
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);
|
|
892
865
|
}
|
|
866
|
+
return this.httpUtility.get(`${this.apiUrl}/user/${userId}`);
|
|
893
867
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
868
|
+
/**
|
|
869
|
+
* Send an event for a task
|
|
870
|
+
* Maps to PUT /{uuid}/event/{event} endpoint
|
|
871
|
+
* For ASSIGN events, userId and department are required
|
|
872
|
+
*/
|
|
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');
|
|
900
877
|
}
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
...this.route.snapshot.data
|
|
905
|
-
};
|
|
906
|
-
console.log('[BaseFormComponent] Combined state:', state);
|
|
907
|
-
console.log('[BaseFormComponent] Final state being used:', state);
|
|
908
|
-
if (state && 'formData' in state) {
|
|
909
|
-
console.log('[BaseFormComponent] Found form data in state:', state['formData']);
|
|
910
|
-
this.patchFormData(state['formData']);
|
|
878
|
+
let params = new HttpParams();
|
|
879
|
+
if (userId) {
|
|
880
|
+
params = params.set('userId', userId);
|
|
911
881
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
this.loadFromUuid();
|
|
882
|
+
if (department) {
|
|
883
|
+
params = params.set('department', department);
|
|
915
884
|
}
|
|
885
|
+
return this.httpUtility.put(`${this.apiUrl}/${uuid}/event/${event}`, null, { params });
|
|
916
886
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
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 });
|
|
889
|
+
}
|
|
890
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTaskService, decorators: [{
|
|
891
|
+
type: Injectable
|
|
892
|
+
}], ctorParameters: () => [{ type: HttpUtilityService }, { type: UtilsService }, { type: undefined }, { type: undefined }, { type: undefined }] });
|
|
893
|
+
|
|
894
|
+
class EnumRegistryService {
|
|
895
|
+
utilsService;
|
|
896
|
+
enumMap = new Map();
|
|
897
|
+
constructor(utilsService) {
|
|
898
|
+
this.utilsService = utilsService;
|
|
925
899
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
900
|
+
/**
|
|
901
|
+
* Initialize global enums
|
|
902
|
+
*/
|
|
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
|
+
};
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
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
|
|
920
|
+
*/
|
|
921
|
+
register(name, enumType) {
|
|
922
|
+
this.enumMap.set(name, enumType);
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
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
|
|
928
|
+
*/
|
|
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 [];
|
|
936
934
|
}
|
|
937
|
-
this.
|
|
935
|
+
return this.utilsService.getEnumValues(enumType);
|
|
938
936
|
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
937
|
+
/**
|
|
938
|
+
* Check if an enum type is registered
|
|
939
|
+
* @param name Name of the enum to check
|
|
940
|
+
*/
|
|
941
|
+
hasEnum(name) {
|
|
942
|
+
return this.enumMap.has(name);
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Clear all registered enums
|
|
946
|
+
*/
|
|
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'
|
|
969
|
+
}
|
|
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
|
+
}
|
|
945
981
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
982
|
+
// Handle field groups recursively
|
|
983
|
+
if (field.fieldGroup) {
|
|
984
|
+
field.fieldGroup = this.getFormConfig(field.fieldGroup, mode, dynamicOptions);
|
|
985
|
+
}
|
|
986
|
+
return {
|
|
987
|
+
...field,
|
|
988
|
+
...commonConfig,
|
|
989
|
+
props: {
|
|
990
|
+
...field.props,
|
|
991
|
+
...commonConfig.props
|
|
954
992
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
993
|
+
};
|
|
994
|
+
});
|
|
995
|
+
}
|
|
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();
|
|
961
1037
|
}
|
|
962
1038
|
}
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
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
|
|
1058
|
+
};
|
|
1059
|
+
this.formState.form.patchValue(data, { emitEvent: false });
|
|
1060
|
+
}
|
|
1061
|
+
submit() {
|
|
1062
|
+
if (!this.validateSubmission()) {
|
|
1063
|
+
return of(null);
|
|
966
1064
|
}
|
|
967
|
-
|
|
968
|
-
|
|
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
|
+
}));
|
|
1071
|
+
}
|
|
1072
|
+
validateSubmission() {
|
|
1073
|
+
if (!this.formState.form.valid) {
|
|
1074
|
+
this.notificationService.showError('Please check form errors');
|
|
1075
|
+
return false;
|
|
969
1076
|
}
|
|
970
|
-
|
|
971
|
-
|
|
1077
|
+
if (!this.formState.hasChanges && !this.formState.isNew) {
|
|
1078
|
+
this.notificationService.showSuccess('No changes to save');
|
|
1079
|
+
return false;
|
|
1080
|
+
}
|
|
1081
|
+
if (!this.formState.currentData || Object.keys(this.formState.currentData).length === 0) {
|
|
1082
|
+
this.notificationService.showError('No data to save');
|
|
1083
|
+
return false;
|
|
972
1084
|
}
|
|
1085
|
+
return true;
|
|
973
1086
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
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();
|
|
982
1097
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
this.form.disable();
|
|
1098
|
+
else {
|
|
1099
|
+
this.formState.form.enable();
|
|
986
1100
|
}
|
|
987
1101
|
}
|
|
988
|
-
|
|
989
|
-
return this.
|
|
1102
|
+
getState() {
|
|
1103
|
+
return this.formState;
|
|
990
1104
|
}
|
|
991
|
-
|
|
992
|
-
|
|
1105
|
+
patchValue(value) {
|
|
1106
|
+
this.formState.form.patchValue(value);
|
|
1107
|
+
this.updateCurrentData();
|
|
1108
|
+
this.checkFormChanges();
|
|
993
1109
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
update: (uuid, data) => entityService.update(uuid, { ...data, uuid }),
|
|
999
|
-
getById: (uuid) => entityService.getById(uuid)
|
|
1110
|
+
updateCurrentData() {
|
|
1111
|
+
this.formState.currentData = {
|
|
1112
|
+
...this.formState.form.value,
|
|
1113
|
+
uuid: this.formState.originalData?.uuid
|
|
1000
1114
|
};
|
|
1001
1115
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
formValid: this.form.valid,
|
|
1005
|
-
formData: this.currentFormData,
|
|
1006
|
-
formState: this.form.value
|
|
1007
|
-
});
|
|
1008
|
-
if (!this.currentFormData || Object.keys(this.currentFormData).length === 0) {
|
|
1009
|
-
this.notificationService.showError('No data to save');
|
|
1010
|
-
return;
|
|
1011
|
-
}
|
|
1012
|
-
if (this.form.valid) {
|
|
1013
|
-
if (this.isNew) {
|
|
1014
|
-
console.log('[BaseFormComponent] Saving new entity');
|
|
1015
|
-
this.saveEntity();
|
|
1016
|
-
return;
|
|
1017
|
-
}
|
|
1018
|
-
const hasChanges = this.hasFormChanges(this.currentFormData);
|
|
1019
|
-
console.log('[BaseFormComponent] Change detection:', {
|
|
1020
|
-
hasChanges,
|
|
1021
|
-
currentData: this.currentFormData,
|
|
1022
|
-
originalData: this.originalFormData
|
|
1023
|
-
});
|
|
1024
|
-
if (!hasChanges) {
|
|
1025
|
-
this.notificationService.showSuccess('No changes to save');
|
|
1026
|
-
return;
|
|
1027
|
-
}
|
|
1028
|
-
console.log('[BaseFormComponent] Saving changes');
|
|
1029
|
-
this.saveEntity();
|
|
1030
|
-
}
|
|
1031
|
-
else {
|
|
1032
|
-
console.warn('[BaseFormComponent] Form validation failed:', this.form.errors);
|
|
1033
|
-
this.notificationService.showError('Please check form errors');
|
|
1034
|
-
}
|
|
1116
|
+
checkFormChanges() {
|
|
1117
|
+
this.formState.hasChanges = this.hasFormChanges(this.formState.currentData);
|
|
1035
1118
|
}
|
|
1036
1119
|
hasFormChanges(currentData) {
|
|
1037
|
-
if (!this.
|
|
1120
|
+
if (!this.formState.originalData || this.formState.isNew) {
|
|
1038
1121
|
return true;
|
|
1039
1122
|
}
|
|
1040
|
-
|
|
1041
|
-
currentData,
|
|
1042
|
-
originalData: this.originalFormData
|
|
1043
|
-
});
|
|
1044
|
-
const hasChanges = Object.keys(currentData).some(key => {
|
|
1123
|
+
return Object.keys(currentData).some(key => {
|
|
1045
1124
|
const currentValue = currentData[key];
|
|
1046
|
-
const originalValue = this.
|
|
1047
|
-
|
|
1048
|
-
if (changed) {
|
|
1049
|
-
console.log(`[BaseFormComponent] Field '${key}' changed:`, {
|
|
1050
|
-
from: originalValue,
|
|
1051
|
-
to: currentValue
|
|
1052
|
-
});
|
|
1053
|
-
}
|
|
1054
|
-
return changed;
|
|
1055
|
-
});
|
|
1056
|
-
console.log('[BaseFormComponent] Changes detected:', hasChanges);
|
|
1057
|
-
return hasChanges;
|
|
1058
|
-
}
|
|
1059
|
-
saveEntity() {
|
|
1060
|
-
this.loading = true;
|
|
1061
|
-
const formData = this.currentFormData;
|
|
1062
|
-
const entityName = this.getEntityName();
|
|
1063
|
-
const entityOperations = this.getEntityOperations();
|
|
1064
|
-
const request$ = this.isNew
|
|
1065
|
-
? entityOperations.create(formData)
|
|
1066
|
-
: entityOperations.update(this.originalFormData.uuid, formData);
|
|
1067
|
-
request$.subscribe({
|
|
1068
|
-
next: () => {
|
|
1069
|
-
this.loading = false;
|
|
1070
|
-
this.notificationService.showSuccess(`${entityName} ${AppMessages.FORM.SUCCESS.SAVE}`);
|
|
1071
|
-
this.router.navigate(this.onSubmitRoute());
|
|
1072
|
-
},
|
|
1073
|
-
error: (error) => {
|
|
1074
|
-
const errorMessage = error.error?.message || `${AppMessages.FORM.ERROR.SAVE} ${entityName}`;
|
|
1075
|
-
this.showError(errorMessage);
|
|
1076
|
-
this.loading = false;
|
|
1077
|
-
}
|
|
1078
|
-
});
|
|
1079
|
-
}
|
|
1080
|
-
loadEntity(uuid) {
|
|
1081
|
-
this.loading = true;
|
|
1082
|
-
this.isNew = false;
|
|
1083
|
-
const entityName = this.getEntityName();
|
|
1084
|
-
this.getEntityOperations().getById(uuid).subscribe({
|
|
1085
|
-
next: (data) => {
|
|
1086
|
-
this.originalFormData = data;
|
|
1087
|
-
this.currentFormData = { ...data };
|
|
1088
|
-
this.loading = false;
|
|
1089
|
-
console.log('[BaseFormComponent] Original data loaded:', this.originalFormData);
|
|
1090
|
-
console.log('[BaseFormComponent] Form data updated:', this.currentFormData);
|
|
1091
|
-
},
|
|
1092
|
-
error: () => {
|
|
1093
|
-
this.showError(`${AppMessages.FORM.ERROR.LOAD} ${entityName}`);
|
|
1094
|
-
this.loading = false;
|
|
1095
|
-
}
|
|
1125
|
+
const originalValue = this.formState.originalData[key];
|
|
1126
|
+
return currentValue !== originalValue;
|
|
1096
1127
|
});
|
|
1097
1128
|
}
|
|
1098
|
-
isValidPageMode(mode) {
|
|
1099
|
-
return Object.values(AppConstants.PAGE_MODE).includes(mode);
|
|
1100
|
-
}
|
|
1101
|
-
getDropdownOptions() {
|
|
1102
|
-
return {};
|
|
1103
|
-
}
|
|
1104
|
-
/**
|
|
1105
|
-
* Checks if the component is running in a specific context
|
|
1106
|
-
* @param contextType The context type to check against
|
|
1107
|
-
*/
|
|
1108
|
-
isInContext(contextType) {
|
|
1109
|
-
return this.context === contextType;
|
|
1110
|
-
}
|
|
1111
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseFormComponent, deps: [{ token: i1$2.ActivatedRoute }, { token: i1$2.Router }, { token: NotificationService }, { token: UtilsService }, { token: FormlyConfigService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1112
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: BaseFormComponent, isStandalone: true, selector: "ng-component", inputs: { context: "context" }, usesInheritance: true, ngImport: i0, template: '', isInline: true });
|
|
1113
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseFormComponent });
|
|
1114
1129
|
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1130
|
+
|
|
1131
|
+
class TextInputType extends FieldType {
|
|
1132
|
+
defaultOptions = {
|
|
1133
|
+
props: {
|
|
1134
|
+
type: 'text',
|
|
1135
|
+
placeholder: ''
|
|
1136
|
+
}
|
|
1137
|
+
};
|
|
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: `
|
|
1140
|
+
<input pInputText
|
|
1141
|
+
[type]="props['type'] || 'text'"
|
|
1142
|
+
[formControl]="$any(formControl)"
|
|
1143
|
+
[formlyAttributes]="field"
|
|
1144
|
+
[placeholder]="props['placeholder'] || ''"
|
|
1145
|
+
[attr.aria-label]="props['label'] || props['placeholder'] || ''"
|
|
1146
|
+
class="w-full"/>
|
|
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"] }] });
|
|
1148
|
+
}
|
|
1149
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TextInputType, decorators: [{
|
|
1118
1150
|
type: Component,
|
|
1119
1151
|
args: [{
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1152
|
+
selector: 'formly-field-text-input',
|
|
1153
|
+
standalone: true,
|
|
1154
|
+
imports: [
|
|
1155
|
+
CommonModule,
|
|
1156
|
+
ReactiveFormsModule,
|
|
1157
|
+
FormlyModule,
|
|
1158
|
+
InputTextModule
|
|
1159
|
+
],
|
|
1160
|
+
template: `
|
|
1161
|
+
<input pInputText
|
|
1162
|
+
[type]="props['type'] || 'text'"
|
|
1163
|
+
[formControl]="$any(formControl)"
|
|
1164
|
+
[formlyAttributes]="field"
|
|
1165
|
+
[placeholder]="props['placeholder'] || ''"
|
|
1166
|
+
[attr.aria-label]="props['label'] || props['placeholder'] || ''"
|
|
1167
|
+
class="w-full"/>
|
|
1168
|
+
`
|
|
1169
|
+
}]
|
|
1170
|
+
}] });
|
|
1171
|
+
|
|
1172
|
+
class SelectType extends FieldType {
|
|
1173
|
+
get options$() {
|
|
1174
|
+
const options = this.props['options'];
|
|
1175
|
+
return isObservable(options) ? options : of(options || []);
|
|
1176
|
+
}
|
|
1177
|
+
defaultOptions = {
|
|
1178
|
+
props: {
|
|
1179
|
+
options: [],
|
|
1180
|
+
optionLabel: 'label',
|
|
1181
|
+
optionValue: 'value',
|
|
1182
|
+
placeholder: 'Select',
|
|
1183
|
+
showClear: false,
|
|
1184
|
+
filter: false,
|
|
1185
|
+
virtualScroll: false
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
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: `
|
|
1190
|
+
<ng-container *ngIf="options$ | async as options">
|
|
1191
|
+
<p-select
|
|
1192
|
+
[formControl]="$any(formControl)"
|
|
1193
|
+
[formlyAttributes]="field"
|
|
1194
|
+
[options]="options"
|
|
1195
|
+
[virtualScroll]="false"
|
|
1196
|
+
[showClear]="false"
|
|
1197
|
+
[placeholder]="props['placeholder'] || 'Select'"
|
|
1198
|
+
styleClass="w-full">
|
|
1199
|
+
</p-select>
|
|
1200
|
+
</ng-container>
|
|
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"] }] });
|
|
1202
|
+
}
|
|
1203
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: SelectType, decorators: [{
|
|
1204
|
+
type: Component,
|
|
1205
|
+
args: [{
|
|
1206
|
+
selector: 'formly-field-select',
|
|
1207
|
+
standalone: true,
|
|
1208
|
+
imports: [
|
|
1209
|
+
CommonModule,
|
|
1210
|
+
ReactiveFormsModule,
|
|
1211
|
+
FormlyModule,
|
|
1212
|
+
SelectModule
|
|
1213
|
+
],
|
|
1214
|
+
template: `
|
|
1215
|
+
<ng-container *ngIf="options$ | async as options">
|
|
1216
|
+
<p-select
|
|
1217
|
+
[formControl]="$any(formControl)"
|
|
1218
|
+
[formlyAttributes]="field"
|
|
1219
|
+
[options]="options"
|
|
1220
|
+
[virtualScroll]="false"
|
|
1221
|
+
[showClear]="false"
|
|
1222
|
+
[placeholder]="props['placeholder'] || 'Select'"
|
|
1223
|
+
styleClass="w-full">
|
|
1224
|
+
</p-select>
|
|
1225
|
+
</ng-container>
|
|
1226
|
+
`
|
|
1227
|
+
}]
|
|
1228
|
+
}] });
|
|
1229
|
+
|
|
1230
|
+
class RadioType extends FieldType {
|
|
1231
|
+
get options$() {
|
|
1232
|
+
const options = this.props['options'];
|
|
1233
|
+
return isObservable(options) ? options : of(options || []);
|
|
1234
|
+
}
|
|
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: `
|
|
1237
|
+
<div class="flex flex-column gap-1">
|
|
1238
|
+
<ng-container *ngIf="options$ | async as options">
|
|
1239
|
+
<div *ngFor="let option of options; let i = index" class="flex align-items-center gap-2">
|
|
1240
|
+
<p-radioButton
|
|
1241
|
+
[name]="field.name || ''"
|
|
1242
|
+
[value]="option.value"
|
|
1243
|
+
[formControl]="$any(formControl)"
|
|
1244
|
+
[formlyAttributes]="field"
|
|
1245
|
+
[size]="'small'"
|
|
1246
|
+
[inputId]="field.key + '_' + i">
|
|
1247
|
+
</p-radioButton>
|
|
1248
|
+
<label [for]="field.key + '_' + i" class="p-radio-label cursor-pointer">
|
|
1249
|
+
{{option.label}}
|
|
1250
|
+
</label>
|
|
1251
|
+
</div>
|
|
1252
|
+
</ng-container>
|
|
1253
|
+
</div>
|
|
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"] }] });
|
|
1255
|
+
}
|
|
1256
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: RadioType, decorators: [{
|
|
1257
|
+
type: Component,
|
|
1258
|
+
args: [{
|
|
1259
|
+
selector: 'formly-field-radio',
|
|
1260
|
+
standalone: true,
|
|
1261
|
+
imports: [
|
|
1262
|
+
CommonModule,
|
|
1263
|
+
ReactiveFormsModule,
|
|
1264
|
+
FormlyModule,
|
|
1265
|
+
RadioButtonModule
|
|
1266
|
+
],
|
|
1267
|
+
template: `
|
|
1268
|
+
<div class="flex flex-column gap-1">
|
|
1269
|
+
<ng-container *ngIf="options$ | async as options">
|
|
1270
|
+
<div *ngFor="let option of options; let i = index" class="flex align-items-center gap-2">
|
|
1271
|
+
<p-radioButton
|
|
1272
|
+
[name]="field.name || ''"
|
|
1273
|
+
[value]="option.value"
|
|
1274
|
+
[formControl]="$any(formControl)"
|
|
1275
|
+
[formlyAttributes]="field"
|
|
1276
|
+
[size]="'small'"
|
|
1277
|
+
[inputId]="field.key + '_' + i">
|
|
1278
|
+
</p-radioButton>
|
|
1279
|
+
<label [for]="field.key + '_' + i" class="p-radio-label cursor-pointer">
|
|
1280
|
+
{{option.label}}
|
|
1281
|
+
</label>
|
|
1282
|
+
</div>
|
|
1283
|
+
</ng-container>
|
|
1284
|
+
</div>
|
|
1285
|
+
`
|
|
1286
|
+
}]
|
|
1287
|
+
}] });
|
|
1288
|
+
|
|
1289
|
+
class NumericInputType extends FieldType {
|
|
1290
|
+
defaultOptions = {
|
|
1291
|
+
props: {
|
|
1292
|
+
min: undefined,
|
|
1293
|
+
max: undefined,
|
|
1294
|
+
minFractionDigits: 0,
|
|
1295
|
+
maxFractionDigits: 0,
|
|
1296
|
+
prefix: '',
|
|
1297
|
+
suffix: '',
|
|
1298
|
+
useGrouping: true
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
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: `
|
|
1303
|
+
<p-inputNumber
|
|
1304
|
+
[formControl]="$any(formControl)"
|
|
1305
|
+
[formlyAttributes]="field"
|
|
1306
|
+
[placeholder]="props['placeholder'] || ''"
|
|
1307
|
+
[min]="props['min'] || 0"
|
|
1308
|
+
[max]="props['max']"
|
|
1309
|
+
[minFractionDigits]="props['minFractionDigits'] || 0"
|
|
1310
|
+
[maxFractionDigits]="props['maxFractionDigits'] || 0"
|
|
1311
|
+
[prefix]="props['prefix'] || ''"
|
|
1312
|
+
[suffix]="props['suffix'] || ''"
|
|
1313
|
+
[useGrouping]="props['useGrouping'] ?? true"
|
|
1314
|
+
[showButtons]="true"
|
|
1315
|
+
class="w-full">
|
|
1316
|
+
</p-inputNumber>
|
|
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"] }] });
|
|
1318
|
+
}
|
|
1319
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: NumericInputType, decorators: [{
|
|
1320
|
+
type: Component,
|
|
1321
|
+
args: [{
|
|
1322
|
+
selector: 'formly-field-numeric-input',
|
|
1323
|
+
standalone: true,
|
|
1324
|
+
imports: [
|
|
1325
|
+
CommonModule,
|
|
1326
|
+
ReactiveFormsModule,
|
|
1327
|
+
FormlyModule,
|
|
1328
|
+
InputNumberModule
|
|
1329
|
+
],
|
|
1330
|
+
template: `
|
|
1331
|
+
<p-inputNumber
|
|
1332
|
+
[formControl]="$any(formControl)"
|
|
1333
|
+
[formlyAttributes]="field"
|
|
1334
|
+
[placeholder]="props['placeholder'] || ''"
|
|
1335
|
+
[min]="props['min'] || 0"
|
|
1336
|
+
[max]="props['max']"
|
|
1337
|
+
[minFractionDigits]="props['minFractionDigits'] || 0"
|
|
1338
|
+
[maxFractionDigits]="props['maxFractionDigits'] || 0"
|
|
1339
|
+
[prefix]="props['prefix'] || ''"
|
|
1340
|
+
[suffix]="props['suffix'] || ''"
|
|
1341
|
+
[useGrouping]="props['useGrouping'] ?? true"
|
|
1342
|
+
[showButtons]="true"
|
|
1343
|
+
class="w-full">
|
|
1344
|
+
</p-inputNumber>
|
|
1345
|
+
`
|
|
1346
|
+
}]
|
|
1347
|
+
}] });
|
|
1348
|
+
|
|
1349
|
+
class MultiselectType extends FieldType {
|
|
1350
|
+
get options$() {
|
|
1351
|
+
const options = this.props['options'];
|
|
1352
|
+
return isObservable(options) ? options : of(options || []);
|
|
1353
|
+
}
|
|
1354
|
+
defaultOptions = {
|
|
1355
|
+
props: {
|
|
1356
|
+
options: [],
|
|
1357
|
+
optionLabel: 'label',
|
|
1358
|
+
optionValue: 'value',
|
|
1359
|
+
placeholder: 'Select',
|
|
1360
|
+
filter: false,
|
|
1361
|
+
maxSelectedLabels: 3,
|
|
1362
|
+
showToggleAll: true,
|
|
1363
|
+
virtualScroll: false
|
|
1364
|
+
}
|
|
1365
|
+
};
|
|
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: `
|
|
1368
|
+
<ng-container *ngIf="options$ | async as options">
|
|
1369
|
+
<p-multiSelect
|
|
1370
|
+
[formControl]="$any(formControl)"
|
|
1371
|
+
[formlyAttributes]="field"
|
|
1372
|
+
[options]="options"
|
|
1373
|
+
[optionLabel]="props['optionLabel'] || 'label'"
|
|
1374
|
+
[optionValue]="props['optionValue'] || 'value'"
|
|
1375
|
+
[placeholder]="props['placeholder'] || 'Select'"
|
|
1376
|
+
[filter]="props['filter'] || true"
|
|
1377
|
+
[filterBy]="props['filterBy'] || 'label'"
|
|
1378
|
+
[maxSelectedLabels]="props['maxSelectedLabels'] || 2"
|
|
1379
|
+
[showToggleAll]="props['showToggleAll'] || true"
|
|
1380
|
+
[virtualScroll]="props['virtualScroll'] || false"
|
|
1381
|
+
[display]="props['display'] || 'chip'"
|
|
1382
|
+
styleClass="w-full">
|
|
1383
|
+
</p-multiSelect>
|
|
1384
|
+
</ng-container>
|
|
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"] }] });
|
|
1386
|
+
}
|
|
1387
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: MultiselectType, decorators: [{
|
|
1388
|
+
type: Component,
|
|
1389
|
+
args: [{
|
|
1390
|
+
selector: 'formly-field-multiselect',
|
|
1391
|
+
standalone: true,
|
|
1392
|
+
imports: [
|
|
1393
|
+
CommonModule,
|
|
1394
|
+
ReactiveFormsModule,
|
|
1395
|
+
FormlyModule,
|
|
1396
|
+
MultiSelectModule
|
|
1397
|
+
],
|
|
1398
|
+
template: `
|
|
1399
|
+
<ng-container *ngIf="options$ | async as options">
|
|
1400
|
+
<p-multiSelect
|
|
1401
|
+
[formControl]="$any(formControl)"
|
|
1402
|
+
[formlyAttributes]="field"
|
|
1403
|
+
[options]="options"
|
|
1404
|
+
[optionLabel]="props['optionLabel'] || 'label'"
|
|
1405
|
+
[optionValue]="props['optionValue'] || 'value'"
|
|
1406
|
+
[placeholder]="props['placeholder'] || 'Select'"
|
|
1407
|
+
[filter]="props['filter'] || true"
|
|
1408
|
+
[filterBy]="props['filterBy'] || 'label'"
|
|
1409
|
+
[maxSelectedLabels]="props['maxSelectedLabels'] || 2"
|
|
1410
|
+
[showToggleAll]="props['showToggleAll'] || true"
|
|
1411
|
+
[virtualScroll]="props['virtualScroll'] || false"
|
|
1412
|
+
[display]="props['display'] || 'chip'"
|
|
1413
|
+
styleClass="w-full">
|
|
1414
|
+
</p-multiSelect>
|
|
1415
|
+
</ng-container>
|
|
1416
|
+
`
|
|
1417
|
+
}]
|
|
1418
|
+
}] });
|
|
1419
|
+
|
|
1420
|
+
class DateType extends FieldType {
|
|
1421
|
+
defaultOptions = {
|
|
1422
|
+
props: {
|
|
1423
|
+
dateFormat: 'dd-mm-yy',
|
|
1424
|
+
showTime: false,
|
|
1425
|
+
touchUI: false,
|
|
1426
|
+
placeholder: '',
|
|
1427
|
+
showButtonBar: true,
|
|
1428
|
+
}
|
|
1429
|
+
};
|
|
1430
|
+
onDateSelect(event) {
|
|
1431
|
+
this.formatAndSetDate(event);
|
|
1432
|
+
}
|
|
1433
|
+
onDateInput(event) {
|
|
1434
|
+
const inputValue = event.target.value.trim();
|
|
1435
|
+
if (this.isValidDateInput(inputValue, this.props['dateFormat'] || 'dd-mm-yy')) {
|
|
1436
|
+
const parsedDate = this.parseDate(inputValue, this.props['dateFormat'] || 'dd-mm-yy');
|
|
1437
|
+
if (parsedDate) {
|
|
1438
|
+
this.formatAndSetDate(parsedDate);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
else {
|
|
1442
|
+
this.formControl?.setErrors({ invalidDate: true });
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
formatAndSetDate(date) {
|
|
1446
|
+
if (this.formControl) {
|
|
1447
|
+
const formattedDate = formatDate(date, 'dd-MM-yyyy', 'en-US');
|
|
1448
|
+
this.formControl.setValue(formattedDate);
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
parseDate(value, format) {
|
|
1452
|
+
const parts = value.split(/[-/]/); // Split by common delimiters
|
|
1453
|
+
if (parts.length === 3) {
|
|
1454
|
+
const [day, month, year] = format === 'dd-mm-yy'
|
|
1455
|
+
? [parseInt(parts[0], 10), parseInt(parts[1], 10) - 1, parseInt(parts[2], 10)]
|
|
1456
|
+
: [parseInt(parts[1], 10), parseInt(parts[0], 10) - 1, parseInt(parts[2], 10)];
|
|
1457
|
+
return new Date(year, month, day);
|
|
1458
|
+
}
|
|
1459
|
+
return null;
|
|
1460
|
+
}
|
|
1461
|
+
isValidDateInput(value, format) {
|
|
1462
|
+
const dateRegex = format === 'dd-mm-yy'
|
|
1463
|
+
? /^\d{2}-\d{2}-\d{4}$/
|
|
1464
|
+
: /^\d{2}\/\d{2}\/\d{4}$/;
|
|
1465
|
+
return dateRegex.test(value);
|
|
1466
|
+
}
|
|
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: `
|
|
1469
|
+
<p-datepicker
|
|
1470
|
+
[formControl]="$any(formControl)"
|
|
1471
|
+
[formlyAttributes]="field"
|
|
1472
|
+
[showIcon]="true"
|
|
1473
|
+
[dateFormat]="props['dateFormat'] || 'dd-mm-yy'"
|
|
1474
|
+
[showTime]="props['showTime'] || false"
|
|
1475
|
+
[touchUI]="props['touchUI'] || false"
|
|
1476
|
+
[placeholder]="props['placeholder'] || ''"
|
|
1477
|
+
[minDate]="props['minDate']"
|
|
1478
|
+
[maxDate]="props['maxDate']"
|
|
1479
|
+
[showOnFocus]="false"
|
|
1480
|
+
[todayButtonStyleClass]="'p-button-secondary'"
|
|
1481
|
+
[clearButtonStyleClass]="'p-button-secondary'"
|
|
1482
|
+
styleClass="w-full"
|
|
1483
|
+
(onSelect)="onDateSelect($event)"
|
|
1484
|
+
(onInput)="onDateInput($event)">
|
|
1485
|
+
</p-datepicker>
|
|
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"] }] });
|
|
1487
|
+
}
|
|
1488
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: DateType, decorators: [{
|
|
1489
|
+
type: Component,
|
|
1490
|
+
args: [{
|
|
1491
|
+
selector: 'formly-field-date',
|
|
1492
|
+
standalone: true,
|
|
1493
|
+
imports: [
|
|
1494
|
+
CommonModule,
|
|
1495
|
+
ReactiveFormsModule,
|
|
1496
|
+
FormlyModule,
|
|
1497
|
+
DatePickerModule
|
|
1498
|
+
],
|
|
1499
|
+
template: `
|
|
1500
|
+
<p-datepicker
|
|
1501
|
+
[formControl]="$any(formControl)"
|
|
1502
|
+
[formlyAttributes]="field"
|
|
1503
|
+
[showIcon]="true"
|
|
1504
|
+
[dateFormat]="props['dateFormat'] || 'dd-mm-yy'"
|
|
1505
|
+
[showTime]="props['showTime'] || false"
|
|
1506
|
+
[touchUI]="props['touchUI'] || false"
|
|
1507
|
+
[placeholder]="props['placeholder'] || ''"
|
|
1508
|
+
[minDate]="props['minDate']"
|
|
1509
|
+
[maxDate]="props['maxDate']"
|
|
1510
|
+
[showOnFocus]="false"
|
|
1511
|
+
[todayButtonStyleClass]="'p-button-secondary'"
|
|
1512
|
+
[clearButtonStyleClass]="'p-button-secondary'"
|
|
1513
|
+
styleClass="w-full"
|
|
1514
|
+
(onSelect)="onDateSelect($event)"
|
|
1515
|
+
(onInput)="onDateInput($event)">
|
|
1516
|
+
</p-datepicker>
|
|
1517
|
+
`
|
|
1518
|
+
}]
|
|
1519
|
+
}] });
|
|
1520
|
+
|
|
1521
|
+
class CheckboxType extends FieldType {
|
|
1522
|
+
defaultOptions = {
|
|
1523
|
+
props: {
|
|
1524
|
+
trueValue: true,
|
|
1525
|
+
falseValue: false
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
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: `
|
|
1530
|
+
<div class="checkbox-container">
|
|
1531
|
+
<p-checkbox
|
|
1532
|
+
[formControl]="$any(formControl)"
|
|
1533
|
+
[formlyAttributes]="field"
|
|
1534
|
+
[binary]="!props['options']"
|
|
1535
|
+
[trueValue]="props['trueValue']"
|
|
1536
|
+
[falseValue]="props['falseValue']">
|
|
1537
|
+
{{props['checkboxLabel'] || ''}}
|
|
1538
|
+
</p-checkbox>
|
|
1539
|
+
</div>
|
|
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"] }] });
|
|
1541
|
+
}
|
|
1542
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: CheckboxType, decorators: [{
|
|
1543
|
+
type: Component,
|
|
1544
|
+
args: [{ selector: 'formly-field-checkbox', standalone: true, imports: [
|
|
1545
|
+
CommonModule,
|
|
1546
|
+
ReactiveFormsModule,
|
|
1547
|
+
FormlyModule,
|
|
1548
|
+
CheckboxModule
|
|
1549
|
+
], template: `
|
|
1550
|
+
<div class="checkbox-container">
|
|
1551
|
+
<p-checkbox
|
|
1552
|
+
[formControl]="$any(formControl)"
|
|
1553
|
+
[formlyAttributes]="field"
|
|
1554
|
+
[binary]="!props['options']"
|
|
1555
|
+
[trueValue]="props['trueValue']"
|
|
1556
|
+
[falseValue]="props['falseValue']">
|
|
1557
|
+
{{props['checkboxLabel'] || ''}}
|
|
1558
|
+
</p-checkbox>
|
|
1559
|
+
</div>
|
|
1560
|
+
`, styles: [".checkbox-container{display:flex;align-items:center;height:40px}\n"] }]
|
|
1561
|
+
}] });
|
|
1562
|
+
|
|
1563
|
+
class TextareaType extends FieldType {
|
|
1564
|
+
defaultOptions = {
|
|
1565
|
+
props: {
|
|
1566
|
+
rows: 3,
|
|
1567
|
+
cols: undefined,
|
|
1568
|
+
autoResize: true,
|
|
1569
|
+
placeholder: ''
|
|
1570
|
+
}
|
|
1571
|
+
};
|
|
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: `
|
|
1574
|
+
<textarea pInputTextarea
|
|
1575
|
+
[formControl]="$any(formControl)"
|
|
1576
|
+
[formlyAttributes]="field"
|
|
1577
|
+
[rows]="props['rows'] || 3"
|
|
1578
|
+
[cols]="props['cols']"
|
|
1579
|
+
[autoResize]="props['autoResize']"
|
|
1580
|
+
[placeholder]="props['placeholder'] || ''"
|
|
1581
|
+
class="w-full">
|
|
1582
|
+
</textarea>
|
|
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"] }] });
|
|
1584
|
+
}
|
|
1585
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TextareaType, decorators: [{
|
|
1586
|
+
type: Component,
|
|
1587
|
+
args: [{
|
|
1588
|
+
selector: 'formly-field-textarea',
|
|
1589
|
+
standalone: true,
|
|
1590
|
+
imports: [
|
|
1591
|
+
CommonModule,
|
|
1592
|
+
ReactiveFormsModule,
|
|
1593
|
+
FormlyModule,
|
|
1594
|
+
Textarea
|
|
1595
|
+
],
|
|
1596
|
+
template: `
|
|
1597
|
+
<textarea pInputTextarea
|
|
1598
|
+
[formControl]="$any(formControl)"
|
|
1599
|
+
[formlyAttributes]="field"
|
|
1600
|
+
[rows]="props['rows'] || 3"
|
|
1601
|
+
[cols]="props['cols']"
|
|
1602
|
+
[autoResize]="props['autoResize']"
|
|
1603
|
+
[placeholder]="props['placeholder'] || ''"
|
|
1604
|
+
class="w-full">
|
|
1605
|
+
</textarea>
|
|
1606
|
+
`
|
|
1607
|
+
}]
|
|
1608
|
+
}] });
|
|
1609
|
+
|
|
1610
|
+
class FormFieldWrapper extends FieldWrapper {
|
|
1611
|
+
get errorMessage() {
|
|
1612
|
+
if (!this.field.formControl?.errors) {
|
|
1613
|
+
return '';
|
|
1614
|
+
}
|
|
1615
|
+
const firstErrorKey = Object.keys(this.field.formControl.errors)[0];
|
|
1616
|
+
return this.props.validation?.messages?.[firstErrorKey] || 'This field is required';
|
|
1617
|
+
}
|
|
1618
|
+
get showError() {
|
|
1619
|
+
if (!this.field.formControl) {
|
|
1620
|
+
return false;
|
|
1621
|
+
}
|
|
1622
|
+
const { touched, dirty, invalid } = this.field.formControl;
|
|
1623
|
+
return invalid && (touched || dirty);
|
|
1624
|
+
}
|
|
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: `
|
|
1627
|
+
<div class="field mb-4">
|
|
1628
|
+
<label *ngIf="props.label" [for]="id" class="font-medium">
|
|
1629
|
+
{{ props.label }}
|
|
1630
|
+
<span class="required-star" *ngIf="props.required">*</span>
|
|
1631
|
+
<i *ngIf="props.tooltip"
|
|
1632
|
+
class="pi pi-info-circle ml-2"
|
|
1633
|
+
[pTooltip]="props.tooltip">
|
|
1634
|
+
</i>
|
|
1635
|
+
</label>
|
|
1636
|
+
|
|
1637
|
+
<div [class.p-input-error]="showError">
|
|
1638
|
+
<ng-container #fieldComponent></ng-container>
|
|
1639
|
+
</div>
|
|
1640
|
+
|
|
1641
|
+
<small *ngIf="props.description" class="block mt-1">
|
|
1642
|
+
{{ props.description }}
|
|
1643
|
+
</small>
|
|
1644
|
+
|
|
1645
|
+
<small *ngIf="showError" class="p-error block">
|
|
1646
|
+
<i class="pi pi-exclamation-circle mr-2"></i>
|
|
1647
|
+
{{ errorMessage }}
|
|
1648
|
+
</small>
|
|
1649
|
+
</div>
|
|
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"] }] });
|
|
1651
|
+
}
|
|
1652
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: FormFieldWrapper, decorators: [{
|
|
1653
|
+
type: Component,
|
|
1654
|
+
args: [{ selector: 'formly-wrapper-form-field', standalone: true, imports: [CommonModule, TooltipModule], template: `
|
|
1655
|
+
<div class="field mb-4">
|
|
1656
|
+
<label *ngIf="props.label" [for]="id" class="font-medium">
|
|
1657
|
+
{{ props.label }}
|
|
1658
|
+
<span class="required-star" *ngIf="props.required">*</span>
|
|
1659
|
+
<i *ngIf="props.tooltip"
|
|
1660
|
+
class="pi pi-info-circle ml-2"
|
|
1661
|
+
[pTooltip]="props.tooltip">
|
|
1662
|
+
</i>
|
|
1663
|
+
</label>
|
|
1664
|
+
|
|
1665
|
+
<div [class.p-input-error]="showError">
|
|
1666
|
+
<ng-container #fieldComponent></ng-container>
|
|
1667
|
+
</div>
|
|
1668
|
+
|
|
1669
|
+
<small *ngIf="props.description" class="block mt-1">
|
|
1670
|
+
{{ props.description }}
|
|
1671
|
+
</small>
|
|
1672
|
+
|
|
1673
|
+
<small *ngIf="showError" class="p-error block">
|
|
1674
|
+
<i class="pi pi-exclamation-circle mr-2"></i>
|
|
1675
|
+
{{ errorMessage }}
|
|
1676
|
+
</small>
|
|
1677
|
+
</div>
|
|
1678
|
+
`, 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"] }]
|
|
1679
|
+
}] });
|
|
1680
|
+
|
|
1681
|
+
const FORMLY_TYPES = {
|
|
1682
|
+
TEXT: 'text',
|
|
1683
|
+
NUMBER: 'number',
|
|
1684
|
+
DATE: 'date',
|
|
1685
|
+
CHECKBOX: 'checkbox',
|
|
1686
|
+
RADIO: 'radio',
|
|
1687
|
+
SELECT: 'select',
|
|
1688
|
+
MULTISELECT: 'multiselect',
|
|
1689
|
+
TEXTAREA: 'textarea'
|
|
1690
|
+
};
|
|
1691
|
+
const FORMLY_WRAPPERS = {
|
|
1692
|
+
FORM_FIELD: 'form-field'
|
|
1693
|
+
};
|
|
1694
|
+
|
|
1695
|
+
class CoreFormlyModule {
|
|
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({
|
|
1699
|
+
types: [
|
|
1700
|
+
{ name: FORMLY_TYPES.TEXT, component: TextInputType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
1701
|
+
{ name: FORMLY_TYPES.NUMBER, component: NumericInputType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
1702
|
+
{ name: FORMLY_TYPES.DATE, component: DateType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
1703
|
+
{ name: FORMLY_TYPES.CHECKBOX, component: CheckboxType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
1704
|
+
{ name: FORMLY_TYPES.RADIO, component: RadioType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
1705
|
+
{ name: FORMLY_TYPES.SELECT, component: SelectType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
1706
|
+
{ name: FORMLY_TYPES.MULTISELECT, component: MultiselectType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
1707
|
+
{ name: FORMLY_TYPES.TEXTAREA, component: TextareaType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] }, // Added textarea type
|
|
1708
|
+
],
|
|
1709
|
+
wrappers: [
|
|
1710
|
+
{ name: FORMLY_WRAPPERS.FORM_FIELD, component: FormFieldWrapper }
|
|
1711
|
+
]
|
|
1712
|
+
}), FormlyModule] });
|
|
1713
|
+
}
|
|
1714
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: CoreFormlyModule, decorators: [{
|
|
1715
|
+
type: NgModule,
|
|
1716
|
+
args: [{
|
|
1717
|
+
imports: [
|
|
1718
|
+
FormlyModule.forRoot({
|
|
1719
|
+
types: [
|
|
1720
|
+
{ name: FORMLY_TYPES.TEXT, component: TextInputType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
1721
|
+
{ name: FORMLY_TYPES.NUMBER, component: NumericInputType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
1722
|
+
{ name: FORMLY_TYPES.DATE, component: DateType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
1723
|
+
{ name: FORMLY_TYPES.CHECKBOX, component: CheckboxType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
1724
|
+
{ name: FORMLY_TYPES.RADIO, component: RadioType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
1725
|
+
{ name: FORMLY_TYPES.SELECT, component: SelectType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
1726
|
+
{ name: FORMLY_TYPES.MULTISELECT, component: MultiselectType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
1727
|
+
{ name: FORMLY_TYPES.TEXTAREA, component: TextareaType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] }, // Added textarea type
|
|
1728
|
+
],
|
|
1729
|
+
wrappers: [
|
|
1730
|
+
{ name: FORMLY_WRAPPERS.FORM_FIELD, component: FormFieldWrapper }
|
|
1731
|
+
]
|
|
1732
|
+
})
|
|
1733
|
+
],
|
|
1734
|
+
exports: [FormlyModule]
|
|
1735
|
+
}]
|
|
1736
|
+
}] });
|
|
1737
|
+
|
|
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 = [];
|
|
1139
2051
|
rows = 10;
|
|
1140
2052
|
totalRecords = 0;
|
|
1141
2053
|
filters = [];
|
|
1142
2054
|
subscriptions = [];
|
|
1143
|
-
constructor(
|
|
2055
|
+
constructor(utilsService, notificationService) {
|
|
1144
2056
|
super(utilsService, notificationService);
|
|
1145
|
-
this.router = router;
|
|
1146
|
-
this.notificationService = notificationService;
|
|
1147
2057
|
this.utilsService = utilsService;
|
|
1148
|
-
this.
|
|
2058
|
+
this.notificationService = notificationService;
|
|
1149
2059
|
}
|
|
1150
2060
|
/**
|
|
1151
|
-
* Initialize the component
|
|
1152
|
-
* and load initial data.
|
|
2061
|
+
* Initialize the component and load initial data
|
|
1153
2062
|
*/
|
|
1154
2063
|
ngOnInit() {
|
|
1155
2064
|
// Subscribe to loading state if available
|
|
@@ -1179,16 +2088,51 @@ class BaseListPageComponent extends BasePageComponent {
|
|
|
1179
2088
|
* Load data with pagination and sorting
|
|
1180
2089
|
* @param event The lazy load event from PrimeNG table
|
|
1181
2090
|
*/
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
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
|
+
}
|
|
1192
2136
|
/**
|
|
1193
2137
|
* Convert PrimeNG filters to backend format with proper operator mapping
|
|
1194
2138
|
*/
|
|
@@ -1260,136 +2204,6 @@ class BaseListPageComponent extends BasePageComponent {
|
|
|
1260
2204
|
};
|
|
1261
2205
|
return operatorMap[operator] || 'EQUALS'; // Default to EQUALS if operator not found
|
|
1262
2206
|
}
|
|
1263
|
-
loadData(event) {
|
|
1264
|
-
console.log('LoadData called with event:', event);
|
|
1265
|
-
this.loading = true;
|
|
1266
|
-
// Create page request with default values
|
|
1267
|
-
const pageRequest = {
|
|
1268
|
-
page: 0,
|
|
1269
|
-
size: 10,
|
|
1270
|
-
sorts: [],
|
|
1271
|
-
filters: []
|
|
1272
|
-
};
|
|
1273
|
-
if (event) {
|
|
1274
|
-
// Handle pagination
|
|
1275
|
-
if (event.first !== undefined && event.rows) {
|
|
1276
|
-
pageRequest.page = Math.floor(event.first / event.rows);
|
|
1277
|
-
pageRequest.size = event.rows;
|
|
1278
|
-
}
|
|
1279
|
-
// Handle sorting
|
|
1280
|
-
if (event.sortField) {
|
|
1281
|
-
pageRequest.sorts = [{
|
|
1282
|
-
field: Array.isArray(event.sortField) ? event.sortField[0] : event.sortField,
|
|
1283
|
-
direction: event.sortOrder === 1 ? 'asc' : 'desc'
|
|
1284
|
-
}];
|
|
1285
|
-
}
|
|
1286
|
-
// Handle filtering
|
|
1287
|
-
if (event.filters) {
|
|
1288
|
-
const convertedFilters = this.convertFilters(event.filters);
|
|
1289
|
-
console.log('Converted filters:', convertedFilters);
|
|
1290
|
-
pageRequest.filters = convertedFilters;
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
console.log('Sending page request:', pageRequest);
|
|
1294
|
-
this.getEntityService().getAll(pageRequest)
|
|
1295
|
-
.pipe(finalize$1(() => this.loading = false))
|
|
1296
|
-
.subscribe({
|
|
1297
|
-
next: (response) => {
|
|
1298
|
-
this.items = [...response.content];
|
|
1299
|
-
this.totalRecords = response.page.totalElements;
|
|
1300
|
-
},
|
|
1301
|
-
error: (error) => {
|
|
1302
|
-
console.error('Failed to load data:', error);
|
|
1303
|
-
this.showError(`${AppMessages.LIST.ERROR.LOAD} ${this.getEntityName()}`);
|
|
1304
|
-
}
|
|
1305
|
-
});
|
|
1306
|
-
}
|
|
1307
|
-
/**
|
|
1308
|
-
* Navigate to create a new entity
|
|
1309
|
-
*/
|
|
1310
|
-
onCreate() {
|
|
1311
|
-
// Navigate to the form route and pass the mode as a query parameter
|
|
1312
|
-
this.navigateTo([this.getFormRoute()], { [AppConstants.FORM.MODE]: AppConstants.PAGE_MODE.CREATE });
|
|
1313
|
-
}
|
|
1314
|
-
/**
|
|
1315
|
-
* Navigate to view an entity
|
|
1316
|
-
* @param item The entity to view
|
|
1317
|
-
*/
|
|
1318
|
-
onView(item) {
|
|
1319
|
-
if (!this.validateItemUuid(item, 'view'))
|
|
1320
|
-
return;
|
|
1321
|
-
// Correct: Navigate to form route, pass mode and uuid as query params
|
|
1322
|
-
this.navigateTo([this.getFormRoute()], {
|
|
1323
|
-
[AppConstants.FORM.MODE]: AppConstants.PAGE_MODE.VIEW,
|
|
1324
|
-
[AppConstants.FORM.UUID]: item.uuid
|
|
1325
|
-
});
|
|
1326
|
-
}
|
|
1327
|
-
/**
|
|
1328
|
-
* Navigate to edit an entity
|
|
1329
|
-
* @param item The entity to edit
|
|
1330
|
-
*/
|
|
1331
|
-
onEdit(item) {
|
|
1332
|
-
if (!this.validateItemUuid(item, 'edit'))
|
|
1333
|
-
return;
|
|
1334
|
-
// Correct: Navigate to form route, pass mode and uuid as query params
|
|
1335
|
-
this.navigateTo([this.getFormRoute()], {
|
|
1336
|
-
[AppConstants.FORM.MODE]: AppConstants.PAGE_MODE.EDIT,
|
|
1337
|
-
[AppConstants.FORM.UUID]: item.uuid
|
|
1338
|
-
});
|
|
1339
|
-
}
|
|
1340
|
-
/**
|
|
1341
|
-
* Navigate to a route with error handling
|
|
1342
|
-
* @param path The route path
|
|
1343
|
-
* @param matrixParams Optional matrix parameters
|
|
1344
|
-
*/
|
|
1345
|
-
navigateTo(path, matrixParams) {
|
|
1346
|
-
this.router.navigate(path, { queryParams: matrixParams })
|
|
1347
|
-
.catch(error => {
|
|
1348
|
-
console.error('Navigation error:', error);
|
|
1349
|
-
this.showError(AppMessages.LIST.ERROR.NAVIGATION);
|
|
1350
|
-
});
|
|
1351
|
-
}
|
|
1352
|
-
/**
|
|
1353
|
-
* Delete an entity with confirmation
|
|
1354
|
-
* @param item The entity to delete
|
|
1355
|
-
*/
|
|
1356
|
-
onDelete(item) {
|
|
1357
|
-
if (!this.validateItemUuid(item, 'delete'))
|
|
1358
|
-
return;
|
|
1359
|
-
this.confirmationService.confirm({
|
|
1360
|
-
message: `Are you sure you want to delete this ${this.getEntityName()}?`,
|
|
1361
|
-
header: 'Confirm Deletion',
|
|
1362
|
-
icon: 'pi pi-exclamation-triangle',
|
|
1363
|
-
accept: () => {
|
|
1364
|
-
this.actionLoading = true;
|
|
1365
|
-
this.getEntityService().delete(item.uuid)
|
|
1366
|
-
.pipe(finalize$1(() => this.actionLoading = false))
|
|
1367
|
-
.subscribe({
|
|
1368
|
-
next: () => {
|
|
1369
|
-
this.showSuccess(`${this.getEntityName()} ${AppMessages.LIST.SUCCESS.DELETE}`);
|
|
1370
|
-
this.loadData();
|
|
1371
|
-
},
|
|
1372
|
-
error: (error) => {
|
|
1373
|
-
console.error('Delete failed:', error);
|
|
1374
|
-
this.showError(`${AppMessages.LIST.ERROR.DELETE} ${this.getEntityName()}`);
|
|
1375
|
-
}
|
|
1376
|
-
});
|
|
1377
|
-
}
|
|
1378
|
-
});
|
|
1379
|
-
}
|
|
1380
|
-
/**
|
|
1381
|
-
* Validate that an entity has a UUID
|
|
1382
|
-
* @param item The entity to validate
|
|
1383
|
-
* @param action The action being performed
|
|
1384
|
-
* @returns true if valid, false otherwise
|
|
1385
|
-
*/
|
|
1386
|
-
validateItemUuid(item, action) {
|
|
1387
|
-
if (!item?.uuid) {
|
|
1388
|
-
this.showError(`${AppMessages.LIST.ERROR.INVALID_ID} ${action}`);
|
|
1389
|
-
return false;
|
|
1390
|
-
}
|
|
1391
|
-
return true;
|
|
1392
|
-
}
|
|
1393
2207
|
/**
|
|
1394
2208
|
* Get an observable for loading state from the entity service
|
|
1395
2209
|
* @returns An observable of loading state if available, null otherwise
|
|
@@ -1398,1002 +2212,782 @@ class BaseListPageComponent extends BasePageComponent {
|
|
|
1398
2212
|
const service = this.getEntityService();
|
|
1399
2213
|
return service.loading$ || null;
|
|
1400
2214
|
}
|
|
1401
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type:
|
|
1402
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.6", type:
|
|
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 });
|
|
1403
2217
|
}
|
|
1404
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type:
|
|
2218
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTableComponent, decorators: [{
|
|
1405
2219
|
type: Directive
|
|
1406
|
-
}], ctorParameters: () => [{ type:
|
|
2220
|
+
}], ctorParameters: () => [{ type: UtilsService }, { type: NotificationService }], propDecorators: { table: [{
|
|
1407
2221
|
type: ViewChild,
|
|
1408
2222
|
args: ['dt']
|
|
1409
2223
|
}] } });
|
|
1410
2224
|
|
|
1411
|
-
|
|
1412
|
-
|
|
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 {
|
|
1413
2231
|
router;
|
|
1414
2232
|
notificationService;
|
|
1415
2233
|
utilsService;
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
formType = '';
|
|
1420
|
-
// Form properties
|
|
1421
|
-
isNew = true;
|
|
1422
|
-
mode = AppConstants.PAGE_MODE.CREATE;
|
|
1423
|
-
currentFormData;
|
|
1424
|
-
originalFormData = null;
|
|
1425
|
-
hasChanges = false;
|
|
1426
|
-
// debug method removed
|
|
1427
|
-
constructor(route, router, notificationService, utilsService) {
|
|
2234
|
+
confirmationService;
|
|
2235
|
+
actionLoading = false;
|
|
2236
|
+
constructor(router, notificationService, utilsService, confirmationService) {
|
|
1428
2237
|
super(utilsService, notificationService);
|
|
1429
|
-
this.route = route;
|
|
1430
2238
|
this.router = router;
|
|
1431
2239
|
this.notificationService = notificationService;
|
|
1432
2240
|
this.utilsService = utilsService;
|
|
2241
|
+
this.confirmationService = confirmationService;
|
|
1433
2242
|
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
this.route.queryParams.pipe(take(1)).subscribe({
|
|
1441
|
-
next: params => this.initializeForm(params),
|
|
1442
|
-
error: error => {
|
|
1443
|
-
console.error('Error initializing form:', error);
|
|
1444
|
-
this.showError(AppMessages.FORM.ERROR.INIT);
|
|
1445
|
-
}
|
|
1446
|
-
});
|
|
1447
|
-
}
|
|
1448
|
-
catch (error) {
|
|
1449
|
-
console.error('Error in ngOnInit:', error);
|
|
1450
|
-
this.showError(AppMessages.FORM.ERROR.INIT);
|
|
1451
|
-
}
|
|
1452
|
-
}
|
|
1453
|
-
initializeFormData() {
|
|
1454
|
-
console.log('[BaseTabbedFormComponent] Initializing form data');
|
|
1455
|
-
console.log('[BaseTabbedFormComponent] Current mode:', this.mode);
|
|
1456
|
-
if (this.mode === AppConstants.PAGE_MODE.CREATE) {
|
|
1457
|
-
console.log('[BaseTabbedFormComponent] Create mode, using defaults');
|
|
1458
|
-
this.loadDefaultTab();
|
|
1459
|
-
return;
|
|
1460
|
-
}
|
|
1461
|
-
const state = {
|
|
1462
|
-
...this.router.getCurrentNavigation()?.extras?.state,
|
|
1463
|
-
...window.history.state
|
|
1464
|
-
};
|
|
1465
|
-
console.log('[BaseTabbedFormComponent] Combined state:', state);
|
|
1466
|
-
if (state?.formData) {
|
|
1467
|
-
console.log('[BaseTabbedFormComponent] Found form data in state:', state.formData);
|
|
1468
|
-
this.patchFormData(state.formData);
|
|
1469
|
-
this.loadDefaultTab();
|
|
1470
|
-
}
|
|
1471
|
-
else {
|
|
1472
|
-
console.log('[BaseTabbedFormComponent] No form data, loading from UUID');
|
|
1473
|
-
this.loadFromUuid();
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1476
|
-
loadFromUuid() {
|
|
1477
|
-
// Flatten nested subscriptions using RxJS operators
|
|
1478
|
-
this.route.queryParams.pipe(take(1), map((params) => {
|
|
1479
|
-
const uuid = params[AppConstants.FORM.UUID];
|
|
1480
|
-
if (!uuid) {
|
|
1481
|
-
throw new Error(`UUID is required for ${this.mode} mode when no formData is provided`);
|
|
1482
|
-
}
|
|
1483
|
-
return uuid;
|
|
1484
|
-
})).subscribe({
|
|
1485
|
-
next: (uuid) => this.loadEntity(uuid),
|
|
1486
|
-
error: (error) => {
|
|
1487
|
-
console.error('Error loading UUID:', error);
|
|
1488
|
-
this.showError(`${AppMessages.FORM.ERROR.LOAD} UUID`);
|
|
1489
|
-
}
|
|
1490
|
-
});
|
|
1491
|
-
}
|
|
1492
|
-
initializeForm(params) {
|
|
1493
|
-
const routeMode = params[AppConstants.FORM.MODE];
|
|
1494
|
-
try {
|
|
1495
|
-
this.initializeFormMode(routeMode);
|
|
1496
|
-
this.setupFormState();
|
|
1497
|
-
if (this.formType === TabbedFormType.LIST) {
|
|
1498
|
-
this.initializeListForm(params);
|
|
1499
|
-
}
|
|
1500
|
-
else {
|
|
1501
|
-
// Existing logic for STANDARD form type
|
|
1502
|
-
this.initializeFormData();
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
catch (error) {
|
|
1506
|
-
console.error('Error initializing form:', error);
|
|
1507
|
-
this.showError(`${AppMessages.FORM.ERROR.INIT}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
patchFormData(data) {
|
|
1511
|
-
if (data) {
|
|
1512
|
-
this.currentFormData = { ...data };
|
|
1513
|
-
this.originalFormData = { ...data }; // Create a deep copy to avoid reference issues
|
|
1514
|
-
this.isNew = false;
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
initializeFormMode(routeMode) {
|
|
1518
|
-
if (!routeMode) {
|
|
1519
|
-
this.mode = AppConstants.PAGE_MODE.CREATE;
|
|
1520
|
-
return;
|
|
1521
|
-
}
|
|
1522
|
-
// Validate mode more explicitly
|
|
1523
|
-
if (!this.isValidPageMode(routeMode)) {
|
|
1524
|
-
const validModes = Object.values(AppConstants.PAGE_MODE).join(', ');
|
|
1525
|
-
throw new Error(`Invalid page mode: ${routeMode}. Valid modes are: ${validModes}`);
|
|
1526
|
-
}
|
|
1527
|
-
this.mode = routeMode;
|
|
1528
|
-
}
|
|
1529
|
-
setupFormState() {
|
|
1530
|
-
console.log('[BaseTabbedFormComponent] Setting up form state, mode:', this.mode);
|
|
1531
|
-
if (this.mode === AppConstants.PAGE_MODE.CREATE) {
|
|
1532
|
-
console.log('[BaseTabbedFormComponent] Create mode setup');
|
|
1533
|
-
this.isNew = true;
|
|
1534
|
-
this.loading = false;
|
|
1535
|
-
this.originalFormData = null;
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
loadEntity(uuid) {
|
|
1539
|
-
this.loading = true;
|
|
1540
|
-
this.isNew = false;
|
|
1541
|
-
const entityName = this.getEntityName();
|
|
1542
|
-
this.getEntityService().getById(uuid).subscribe({
|
|
1543
|
-
next: (data) => {
|
|
1544
|
-
this.originalFormData = data;
|
|
1545
|
-
this.currentFormData = { ...data };
|
|
1546
|
-
this.loading = false;
|
|
1547
|
-
console.log('[BaseTabbedFormComponent] Original data loaded:', this.originalFormData);
|
|
1548
|
-
console.log('[BaseTabbedFormComponent] Form data updated:', this.currentFormData);
|
|
1549
|
-
this.loadDefaultTab();
|
|
1550
|
-
},
|
|
1551
|
-
error: (error) => {
|
|
1552
|
-
console.log('[BaseTabbedFormComponent] Error loading entity:', error);
|
|
1553
|
-
this.showError(`${AppMessages.FORM.ERROR.LOAD} ${entityName}`);
|
|
1554
|
-
this.loading = false;
|
|
1555
|
-
}
|
|
1556
|
-
});
|
|
1557
|
-
}
|
|
1558
|
-
findTabByValue(value) {
|
|
1559
|
-
return this.tabs.find(tab => tab.value === value);
|
|
1560
|
-
}
|
|
1561
|
-
loadDefaultTab() {
|
|
1562
|
-
console.log('[BaseTabbedFormComponent] Loading default tab');
|
|
1563
|
-
const defaultTab = this.tabs.find(tab => tab.isParentModel) || this.tabs[0];
|
|
1564
|
-
if (!defaultTab)
|
|
1565
|
-
return;
|
|
1566
|
-
if (this.formType === TabbedFormType.LIST) {
|
|
1567
|
-
const state = this.router.getCurrentNavigation()?.extras?.state || window.history.state;
|
|
1568
|
-
const formDataList = state?.formDataList || [];
|
|
1569
|
-
const selectedData = formDataList.find((data) => data.uuid === defaultTab.value);
|
|
1570
|
-
if (selectedData) {
|
|
1571
|
-
this.currentFormData = selectedData;
|
|
1572
|
-
this.originalFormData = { ...selectedData };
|
|
1573
|
-
this.patchFormData(selectedData);
|
|
1574
|
-
}
|
|
1575
|
-
this.router.navigate([defaultTab.value], {
|
|
1576
|
-
relativeTo: this.route,
|
|
1577
|
-
state: {
|
|
1578
|
-
formData: selectedData,
|
|
1579
|
-
formDataList: formDataList,
|
|
1580
|
-
context: ComponentContext.TABBED
|
|
1581
|
-
}
|
|
1582
|
-
});
|
|
1583
|
-
}
|
|
1584
|
-
else if (this.formType === TabbedFormType.STANDARD) {
|
|
1585
|
-
// For STANDARD type, just navigate to the tab route with the parent form data
|
|
1586
|
-
this.router.navigate([defaultTab.value], {
|
|
1587
|
-
relativeTo: this.route,
|
|
1588
|
-
queryParamsHandling: 'preserve', // Keep existing query params (mode & uuid)
|
|
1589
|
-
state: {
|
|
1590
|
-
formData: defaultTab.isParentModel ? this.currentFormData : null,
|
|
1591
|
-
parentFormData: this.currentFormData, // Pass the parent form data for child forms
|
|
1592
|
-
context: ComponentContext.TABBED
|
|
1593
|
-
}
|
|
1594
|
-
});
|
|
1595
|
-
}
|
|
1596
|
-
else {
|
|
1597
|
-
// Default behavior for other form types
|
|
1598
|
-
this.router.navigate([defaultTab.value], {
|
|
1599
|
-
relativeTo: this.route,
|
|
1600
|
-
queryParamsHandling: 'preserve', // Keep existing query params (mode & uuid)
|
|
1601
|
-
state: {
|
|
1602
|
-
formData: this.currentFormData,
|
|
1603
|
-
context: ComponentContext.TABBED
|
|
1604
|
-
}
|
|
1605
|
-
});
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
getTabFormData(tabConfig) {
|
|
1609
|
-
if (!tabConfig)
|
|
1610
|
-
return null;
|
|
1611
|
-
if (tabConfig.isParentModel) {
|
|
1612
|
-
return this.currentFormData;
|
|
1613
|
-
}
|
|
1614
|
-
const fieldName = this.utilsService.toCamelCase(tabConfig.value);
|
|
1615
|
-
// Use type assertion since we know this is a valid field
|
|
1616
|
-
return this.currentFormData[fieldName];
|
|
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 });
|
|
1617
2249
|
}
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
//
|
|
1626
|
-
this.
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
this.originalFormData = formDataList[0];
|
|
1630
|
-
// Navigate to the first tab
|
|
1631
|
-
this.router.navigate([this.activeTab], {
|
|
1632
|
-
relativeTo: this.route,
|
|
1633
|
-
state: {
|
|
1634
|
-
formData: this.currentFormData,
|
|
1635
|
-
formDataList: formDataList,
|
|
1636
|
-
context: ComponentContext.TABBED
|
|
1637
|
-
}
|
|
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
|
|
1638
2261
|
});
|
|
1639
|
-
// Ensure the form data is updated after navigation
|
|
1640
|
-
this.patchFormData(this.currentFormData);
|
|
1641
2262
|
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
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
|
+
});
|
|
1648
2275
|
}
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
2276
|
+
/**
|
|
2277
|
+
* Delete an entity with confirmation
|
|
2278
|
+
* @param item The entity to delete
|
|
2279
|
+
*/
|
|
2280
|
+
onDelete(item) {
|
|
2281
|
+
if (!this.validateItemUuid(item, 'delete'))
|
|
1653
2282
|
return;
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
context: ComponentContext.TABBED
|
|
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()}`);
|
|
1671
2299
|
}
|
|
1672
2300
|
});
|
|
1673
2301
|
}
|
|
1674
|
-
}
|
|
1675
|
-
else if (this.formType === TabbedFormType.STANDARD) {
|
|
1676
|
-
// For STANDARD type, navigate to the tab route with the parent form data
|
|
1677
|
-
this.router.navigate([this.activeTab], {
|
|
1678
|
-
relativeTo: this.route,
|
|
1679
|
-
queryParamsHandling: 'preserve',
|
|
1680
|
-
state: {
|
|
1681
|
-
formData: tabConfig.isParentModel ? this.currentFormData : null,
|
|
1682
|
-
parentFormData: this.currentFormData,
|
|
1683
|
-
context: ComponentContext.TABBED
|
|
1684
|
-
}
|
|
1685
|
-
});
|
|
1686
|
-
}
|
|
1687
|
-
else {
|
|
1688
|
-
// Default behavior for other form types
|
|
1689
|
-
const formData = this.getTabFormData(tabConfig);
|
|
1690
|
-
this.router.navigate([this.activeTab], {
|
|
1691
|
-
relativeTo: this.route,
|
|
1692
|
-
queryParamsHandling: 'preserve',
|
|
1693
|
-
state: {
|
|
1694
|
-
formData,
|
|
1695
|
-
context: ComponentContext.TABBED
|
|
1696
|
-
}
|
|
1697
|
-
});
|
|
1698
|
-
}
|
|
1699
|
-
}
|
|
1700
|
-
hasFormChanges(currentData) {
|
|
1701
|
-
// For new forms or no original data, consider it changed
|
|
1702
|
-
if (this.isNew || !this.originalFormData) {
|
|
1703
|
-
return true;
|
|
1704
|
-
}
|
|
1705
|
-
// Deep comparison of objects
|
|
1706
|
-
return !this.isEqual(currentData, this.originalFormData);
|
|
1707
|
-
}
|
|
1708
|
-
isEqual(obj1, obj2) {
|
|
1709
|
-
if (obj1 === obj2)
|
|
1710
|
-
return true;
|
|
1711
|
-
if (typeof obj1 !== 'object' || obj1 === null ||
|
|
1712
|
-
typeof obj2 !== 'object' || obj2 === null)
|
|
1713
|
-
return false;
|
|
1714
|
-
const keys1 = Object.keys(obj1);
|
|
1715
|
-
const keys2 = Object.keys(obj2);
|
|
1716
|
-
if (keys1.length !== keys2.length)
|
|
1717
|
-
return false;
|
|
1718
|
-
return keys1.every(key => Object.prototype.hasOwnProperty.call(obj2, key) &&
|
|
1719
|
-
this.isEqual(obj1[key], obj2[key]));
|
|
1720
|
-
}
|
|
1721
|
-
isValidPageMode(mode) {
|
|
1722
|
-
return Object.values(AppConstants.PAGE_MODE).includes(mode);
|
|
1723
|
-
}
|
|
1724
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTabbedFormComponent, deps: [{ token: i1$2.ActivatedRoute }, { token: i1$2.Router }, { token: NotificationService }, { token: UtilsService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1725
|
-
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 });
|
|
1726
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTabbedFormComponent });
|
|
1727
|
-
}
|
|
1728
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTabbedFormComponent, decorators: [{
|
|
1729
|
-
type: Injectable
|
|
1730
|
-
}, {
|
|
1731
|
-
type: Component,
|
|
1732
|
-
args: [{
|
|
1733
|
-
template: ''
|
|
1734
|
-
}]
|
|
1735
|
-
}], ctorParameters: () => [{ type: i1$2.ActivatedRoute }, { type: i1$2.Router }, { type: NotificationService }, { type: UtilsService }] });
|
|
1736
|
-
|
|
1737
|
-
class IndianDatePipe {
|
|
1738
|
-
datePipe;
|
|
1739
|
-
constructor(datePipe) {
|
|
1740
|
-
this.datePipe = datePipe;
|
|
1741
|
-
}
|
|
1742
|
-
transform(value, format = 'default') {
|
|
1743
|
-
if (!value)
|
|
1744
|
-
return null;
|
|
1745
|
-
// First validate dd-MM-yyyy format
|
|
1746
|
-
const dateRegex = /^(0[1-9]|[12][0-9]|3[01])-(0[1-9]|1[0-2])-\d{4}$/;
|
|
1747
|
-
if (!dateRegex.test(value)) {
|
|
1748
|
-
throw new Error(`Invalid date format: ${value}. Expected format: dd-MM-yyyy`);
|
|
1749
|
-
}
|
|
1750
|
-
// Validate real date
|
|
1751
|
-
const [day, month, year] = value.split('-').map(Number);
|
|
1752
|
-
const date = new Date(year, month - 1, day);
|
|
1753
|
-
if (date.getFullYear() !== year ||
|
|
1754
|
-
date.getMonth() !== month - 1 ||
|
|
1755
|
-
date.getDate() !== day) {
|
|
1756
|
-
throw new Error(`Invalid date: ${value}`);
|
|
1757
|
-
}
|
|
1758
|
-
// Apply requested format
|
|
1759
|
-
switch (format) {
|
|
1760
|
-
case 'short':
|
|
1761
|
-
// Pad with leading zeros for consistent format
|
|
1762
|
-
const paddedDay = day.toString().padStart(2, '0');
|
|
1763
|
-
const paddedMonth = month.toString().padStart(2, '0');
|
|
1764
|
-
return `${paddedDay}-${paddedMonth}-${year}`; // 10-03-2025
|
|
1765
|
-
case 'medium':
|
|
1766
|
-
return this.datePipe.transform(date, 'dd MMM yyyy'); // 10 Jan 2019
|
|
1767
|
-
default:
|
|
1768
|
-
return value; // original dd-MM-yyyy
|
|
1769
|
-
}
|
|
2302
|
+
});
|
|
1770
2303
|
}
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
class TextInputType extends FieldType {
|
|
1783
|
-
defaultOptions = {
|
|
1784
|
-
props: {
|
|
1785
|
-
type: 'text',
|
|
1786
|
-
placeholder: ''
|
|
1787
|
-
}
|
|
1788
|
-
};
|
|
1789
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TextInputType, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1790
|
-
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: `
|
|
1791
|
-
<input pInputText
|
|
1792
|
-
[type]="props['type'] || 'text'"
|
|
1793
|
-
[formControl]="$any(formControl)"
|
|
1794
|
-
[formlyAttributes]="field"
|
|
1795
|
-
[placeholder]="props['placeholder'] || ''"
|
|
1796
|
-
[attr.aria-label]="props['label'] || props['placeholder'] || ''"
|
|
1797
|
-
class="w-full"/>
|
|
1798
|
-
`, 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"] }] });
|
|
1799
|
-
}
|
|
1800
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TextInputType, decorators: [{
|
|
1801
|
-
type: Component,
|
|
1802
|
-
args: [{
|
|
1803
|
-
selector: 'formly-field-text-input',
|
|
1804
|
-
standalone: true,
|
|
1805
|
-
imports: [
|
|
1806
|
-
CommonModule,
|
|
1807
|
-
ReactiveFormsModule,
|
|
1808
|
-
FormlyModule,
|
|
1809
|
-
InputTextModule
|
|
1810
|
-
],
|
|
1811
|
-
template: `
|
|
1812
|
-
<input pInputText
|
|
1813
|
-
[type]="props['type'] || 'text'"
|
|
1814
|
-
[formControl]="$any(formControl)"
|
|
1815
|
-
[formlyAttributes]="field"
|
|
1816
|
-
[placeholder]="props['placeholder'] || ''"
|
|
1817
|
-
[attr.aria-label]="props['label'] || props['placeholder'] || ''"
|
|
1818
|
-
class="w-full"/>
|
|
1819
|
-
`
|
|
1820
|
-
}]
|
|
1821
|
-
}] });
|
|
1822
|
-
|
|
1823
|
-
class SelectType extends FieldType {
|
|
1824
|
-
get options$() {
|
|
1825
|
-
const options = this.props['options'];
|
|
1826
|
-
return isObservable(options) ? options : of(options || []);
|
|
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
|
+
});
|
|
1827
2315
|
}
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
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: `
|
|
1841
|
-
<ng-container *ngIf="options$ | async as options">
|
|
1842
|
-
<p-select
|
|
1843
|
-
[formControl]="$any(formControl)"
|
|
1844
|
-
[formlyAttributes]="field"
|
|
1845
|
-
[options]="options"
|
|
1846
|
-
[virtualScroll]="false"
|
|
1847
|
-
[showClear]="false"
|
|
1848
|
-
[placeholder]="props['placeholder'] || 'Select'"
|
|
1849
|
-
styleClass="w-full">
|
|
1850
|
-
</p-select>
|
|
1851
|
-
</ng-container>
|
|
1852
|
-
`, 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"] }] });
|
|
1853
|
-
}
|
|
1854
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: SelectType, decorators: [{
|
|
1855
|
-
type: Component,
|
|
1856
|
-
args: [{
|
|
1857
|
-
selector: 'formly-field-select',
|
|
1858
|
-
standalone: true,
|
|
1859
|
-
imports: [
|
|
1860
|
-
CommonModule,
|
|
1861
|
-
ReactiveFormsModule,
|
|
1862
|
-
FormlyModule,
|
|
1863
|
-
SelectModule
|
|
1864
|
-
],
|
|
1865
|
-
template: `
|
|
1866
|
-
<ng-container *ngIf="options$ | async as options">
|
|
1867
|
-
<p-select
|
|
1868
|
-
[formControl]="$any(formControl)"
|
|
1869
|
-
[formlyAttributes]="field"
|
|
1870
|
-
[options]="options"
|
|
1871
|
-
[virtualScroll]="false"
|
|
1872
|
-
[showClear]="false"
|
|
1873
|
-
[placeholder]="props['placeholder'] || 'Select'"
|
|
1874
|
-
styleClass="w-full">
|
|
1875
|
-
</p-select>
|
|
1876
|
-
</ng-container>
|
|
1877
|
-
`
|
|
1878
|
-
}]
|
|
1879
|
-
}] });
|
|
1880
|
-
|
|
1881
|
-
class RadioType extends FieldType {
|
|
1882
|
-
get options$() {
|
|
1883
|
-
const options = this.props['options'];
|
|
1884
|
-
return isObservable(options) ? options : of(options || []);
|
|
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;
|
|
1885
2328
|
}
|
|
1886
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type:
|
|
1887
|
-
static
|
|
1888
|
-
<div class="flex flex-column gap-1">
|
|
1889
|
-
<ng-container *ngIf="options$ | async as options">
|
|
1890
|
-
<div *ngFor="let option of options; let i = index" class="flex align-items-center gap-2">
|
|
1891
|
-
<p-radioButton
|
|
1892
|
-
[name]="field.name || ''"
|
|
1893
|
-
[value]="option.value"
|
|
1894
|
-
[formControl]="$any(formControl)"
|
|
1895
|
-
[formlyAttributes]="field"
|
|
1896
|
-
[size]="'small'"
|
|
1897
|
-
[inputId]="field.key + '_' + i">
|
|
1898
|
-
</p-radioButton>
|
|
1899
|
-
<label [for]="field.key + '_' + i" class="p-radio-label cursor-pointer">
|
|
1900
|
-
{{option.label}}
|
|
1901
|
-
</label>
|
|
1902
|
-
</div>
|
|
1903
|
-
</ng-container>
|
|
1904
|
-
</div>
|
|
1905
|
-
`, 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"] }] });
|
|
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 });
|
|
1906
2331
|
}
|
|
1907
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type:
|
|
1908
|
-
type:
|
|
1909
|
-
|
|
1910
|
-
selector: 'formly-field-radio',
|
|
1911
|
-
standalone: true,
|
|
1912
|
-
imports: [
|
|
1913
|
-
CommonModule,
|
|
1914
|
-
ReactiveFormsModule,
|
|
1915
|
-
FormlyModule,
|
|
1916
|
-
RadioButtonModule
|
|
1917
|
-
],
|
|
1918
|
-
template: `
|
|
1919
|
-
<div class="flex flex-column gap-1">
|
|
1920
|
-
<ng-container *ngIf="options$ | async as options">
|
|
1921
|
-
<div *ngFor="let option of options; let i = index" class="flex align-items-center gap-2">
|
|
1922
|
-
<p-radioButton
|
|
1923
|
-
[name]="field.name || ''"
|
|
1924
|
-
[value]="option.value"
|
|
1925
|
-
[formControl]="$any(formControl)"
|
|
1926
|
-
[formlyAttributes]="field"
|
|
1927
|
-
[size]="'small'"
|
|
1928
|
-
[inputId]="field.key + '_' + i">
|
|
1929
|
-
</p-radioButton>
|
|
1930
|
-
<label [for]="field.key + '_' + i" class="p-radio-label cursor-pointer">
|
|
1931
|
-
{{option.label}}
|
|
1932
|
-
</label>
|
|
1933
|
-
</div>
|
|
1934
|
-
</ng-container>
|
|
1935
|
-
</div>
|
|
1936
|
-
`
|
|
1937
|
-
}]
|
|
1938
|
-
}] });
|
|
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 }] });
|
|
1939
2335
|
|
|
1940
|
-
class
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
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);
|
|
1950
2363
|
}
|
|
1951
|
-
};
|
|
1952
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: NumericInputType, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1953
|
-
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: `
|
|
1954
|
-
<p-inputNumber
|
|
1955
|
-
[formControl]="$any(formControl)"
|
|
1956
|
-
[formlyAttributes]="field"
|
|
1957
|
-
[placeholder]="props['placeholder'] || ''"
|
|
1958
|
-
[min]="props['min'] || 0"
|
|
1959
|
-
[max]="props['max']"
|
|
1960
|
-
[minFractionDigits]="props['minFractionDigits'] || 0"
|
|
1961
|
-
[maxFractionDigits]="props['maxFractionDigits'] || 0"
|
|
1962
|
-
[prefix]="props['prefix'] || ''"
|
|
1963
|
-
[suffix]="props['suffix'] || ''"
|
|
1964
|
-
[useGrouping]="props['useGrouping'] ?? true"
|
|
1965
|
-
[showButtons]="true"
|
|
1966
|
-
class="w-full">
|
|
1967
|
-
</p-inputNumber>
|
|
1968
|
-
`, 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"] }] });
|
|
1969
|
-
}
|
|
1970
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: NumericInputType, decorators: [{
|
|
1971
|
-
type: Component,
|
|
1972
|
-
args: [{
|
|
1973
|
-
selector: 'formly-field-numeric-input',
|
|
1974
|
-
standalone: true,
|
|
1975
|
-
imports: [
|
|
1976
|
-
CommonModule,
|
|
1977
|
-
ReactiveFormsModule,
|
|
1978
|
-
FormlyModule,
|
|
1979
|
-
InputNumberModule
|
|
1980
|
-
],
|
|
1981
|
-
template: `
|
|
1982
|
-
<p-inputNumber
|
|
1983
|
-
[formControl]="$any(formControl)"
|
|
1984
|
-
[formlyAttributes]="field"
|
|
1985
|
-
[placeholder]="props['placeholder'] || ''"
|
|
1986
|
-
[min]="props['min'] || 0"
|
|
1987
|
-
[max]="props['max']"
|
|
1988
|
-
[minFractionDigits]="props['minFractionDigits'] || 0"
|
|
1989
|
-
[maxFractionDigits]="props['maxFractionDigits'] || 0"
|
|
1990
|
-
[prefix]="props['prefix'] || ''"
|
|
1991
|
-
[suffix]="props['suffix'] || ''"
|
|
1992
|
-
[useGrouping]="props['useGrouping'] ?? true"
|
|
1993
|
-
[showButtons]="true"
|
|
1994
|
-
class="w-full">
|
|
1995
|
-
</p-inputNumber>
|
|
1996
|
-
`
|
|
1997
|
-
}]
|
|
1998
|
-
}] });
|
|
1999
|
-
|
|
2000
|
-
class MultiselectType extends FieldType {
|
|
2001
|
-
get options$() {
|
|
2002
|
-
const options = this.props['options'];
|
|
2003
|
-
return isObservable(options) ? options : of(options || []);
|
|
2004
2364
|
}
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
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;
|
|
2015
2598
|
}
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
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 });
|
|
2037
2625
|
}
|
|
2038
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type:
|
|
2626
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseTabbedFormComponent, decorators: [{
|
|
2627
|
+
type: Injectable
|
|
2628
|
+
}, {
|
|
2039
2629
|
type: Component,
|
|
2040
2630
|
args: [{
|
|
2041
|
-
|
|
2042
|
-
standalone: true,
|
|
2043
|
-
imports: [
|
|
2044
|
-
CommonModule,
|
|
2045
|
-
ReactiveFormsModule,
|
|
2046
|
-
FormlyModule,
|
|
2047
|
-
MultiSelectModule
|
|
2048
|
-
],
|
|
2049
|
-
template: `
|
|
2050
|
-
<ng-container *ngIf="options$ | async as options">
|
|
2051
|
-
<p-multiSelect
|
|
2052
|
-
[formControl]="$any(formControl)"
|
|
2053
|
-
[formlyAttributes]="field"
|
|
2054
|
-
[options]="options"
|
|
2055
|
-
[optionLabel]="props['optionLabel'] || 'label'"
|
|
2056
|
-
[optionValue]="props['optionValue'] || 'value'"
|
|
2057
|
-
[placeholder]="props['placeholder'] || 'Select'"
|
|
2058
|
-
[filter]="props['filter'] || true"
|
|
2059
|
-
[filterBy]="props['filterBy'] || 'label'"
|
|
2060
|
-
[maxSelectedLabels]="props['maxSelectedLabels'] || 2"
|
|
2061
|
-
[showToggleAll]="props['showToggleAll'] || true"
|
|
2062
|
-
[virtualScroll]="props['virtualScroll'] || false"
|
|
2063
|
-
[display]="props['display'] || 'chip'"
|
|
2064
|
-
styleClass="w-full">
|
|
2065
|
-
</p-multiSelect>
|
|
2066
|
-
</ng-container>
|
|
2067
|
-
`
|
|
2631
|
+
template: ''
|
|
2068
2632
|
}]
|
|
2069
|
-
}] });
|
|
2633
|
+
}], ctorParameters: () => [{ type: i1$1.ActivatedRoute }, { type: i1$1.Router }, { type: NotificationService }, { type: UtilsService }] });
|
|
2070
2634
|
|
|
2071
|
-
class
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
}
|
|
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: []
|
|
2080
2655
|
};
|
|
2081
|
-
|
|
2082
|
-
|
|
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;
|
|
2083
2662
|
}
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
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);
|
|
2090
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
|
+
});
|
|
2091
2697
|
}
|
|
2092
2698
|
else {
|
|
2093
|
-
|
|
2699
|
+
console.warn(`[BaseListTabbedFormComponent] Tab data not found for uuid: ${uuid}`);
|
|
2094
2700
|
}
|
|
2095
2701
|
}
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
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
|
|
2100
2725
|
}
|
|
2101
2726
|
}
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
? [parseInt(parts[0], 10), parseInt(parts[1], 10) - 1, parseInt(parts[2], 10)]
|
|
2107
|
-
: [parseInt(parts[1], 10), parseInt(parts[0], 10) - 1, parseInt(parts[2], 10)];
|
|
2108
|
-
return new Date(year, month, day);
|
|
2727
|
+
initializeFormMode(routeMode) {
|
|
2728
|
+
if (!routeMode) {
|
|
2729
|
+
this.state.mode = AppConstants.PAGE_MODE.CREATE;
|
|
2730
|
+
return;
|
|
2109
2731
|
}
|
|
2110
|
-
|
|
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;
|
|
2111
2737
|
}
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
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
|
+
}
|
|
2117
2744
|
}
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
[showIcon]="true"
|
|
2124
|
-
[dateFormat]="props['dateFormat'] || 'dd-mm-yy'"
|
|
2125
|
-
[showTime]="props['showTime'] || false"
|
|
2126
|
-
[touchUI]="props['touchUI'] || false"
|
|
2127
|
-
[placeholder]="props['placeholder'] || ''"
|
|
2128
|
-
[minDate]="props['minDate']"
|
|
2129
|
-
[maxDate]="props['maxDate']"
|
|
2130
|
-
[showOnFocus]="false"
|
|
2131
|
-
[todayButtonStyleClass]="'p-button-secondary'"
|
|
2132
|
-
[clearButtonStyleClass]="'p-button-secondary'"
|
|
2133
|
-
styleClass="w-full"
|
|
2134
|
-
(onSelect)="onDateSelect($event)"
|
|
2135
|
-
(onInput)="onDateInput($event)">
|
|
2136
|
-
</p-datepicker>
|
|
2137
|
-
`, 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"] }] });
|
|
2138
|
-
}
|
|
2139
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: DateType, decorators: [{
|
|
2140
|
-
type: Component,
|
|
2141
|
-
args: [{
|
|
2142
|
-
selector: 'formly-field-date',
|
|
2143
|
-
standalone: true,
|
|
2144
|
-
imports: [
|
|
2145
|
-
CommonModule,
|
|
2146
|
-
ReactiveFormsModule,
|
|
2147
|
-
FormlyModule,
|
|
2148
|
-
DatePickerModule
|
|
2149
|
-
],
|
|
2150
|
-
template: `
|
|
2151
|
-
<p-datepicker
|
|
2152
|
-
[formControl]="$any(formControl)"
|
|
2153
|
-
[formlyAttributes]="field"
|
|
2154
|
-
[showIcon]="true"
|
|
2155
|
-
[dateFormat]="props['dateFormat'] || 'dd-mm-yy'"
|
|
2156
|
-
[showTime]="props['showTime'] || false"
|
|
2157
|
-
[touchUI]="props['touchUI'] || false"
|
|
2158
|
-
[placeholder]="props['placeholder'] || ''"
|
|
2159
|
-
[minDate]="props['minDate']"
|
|
2160
|
-
[maxDate]="props['maxDate']"
|
|
2161
|
-
[showOnFocus]="false"
|
|
2162
|
-
[todayButtonStyleClass]="'p-button-secondary'"
|
|
2163
|
-
[clearButtonStyleClass]="'p-button-secondary'"
|
|
2164
|
-
styleClass="w-full"
|
|
2165
|
-
(onSelect)="onDateSelect($event)"
|
|
2166
|
-
(onInput)="onDateInput($event)">
|
|
2167
|
-
</p-datepicker>
|
|
2168
|
-
`
|
|
2169
|
-
}]
|
|
2170
|
-
}] });
|
|
2171
|
-
|
|
2172
|
-
class CheckboxType extends FieldType {
|
|
2173
|
-
defaultOptions = {
|
|
2174
|
-
props: {
|
|
2175
|
-
trueValue: true,
|
|
2176
|
-
falseValue: false
|
|
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');
|
|
2177
2750
|
}
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
[formlyAttributes]="field"
|
|
2205
|
-
[binary]="!props['options']"
|
|
2206
|
-
[trueValue]="props['trueValue']"
|
|
2207
|
-
[falseValue]="props['falseValue']">
|
|
2208
|
-
{{props['checkboxLabel'] || ''}}
|
|
2209
|
-
</p-checkbox>
|
|
2210
|
-
</div>
|
|
2211
|
-
`, styles: [".checkbox-container{display:flex;align-items:center;height:40px}\n"] }]
|
|
2212
|
-
}] });
|
|
2213
|
-
|
|
2214
|
-
class TextareaType extends FieldType {
|
|
2215
|
-
defaultOptions = {
|
|
2216
|
-
props: {
|
|
2217
|
-
rows: 3,
|
|
2218
|
-
cols: undefined,
|
|
2219
|
-
autoResize: true,
|
|
2220
|
-
placeholder: ''
|
|
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);
|
|
2221
2777
|
}
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
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 });
|
|
2235
2808
|
}
|
|
2236
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type:
|
|
2809
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BaseListTabbedFormComponent, decorators: [{
|
|
2810
|
+
type: Injectable
|
|
2811
|
+
}, {
|
|
2237
2812
|
type: Component,
|
|
2238
2813
|
args: [{
|
|
2239
|
-
|
|
2240
|
-
standalone: true,
|
|
2241
|
-
imports: [
|
|
2242
|
-
CommonModule,
|
|
2243
|
-
ReactiveFormsModule,
|
|
2244
|
-
FormlyModule,
|
|
2245
|
-
Textarea
|
|
2246
|
-
],
|
|
2247
|
-
template: `
|
|
2248
|
-
<textarea pInputTextarea
|
|
2249
|
-
[formControl]="$any(formControl)"
|
|
2250
|
-
[formlyAttributes]="field"
|
|
2251
|
-
[rows]="props['rows'] || 3"
|
|
2252
|
-
[cols]="props['cols']"
|
|
2253
|
-
[autoResize]="props['autoResize']"
|
|
2254
|
-
[placeholder]="props['placeholder'] || ''"
|
|
2255
|
-
class="w-full">
|
|
2256
|
-
</textarea>
|
|
2257
|
-
`
|
|
2814
|
+
template: ''
|
|
2258
2815
|
}]
|
|
2259
|
-
}] });
|
|
2816
|
+
}], ctorParameters: () => [{ type: i1$1.ActivatedRoute }, { type: i1$1.Router }, { type: NotificationService }, { type: UtilsService }] });
|
|
2260
2817
|
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
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;
|
|
2268
2848
|
}
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
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
|
|
2272
2904
|
}
|
|
2273
|
-
|
|
2274
|
-
return invalid && (touched || dirty);
|
|
2905
|
+
this.router.navigate([`/${this.getEntityName().toLowerCase()}s`]);
|
|
2275
2906
|
}
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
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 });
|
|
2302
2933
|
}
|
|
2303
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type:
|
|
2304
|
-
type:
|
|
2305
|
-
|
|
2306
|
-
<div class="field mb-4">
|
|
2307
|
-
<label *ngIf="props.label" [for]="id" class="font-medium">
|
|
2308
|
-
{{ props.label }}
|
|
2309
|
-
<span class="required-star" *ngIf="props.required">*</span>
|
|
2310
|
-
<i *ngIf="props.tooltip"
|
|
2311
|
-
class="pi pi-info-circle ml-2"
|
|
2312
|
-
[pTooltip]="props.tooltip">
|
|
2313
|
-
</i>
|
|
2314
|
-
</label>
|
|
2315
|
-
|
|
2316
|
-
<div [class.p-input-error]="showError">
|
|
2317
|
-
<ng-container #fieldComponent></ng-container>
|
|
2318
|
-
</div>
|
|
2319
|
-
|
|
2320
|
-
<small *ngIf="props.description" class="block mt-1">
|
|
2321
|
-
{{ props.description }}
|
|
2322
|
-
</small>
|
|
2323
|
-
|
|
2324
|
-
<small *ngIf="showError" class="p-error block">
|
|
2325
|
-
<i class="pi pi-exclamation-circle mr-2"></i>
|
|
2326
|
-
{{ errorMessage }}
|
|
2327
|
-
</small>
|
|
2328
|
-
</div>
|
|
2329
|
-
`, 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"] }]
|
|
2330
|
-
}] });
|
|
2331
|
-
|
|
2332
|
-
const FORMLY_TYPES = {
|
|
2333
|
-
TEXT: 'text',
|
|
2334
|
-
NUMBER: 'number',
|
|
2335
|
-
DATE: 'date',
|
|
2336
|
-
CHECKBOX: 'checkbox',
|
|
2337
|
-
RADIO: 'radio',
|
|
2338
|
-
SELECT: 'select',
|
|
2339
|
-
MULTISELECT: 'multiselect',
|
|
2340
|
-
TEXTAREA: 'textarea'
|
|
2341
|
-
};
|
|
2342
|
-
const FORMLY_WRAPPERS = {
|
|
2343
|
-
FORM_FIELD: 'form-field'
|
|
2344
|
-
};
|
|
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 }] });
|
|
2345
2937
|
|
|
2346
|
-
class
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
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" });
|
|
2364
2974
|
}
|
|
2365
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type:
|
|
2366
|
-
type:
|
|
2975
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: IndianDatePipe, decorators: [{
|
|
2976
|
+
type: Pipe,
|
|
2367
2977
|
args: [{
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
types: [
|
|
2371
|
-
{ name: FORMLY_TYPES.TEXT, component: TextInputType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2372
|
-
{ name: FORMLY_TYPES.NUMBER, component: NumericInputType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2373
|
-
{ name: FORMLY_TYPES.DATE, component: DateType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2374
|
-
{ name: FORMLY_TYPES.CHECKBOX, component: CheckboxType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2375
|
-
{ name: FORMLY_TYPES.RADIO, component: RadioType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2376
|
-
{ name: FORMLY_TYPES.SELECT, component: SelectType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2377
|
-
{ name: FORMLY_TYPES.MULTISELECT, component: MultiselectType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
2378
|
-
{ name: FORMLY_TYPES.TEXTAREA, component: TextareaType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] }, // Added textarea type
|
|
2379
|
-
],
|
|
2380
|
-
wrappers: [
|
|
2381
|
-
{ name: FORMLY_WRAPPERS.FORM_FIELD, component: FormFieldWrapper }
|
|
2382
|
-
]
|
|
2383
|
-
})
|
|
2384
|
-
],
|
|
2385
|
-
exports: [FormlyModule]
|
|
2978
|
+
name: 'indianDate',
|
|
2979
|
+
standalone: true
|
|
2386
2980
|
}]
|
|
2387
|
-
}] });
|
|
2981
|
+
}], ctorParameters: () => [{ type: i1$3.DatePipe }] });
|
|
2388
2982
|
|
|
2389
2983
|
/*
|
|
2390
2984
|
* Public API Surface of core-lib
|
|
2391
2985
|
*/
|
|
2392
|
-
//
|
|
2986
|
+
// Constants
|
|
2393
2987
|
|
|
2394
2988
|
/**
|
|
2395
2989
|
* Generated bundle index. Do not edit.
|
|
2396
2990
|
*/
|
|
2397
2991
|
|
|
2398
|
-
export { AppConstants, AppMessages, BaseCrudService, BaseFormComponent, BaseListPageComponent, BasePageComponent, BaseTabbedFormComponent,
|
|
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 };
|
|
2399
2993
|
//# sourceMappingURL=codex-ts-core-lib.mjs.map
|