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