@codex-ts/core-lib 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{dist/core-lib/fesm2022/core-lib.mjs → fesm2022/codex-ts-core-lib.mjs} +13 -4
- package/fesm2022/codex-ts-core-lib.mjs.map +1 -0
- package/package.json +42 -63
- package/dist/core-lib/fesm2022/core-lib.mjs.map +0 -1
- package/ng-package.json +0 -7
- package/src/index.ts +0 -44
- package/src/lib/components/base-form.component.ts +0 -328
- package/src/lib/components/base-list-page.component.ts +0 -328
- package/src/lib/components/base-page.component.ts +0 -33
- package/src/lib/components/base-tabbed-form.component.ts +0 -345
- package/src/lib/components/formly/core-formly.module.ts +0 -34
- package/src/lib/components/formly/field.interface.ts +0 -15
- package/src/lib/components/formly/form-field.wrapper.ts +0 -94
- package/src/lib/components/formly/formly.constants.ts +0 -14
- package/src/lib/components/formly/primeng-formly.module.ts +0 -77
- package/src/lib/components/formly/types/checkbox.type.ts +0 -43
- package/src/lib/components/formly/types/date.type.ts +0 -88
- package/src/lib/components/formly/types/multiselect.type.ts +0 -61
- package/src/lib/components/formly/types/numeric-input.type.ts +0 -45
- package/src/lib/components/formly/types/radio.type.ts +0 -47
- package/src/lib/components/formly/types/select.type.ts +0 -54
- package/src/lib/components/formly/types/text-input.type.ts +0 -33
- package/src/lib/components/formly/types/textarea.type.ts +0 -37
- package/src/lib/constants/app.constants.ts +0 -15
- package/src/lib/constants/app.messages.ts +0 -36
- package/src/lib/enums/component-context.enum.ts +0 -6
- package/src/lib/enums/tabbed-form-type.enum.ts +0 -4
- package/src/lib/models/pagination/filter-criteria.interface.ts +0 -19
- package/src/lib/models/pagination/page-request.interface.ts +0 -13
- package/src/lib/models/pagination/page.interface.ts +0 -9
- package/src/lib/models/pagination/sort-criteria.interface.ts +0 -4
- package/src/lib/pipes/indian-date.pipe.ts +0 -46
- package/src/lib/services/base-crud.service.ts +0 -81
- package/src/lib/services/entity-registry.service.ts +0 -51
- package/src/lib/services/entity.service.ts +0 -10
- package/src/lib/services/enum-registry.service.ts +0 -66
- package/src/lib/services/formly-config.service.ts +0 -50
- package/src/lib/services/http-utility.service.example.ts +0 -253
- package/src/lib/services/http-utility.service.md +0 -175
- package/src/lib/services/http-utility.service.ts +0 -389
- package/src/lib/services/notification.service.ts +0 -35
- package/src/lib/services/reference-data-provider.service.ts +0 -28
- package/src/lib/services/utils.service.ts +0 -154
- /package/{dist/core-lib/index.d.ts → index.d.ts} +0 -0
- /package/{dist/core-lib/lib → lib}/components/base-form.component.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/components/base-list-page.component.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/components/base-page.component.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/components/base-tabbed-form.component.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/components/formly/core-formly.module.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/components/formly/form-field.wrapper.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/components/formly/formly.constants.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/components/formly/types/checkbox.type.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/components/formly/types/date.type.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/components/formly/types/multiselect.type.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/components/formly/types/numeric-input.type.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/components/formly/types/radio.type.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/components/formly/types/select.type.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/components/formly/types/text-input.type.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/components/formly/types/textarea.type.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/constants/app.constants.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/constants/app.messages.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/enums/component-context.enum.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/enums/tabbed-form-type.enum.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/models/pagination/filter-criteria.interface.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/models/pagination/page-request.interface.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/models/pagination/page.interface.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/models/pagination/sort-criteria.interface.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/pipes/indian-date.pipe.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/services/base-crud.service.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/services/entity-registry.service.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/services/entity.service.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/services/enum-registry.service.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/services/formly-config.service.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/services/http-utility.service.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/services/notification.service.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/services/reference-data-provider.service.d.ts +0 -0
- /package/{dist/core-lib/lib → lib}/services/utils.service.d.ts +0 -0
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
import { Component, OnInit, Injectable } from '@angular/core';
|
|
2
|
-
import { ActivatedRoute, Router } from '@angular/router';
|
|
3
|
-
import { ComponentContext } from '../enums/component-context.enum';
|
|
4
|
-
import { BasePageComponent } from './base-page.component';
|
|
5
|
-
import { UtilsService } from '../services/utils.service';
|
|
6
|
-
import { NotificationService } from '../services/notification.service';
|
|
7
|
-
import { AppMessages } from '../constants/app.messages';
|
|
8
|
-
import { AppConstants, PageMode } from '../constants/app.constants';
|
|
9
|
-
import { Observable, take, map } from 'rxjs';
|
|
10
|
-
import { EntityService } from '../services/entity.service';
|
|
11
|
-
import { TabbedFormType } from '../enums/tabbed-form-type.enum';
|
|
12
|
-
|
|
13
|
-
export interface TabConfig {
|
|
14
|
-
value: string;
|
|
15
|
-
title: string;
|
|
16
|
-
isParentModel?: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
@Injectable()
|
|
20
|
-
@Component({
|
|
21
|
-
template: ''
|
|
22
|
-
})
|
|
23
|
-
export abstract class BaseTabbedFormComponent<T extends { uuid: string }>
|
|
24
|
-
extends BasePageComponent implements OnInit {
|
|
25
|
-
|
|
26
|
-
// Tab specific properties
|
|
27
|
-
activeTab: string = '';
|
|
28
|
-
tabs: TabConfig[] = [];
|
|
29
|
-
formType = '';
|
|
30
|
-
|
|
31
|
-
// Form properties
|
|
32
|
-
isNew = true;
|
|
33
|
-
mode: PageMode = AppConstants.PAGE_MODE.CREATE;
|
|
34
|
-
currentFormData!: T;
|
|
35
|
-
protected originalFormData: T | null = null;
|
|
36
|
-
protected hasChanges: boolean = false;
|
|
37
|
-
|
|
38
|
-
private debug(message: string, ...args: any[]): void {
|
|
39
|
-
// Only log in development
|
|
40
|
-
if (process.env['NODE_ENV'] !== 'production') {
|
|
41
|
-
console.log(`[BaseTabbedFormComponent] ${message}`, ...args);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
constructor(
|
|
46
|
-
protected route: ActivatedRoute,
|
|
47
|
-
protected router: Router,
|
|
48
|
-
protected override notificationService: NotificationService,
|
|
49
|
-
protected override utilsService: UtilsService
|
|
50
|
-
) {
|
|
51
|
-
super(utilsService, notificationService);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
ngOnInit() {
|
|
55
|
-
try {
|
|
56
|
-
// Initialize tabs
|
|
57
|
-
this.tabs = this.getTabs();
|
|
58
|
-
this.activeTab = this.tabs[0]?.value || '';
|
|
59
|
-
|
|
60
|
-
// Initialize form using query params
|
|
61
|
-
this.route.queryParams.pipe(take(1)).subscribe({
|
|
62
|
-
next: params => this.initializeForm(params),
|
|
63
|
-
error: error => {
|
|
64
|
-
console.error('Error initializing form:', error);
|
|
65
|
-
this.showError(AppMessages.FORM.ERROR.INIT);
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
} catch (error) {
|
|
69
|
-
console.error('Error in ngOnInit:', error);
|
|
70
|
-
this.showError(AppMessages.FORM.ERROR.INIT);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
private initializeFormData() {
|
|
76
|
-
this.debug('Initializing form data');
|
|
77
|
-
this.debug('Current mode:', this.mode);
|
|
78
|
-
|
|
79
|
-
if (this.mode === AppConstants.PAGE_MODE.CREATE) {
|
|
80
|
-
this.debug('Create mode, using defaults');
|
|
81
|
-
this.loadDefaultTab();
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const state = {
|
|
86
|
-
...this.router.getCurrentNavigation()?.extras?.state,
|
|
87
|
-
...window.history.state
|
|
88
|
-
} as { formData?: T };
|
|
89
|
-
|
|
90
|
-
this.debug('Combined state:', state);
|
|
91
|
-
|
|
92
|
-
if (state?.formData) {
|
|
93
|
-
this.debug('Found form data in state:', state.formData);
|
|
94
|
-
this.patchFormData(state.formData);
|
|
95
|
-
this.loadDefaultTab();
|
|
96
|
-
} else {
|
|
97
|
-
this.debug('No form data, loading from UUID');
|
|
98
|
-
this.loadFromUuid();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
private loadFromUuid() {
|
|
103
|
-
// Flatten nested subscriptions using RxJS operators
|
|
104
|
-
this.route.queryParams.pipe(
|
|
105
|
-
take(1),
|
|
106
|
-
map((params: { [key: string]: string }) => {
|
|
107
|
-
const uuid = params[AppConstants.FORM.UUID];
|
|
108
|
-
if (!uuid) {
|
|
109
|
-
throw new Error(`UUID is required for ${this.mode} mode when no formData is provided`);
|
|
110
|
-
}
|
|
111
|
-
return uuid;
|
|
112
|
-
})
|
|
113
|
-
).subscribe({
|
|
114
|
-
next: (uuid: string) => this.loadEntity(uuid),
|
|
115
|
-
error: (error: Error) => {
|
|
116
|
-
console.error('Error loading UUID:', error);
|
|
117
|
-
this.showError(`${AppMessages.FORM.ERROR.LOAD} UUID`);
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
private initializeForm(params: any) {
|
|
123
|
-
// Remove debug logging for production
|
|
124
|
-
const routeMode = params[AppConstants.FORM.MODE];
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
this.initializeFormMode(routeMode);
|
|
128
|
-
this.setupFormState();
|
|
129
|
-
|
|
130
|
-
if (this.formType === TabbedFormType.LIST) {
|
|
131
|
-
this.initializeListForm(params);
|
|
132
|
-
} else {
|
|
133
|
-
this.initializeFormData();
|
|
134
|
-
}
|
|
135
|
-
} catch (error) {
|
|
136
|
-
console.error('Error initializing form:', error);
|
|
137
|
-
this.showError(`${AppMessages.FORM.ERROR.INIT}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
protected patchFormData(data: T | null) {
|
|
142
|
-
if (data) {
|
|
143
|
-
this.currentFormData = { ...data };
|
|
144
|
-
this.originalFormData = { ...data }; // Create a deep copy to avoid reference issues
|
|
145
|
-
this.isNew = false;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
private initializeFormMode(routeMode: string | undefined) {
|
|
150
|
-
if (!routeMode) {
|
|
151
|
-
this.mode = AppConstants.PAGE_MODE.CREATE;
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Validate mode more explicitly
|
|
156
|
-
if (!this.isValidPageMode(routeMode)) {
|
|
157
|
-
const validModes = Object.values(AppConstants.PAGE_MODE).join(', ');
|
|
158
|
-
throw new Error(`Invalid page mode: ${routeMode}. Valid modes are: ${validModes}`);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
this.mode = routeMode;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
private setupFormState() {
|
|
165
|
-
this.debug('Setting up form state, mode:', this.mode);
|
|
166
|
-
|
|
167
|
-
if (this.mode === AppConstants.PAGE_MODE.CREATE) {
|
|
168
|
-
this.debug('Create mode setup');
|
|
169
|
-
this.isNew = true;
|
|
170
|
-
this.loading = false;
|
|
171
|
-
this.originalFormData = null;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
protected loadEntity(uuid: string) {
|
|
176
|
-
this.loading = true;
|
|
177
|
-
this.isNew = false;
|
|
178
|
-
const entityName = this.getEntityName();
|
|
179
|
-
|
|
180
|
-
this.getEntityService().getById(uuid).subscribe({
|
|
181
|
-
next: (data: T) => {
|
|
182
|
-
this.originalFormData = data;
|
|
183
|
-
this.currentFormData = { ...data };
|
|
184
|
-
this.loading = false;
|
|
185
|
-
this.debug('Original data loaded:', this.originalFormData);
|
|
186
|
-
this.debug('Form data updated:', this.currentFormData);
|
|
187
|
-
this.loadDefaultTab();
|
|
188
|
-
},
|
|
189
|
-
error: (error: Error) => {
|
|
190
|
-
this.debug('Error loading entity:', error);
|
|
191
|
-
this.showError(`${AppMessages.FORM.ERROR.LOAD} ${entityName}`);
|
|
192
|
-
this.loading = false;
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
protected findTabByValue(value: string): TabConfig | undefined {
|
|
198
|
-
return this.tabs.find(tab => tab.value === value);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
protected loadDefaultTab() {
|
|
202
|
-
this.debug('Loading default tab');
|
|
203
|
-
const defaultTab = this.tabs.find(tab => tab.isParentModel) || this.tabs[0];
|
|
204
|
-
|
|
205
|
-
if (!defaultTab) return;
|
|
206
|
-
|
|
207
|
-
if (this.formType === TabbedFormType.LIST) {
|
|
208
|
-
const state = this.router.getCurrentNavigation()?.extras?.state || window.history.state;
|
|
209
|
-
const formDataList = state?.formDataList || [];
|
|
210
|
-
const selectedData = formDataList.find((data: T) => data.uuid === defaultTab.value);
|
|
211
|
-
|
|
212
|
-
this.router.navigate([defaultTab.value], {
|
|
213
|
-
relativeTo: this.route,
|
|
214
|
-
state: {
|
|
215
|
-
formData: selectedData,
|
|
216
|
-
formDataList: formDataList,
|
|
217
|
-
context: ComponentContext.TABBED
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
} else {
|
|
221
|
-
this.router.navigate([defaultTab.value], {
|
|
222
|
-
relativeTo: this.route,
|
|
223
|
-
queryParamsHandling: 'preserve', // Keep existing query params (mode & uuid)
|
|
224
|
-
state: {
|
|
225
|
-
formData: this.currentFormData,
|
|
226
|
-
context: ComponentContext.TABBED
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
protected getTabFormData(tabConfig: TabConfig): T | null {
|
|
233
|
-
if (!tabConfig) return null;
|
|
234
|
-
|
|
235
|
-
if (tabConfig.isParentModel) {
|
|
236
|
-
return this.currentFormData;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const fieldName = this.utilsService.toCamelCase(tabConfig.value);
|
|
240
|
-
// Use type assertion since we know this is a valid field
|
|
241
|
-
return (this.currentFormData as any)[fieldName] as T;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
private initializeListForm(params: any) {
|
|
245
|
-
const state = this.router.getCurrentNavigation()?.extras?.state || window.history.state;
|
|
246
|
-
const formDataList = state?.formDataList;
|
|
247
|
-
|
|
248
|
-
if (!formDataList?.length) {
|
|
249
|
-
throw new Error('formDataList is required in state for LIST type');
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Create tabs from list in state
|
|
253
|
-
this.tabs = formDataList.map((data: T) => ({
|
|
254
|
-
value: data.uuid,
|
|
255
|
-
title: this.getTabTitle(data),
|
|
256
|
-
isParentModel: true
|
|
257
|
-
}));
|
|
258
|
-
|
|
259
|
-
// Select tab based on UUID in params or first tab
|
|
260
|
-
const uuid = params[AppConstants.FORM.UUID];
|
|
261
|
-
this.activeTab = uuid && this.tabs.find(t => t.value === uuid)
|
|
262
|
-
? uuid
|
|
263
|
-
: this.tabs[0].value;
|
|
264
|
-
|
|
265
|
-
// Set current form data from list
|
|
266
|
-
const selectedData = formDataList.find((d: T) => d.uuid === this.activeTab);
|
|
267
|
-
this.currentFormData = selectedData;
|
|
268
|
-
this.originalFormData = selectedData;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
protected onTabChange(value: string | number): void {
|
|
272
|
-
const tabConfig = this.findTabByValue(value as string);
|
|
273
|
-
if (!tabConfig) {
|
|
274
|
-
console.warn(`Tab not found: ${value}`);
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
this.activeTab = tabConfig.value;
|
|
279
|
-
|
|
280
|
-
if (this.formType === TabbedFormType.LIST) {
|
|
281
|
-
const state = this.router.getCurrentNavigation()?.extras?.state || window.history.state;
|
|
282
|
-
const formDataList = state?.formDataList || [];
|
|
283
|
-
const selectedData = formDataList.find((data: T) => data.uuid === value);
|
|
284
|
-
|
|
285
|
-
// Update current form data
|
|
286
|
-
this.currentFormData = selectedData;
|
|
287
|
-
this.originalFormData = selectedData;
|
|
288
|
-
|
|
289
|
-
this.router.navigate([value], {
|
|
290
|
-
relativeTo: this.route,
|
|
291
|
-
state: {
|
|
292
|
-
formData: selectedData,
|
|
293
|
-
formDataList: formDataList,
|
|
294
|
-
context: ComponentContext.TABBED
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
} else {
|
|
298
|
-
const formData = this.getTabFormData(tabConfig);
|
|
299
|
-
this.router.navigate([this.activeTab], {
|
|
300
|
-
relativeTo: this.route,
|
|
301
|
-
queryParamsHandling: 'preserve',
|
|
302
|
-
state: {
|
|
303
|
-
formData,
|
|
304
|
-
context: ComponentContext.TABBED
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
protected hasFormChanges(currentData: T): boolean {
|
|
311
|
-
// For new forms or no original data, consider it changed
|
|
312
|
-
if (this.isNew || !this.originalFormData) {
|
|
313
|
-
return true;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Deep comparison of objects
|
|
317
|
-
return !this.isEqual(currentData, this.originalFormData);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
private isEqual<V extends Record<string, any>>(obj1: V, obj2: V): boolean {
|
|
321
|
-
if (obj1 === obj2) return true;
|
|
322
|
-
if (typeof obj1 !== 'object' || obj1 === null ||
|
|
323
|
-
typeof obj2 !== 'object' || obj2 === null) return false;
|
|
324
|
-
|
|
325
|
-
const keys1 = Object.keys(obj1);
|
|
326
|
-
const keys2 = Object.keys(obj2);
|
|
327
|
-
|
|
328
|
-
if (keys1.length !== keys2.length) return false;
|
|
329
|
-
|
|
330
|
-
return keys1.every(key =>
|
|
331
|
-
Object.prototype.hasOwnProperty.call(obj2, key) &&
|
|
332
|
-
this.isEqual(obj1[key], obj2[key])
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
private isValidPageMode(mode: string): mode is PageMode {
|
|
337
|
-
return Object.values(AppConstants.PAGE_MODE).includes(mode as PageMode);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Abstract methods that must be implemented by child classes
|
|
341
|
-
protected abstract getTabs(): TabConfig[];
|
|
342
|
-
protected abstract getEntityService(): EntityService<T>;
|
|
343
|
-
protected abstract getEntityName(): string;
|
|
344
|
-
protected abstract getTabTitle(data: T): string;
|
|
345
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { NgModule } from '@angular/core';
|
|
2
|
-
import { FormlyModule } from '@ngx-formly/core';
|
|
3
|
-
import { TextInputType } from './types/text-input.type';
|
|
4
|
-
import { SelectType } from './types/select.type';
|
|
5
|
-
import { RadioType } from './types/radio.type';
|
|
6
|
-
import { NumericInputType } from './types/numeric-input.type';
|
|
7
|
-
import { MultiselectType } from './types/multiselect.type';
|
|
8
|
-
import { DateType } from './types/date.type';
|
|
9
|
-
import { CheckboxType } from './types/checkbox.type';
|
|
10
|
-
import { TextareaType } from './types/textarea.type'; // Added import
|
|
11
|
-
import { FormFieldWrapper } from './form-field.wrapper';
|
|
12
|
-
import { FORMLY_TYPES, FORMLY_WRAPPERS } from './formly.constants'; // Added import
|
|
13
|
-
|
|
14
|
-
@NgModule({
|
|
15
|
-
imports: [
|
|
16
|
-
FormlyModule.forRoot({
|
|
17
|
-
types: [
|
|
18
|
-
{ name: FORMLY_TYPES.TEXT, component: TextInputType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
19
|
-
{ name: FORMLY_TYPES.NUMBER, component: NumericInputType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
20
|
-
{ name: FORMLY_TYPES.DATE, component: DateType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
21
|
-
{ name: FORMLY_TYPES.CHECKBOX, component: CheckboxType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
22
|
-
{ name: FORMLY_TYPES.RADIO, component: RadioType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
23
|
-
{ name: FORMLY_TYPES.SELECT, component: SelectType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
24
|
-
{ name: FORMLY_TYPES.MULTISELECT, component: MultiselectType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] },
|
|
25
|
-
{ name: FORMLY_TYPES.TEXTAREA, component: TextareaType, wrappers: [FORMLY_WRAPPERS.FORM_FIELD] }, // Added textarea type
|
|
26
|
-
],
|
|
27
|
-
wrappers: [
|
|
28
|
-
{ name: FORMLY_WRAPPERS.FORM_FIELD, component: FormFieldWrapper }
|
|
29
|
-
]
|
|
30
|
-
})
|
|
31
|
-
],
|
|
32
|
-
exports: [FormlyModule]
|
|
33
|
-
})
|
|
34
|
-
export class CoreFormlyModule { }
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export interface FormlyFieldProps {
|
|
2
|
-
label?: string; // Field label text
|
|
3
|
-
placeholder?: string; // Placeholder text for input fields
|
|
4
|
-
required?: boolean; // Whether field is required
|
|
5
|
-
disabled?: boolean; // Disabled state
|
|
6
|
-
description?: string; // Help/description text
|
|
7
|
-
validation?: {
|
|
8
|
-
messages?: { [key: string]: string };
|
|
9
|
-
rules?: { [key: string]: any };
|
|
10
|
-
};
|
|
11
|
-
attributes?: { [key: string]: any };
|
|
12
|
-
appearance?: 'outlined' | 'filled';
|
|
13
|
-
size?: 'small' | 'normal' | 'large';
|
|
14
|
-
tooltip?: string;
|
|
15
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { Component } from '@angular/core';
|
|
2
|
-
import { CommonModule } from '@angular/common';
|
|
3
|
-
import { FieldWrapper, FormlyFieldConfig } from '@ngx-formly/core';
|
|
4
|
-
import { TooltipModule } from 'primeng/tooltip';
|
|
5
|
-
|
|
6
|
-
interface FormlyValidationMessage {
|
|
7
|
-
[key: string]: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
interface FormlyFieldProps {
|
|
11
|
-
label?: string;
|
|
12
|
-
required?: boolean;
|
|
13
|
-
tooltip?: string;
|
|
14
|
-
description?: string;
|
|
15
|
-
validation?: {
|
|
16
|
-
messages?: FormlyValidationMessage;
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
@Component({
|
|
21
|
-
selector: 'formly-wrapper-form-field',
|
|
22
|
-
standalone: true,
|
|
23
|
-
imports: [CommonModule, TooltipModule],
|
|
24
|
-
template: `
|
|
25
|
-
<div class="field mb-4">
|
|
26
|
-
<label *ngIf="props.label" [for]="id" class="font-medium">
|
|
27
|
-
{{ props.label }}
|
|
28
|
-
<span class="required-star" *ngIf="props.required">*</span>
|
|
29
|
-
<i *ngIf="props.tooltip"
|
|
30
|
-
class="pi pi-info-circle ml-2"
|
|
31
|
-
[pTooltip]="props.tooltip">
|
|
32
|
-
</i>
|
|
33
|
-
</label>
|
|
34
|
-
|
|
35
|
-
<div [class.p-input-error]="showError">
|
|
36
|
-
<ng-container #fieldComponent></ng-container>
|
|
37
|
-
</div>
|
|
38
|
-
|
|
39
|
-
<small *ngIf="props.description" class="block mt-1">
|
|
40
|
-
{{ props.description }}
|
|
41
|
-
</small>
|
|
42
|
-
|
|
43
|
-
<small *ngIf="showError" class="p-error block">
|
|
44
|
-
<i class="pi pi-exclamation-circle mr-2"></i>
|
|
45
|
-
{{ errorMessage }}
|
|
46
|
-
</small>
|
|
47
|
-
</div>
|
|
48
|
-
`,
|
|
49
|
-
styles: [`
|
|
50
|
-
:host ::ng-deep {
|
|
51
|
-
.required-star {
|
|
52
|
-
color: var(--red-500);
|
|
53
|
-
margin-left: 0.25rem;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
.p-input-error :where(input, .p-inputtext) {
|
|
57
|
-
border-color: var(--red-500);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.p-input-error :where(input:enabled:focus, .p-inputtext:enabled:focus) {
|
|
61
|
-
border-color: var(--red-500);
|
|
62
|
-
box-shadow: 0 0 0 1px var(--red-500);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.p-error {
|
|
66
|
-
color: var(--red-500);
|
|
67
|
-
margin-top: 0.5rem;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
small {
|
|
71
|
-
color: var(--text-color-secondary);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
`]
|
|
75
|
-
})
|
|
76
|
-
export class FormFieldWrapper extends FieldWrapper<FormlyFieldConfig<FormlyFieldProps>> {
|
|
77
|
-
get errorMessage(): string {
|
|
78
|
-
if (!this.field.formControl?.errors) {
|
|
79
|
-
return '';
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const firstErrorKey = Object.keys(this.field.formControl.errors)[0];
|
|
83
|
-
return this.props.validation?.messages?.[firstErrorKey] || 'This field is required';
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
override get showError(): boolean {
|
|
87
|
-
if (!this.field.formControl) {
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const { touched, dirty, invalid } = this.field.formControl;
|
|
92
|
-
return invalid && (touched || dirty);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export const FORMLY_TYPES = {
|
|
2
|
-
TEXT: 'text',
|
|
3
|
-
NUMBER: 'number',
|
|
4
|
-
DATE: 'date',
|
|
5
|
-
CHECKBOX: 'checkbox',
|
|
6
|
-
RADIO: 'radio',
|
|
7
|
-
SELECT: 'select',
|
|
8
|
-
MULTISELECT: 'multiselect',
|
|
9
|
-
TEXTAREA: 'textarea'
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const FORMLY_WRAPPERS = {
|
|
13
|
-
FORM_FIELD: 'form-field'
|
|
14
|
-
};
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { NgModule } from '@angular/core';
|
|
2
|
-
import { ReactiveFormsModule } from '@angular/forms';
|
|
3
|
-
import { FormlyModule } from '@ngx-formly/core';
|
|
4
|
-
import { CommonModule } from '@angular/common';
|
|
5
|
-
|
|
6
|
-
import { InputTextModule } from 'primeng/inputtext';
|
|
7
|
-
import { DropdownModule } from 'primeng/dropdown';
|
|
8
|
-
import { DatePickerModule } from 'primeng/datepicker';
|
|
9
|
-
import { CheckboxModule } from 'primeng/checkbox';
|
|
10
|
-
import { RadioButtonModule } from 'primeng/radiobutton';
|
|
11
|
-
import { MultiSelectModule } from 'primeng/multiselect';
|
|
12
|
-
|
|
13
|
-
import { FormFieldWrapper } from './form-field.wrapper';
|
|
14
|
-
import { TextInputType } from './types/text-input.type';
|
|
15
|
-
import { NumericInputType } from './types/numeric-input.type';
|
|
16
|
-
import { CheckboxType } from './types/checkbox.type';
|
|
17
|
-
import { DateType } from './types/date.type';
|
|
18
|
-
import { RadioType } from './types/radio.type';
|
|
19
|
-
import { SelectType } from './types/select.type';
|
|
20
|
-
import { MultiselectType } from './types/multiselect.type';
|
|
21
|
-
|
|
22
|
-
@NgModule({
|
|
23
|
-
imports: [
|
|
24
|
-
CommonModule,
|
|
25
|
-
ReactiveFormsModule,
|
|
26
|
-
InputTextModule,
|
|
27
|
-
DropdownModule,
|
|
28
|
-
DatePickerModule,
|
|
29
|
-
CheckboxModule,
|
|
30
|
-
RadioButtonModule,
|
|
31
|
-
MultiSelectModule,
|
|
32
|
-
FormlyModule.forRoot({
|
|
33
|
-
wrappers: [
|
|
34
|
-
{ name: 'form-field', component: FormFieldWrapper }
|
|
35
|
-
],
|
|
36
|
-
types: [
|
|
37
|
-
{
|
|
38
|
-
name: 'text-input',
|
|
39
|
-
component: TextInputType,
|
|
40
|
-
wrappers: ['form-field']
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
name: 'numeric-input',
|
|
44
|
-
component: NumericInputType,
|
|
45
|
-
wrappers: ['form-field']
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
name: 'checkbox',
|
|
49
|
-
component: CheckboxType,
|
|
50
|
-
wrappers: ['form-field']
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
name: 'radio',
|
|
54
|
-
component: RadioType,
|
|
55
|
-
wrappers: ['form-field']
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
name: 'date',
|
|
59
|
-
component: DateType,
|
|
60
|
-
wrappers: ['form-field']
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
name: 'select',
|
|
64
|
-
component: SelectType,
|
|
65
|
-
wrappers: ['form-field']
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
name: 'multiselect',
|
|
69
|
-
component: MultiselectType,
|
|
70
|
-
wrappers: ['form-field']
|
|
71
|
-
}
|
|
72
|
-
]
|
|
73
|
-
})
|
|
74
|
-
],
|
|
75
|
-
exports: [FormlyModule]
|
|
76
|
-
})
|
|
77
|
-
export class PrimeNGFormlyModule { }
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { Component } from '@angular/core';
|
|
2
|
-
import { CommonModule } from '@angular/common';
|
|
3
|
-
import { ReactiveFormsModule } from '@angular/forms';
|
|
4
|
-
import { FieldType, FormlyModule } from '@ngx-formly/core';
|
|
5
|
-
import { CheckboxModule } from 'primeng/checkbox';
|
|
6
|
-
|
|
7
|
-
@Component({
|
|
8
|
-
selector: 'formly-field-checkbox',
|
|
9
|
-
standalone: true,
|
|
10
|
-
imports: [
|
|
11
|
-
CommonModule,
|
|
12
|
-
ReactiveFormsModule,
|
|
13
|
-
FormlyModule,
|
|
14
|
-
CheckboxModule
|
|
15
|
-
],
|
|
16
|
-
template: `
|
|
17
|
-
<div class="checkbox-container">
|
|
18
|
-
<p-checkbox
|
|
19
|
-
[formControl]="$any(formControl)"
|
|
20
|
-
[formlyAttributes]="field"
|
|
21
|
-
[binary]="!props['options']"
|
|
22
|
-
[trueValue]="props['trueValue']"
|
|
23
|
-
[falseValue]="props['falseValue']">
|
|
24
|
-
{{props['checkboxLabel'] || ''}}
|
|
25
|
-
</p-checkbox>
|
|
26
|
-
</div>
|
|
27
|
-
`,
|
|
28
|
-
styles: [`
|
|
29
|
-
.checkbox-container {
|
|
30
|
-
display: flex;
|
|
31
|
-
align-items: center;
|
|
32
|
-
height: 40px;
|
|
33
|
-
}
|
|
34
|
-
`]
|
|
35
|
-
})
|
|
36
|
-
export class CheckboxType extends FieldType {
|
|
37
|
-
override defaultOptions = {
|
|
38
|
-
props: {
|
|
39
|
-
trueValue: true,
|
|
40
|
-
falseValue: false
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
}
|