@codex-ts/core-lib 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core-lib/fesm2022/core-lib.mjs +2349 -0
- package/dist/core-lib/fesm2022/core-lib.mjs.map +1 -0
- package/dist/core-lib/index.d.ts +29 -0
- package/dist/core-lib/lib/components/base-form.component.d.ts +62 -0
- package/dist/core-lib/lib/components/base-list-page.component.d.ts +132 -0
- package/dist/core-lib/lib/components/base-page.component.d.ts +25 -0
- package/dist/core-lib/lib/components/base-tabbed-form.component.d.ts +54 -0
- package/dist/core-lib/lib/components/formly/core-formly.module.d.ts +7 -0
- package/dist/core-lib/lib/components/formly/form-field.wrapper.d.ts +21 -0
- package/dist/core-lib/lib/components/formly/formly.constants.d.ts +13 -0
- package/dist/core-lib/lib/components/formly/types/checkbox.type.d.ts +12 -0
- package/dist/core-lib/lib/components/formly/types/date.type.d.ts +20 -0
- package/dist/core-lib/lib/components/formly/types/multiselect.type.d.ts +26 -0
- package/dist/core-lib/lib/components/formly/types/numeric-input.type.d.ts +17 -0
- package/dist/core-lib/lib/components/formly/types/radio.type.d.ts +13 -0
- package/dist/core-lib/lib/components/formly/types/select.type.d.ts +25 -0
- package/dist/core-lib/lib/components/formly/types/text-input.type.d.ts +12 -0
- package/dist/core-lib/lib/components/formly/types/textarea.type.d.ts +14 -0
- package/dist/core-lib/lib/constants/app.constants.d.ts +13 -0
- package/dist/core-lib/lib/constants/app.messages.d.ts +36 -0
- package/dist/core-lib/lib/enums/component-context.enum.d.ts +6 -0
- package/dist/core-lib/lib/enums/tabbed-form-type.enum.d.ts +4 -0
- package/dist/core-lib/lib/models/pagination/filter-criteria.interface.d.ts +9 -0
- package/dist/core-lib/lib/models/pagination/page-request.interface.d.ts +10 -0
- package/dist/core-lib/lib/models/pagination/page.interface.d.ts +9 -0
- package/dist/core-lib/lib/models/pagination/sort-criteria.interface.d.ts +4 -0
- package/dist/core-lib/lib/pipes/indian-date.pipe.d.ts +12 -0
- package/dist/core-lib/lib/services/base-crud.service.d.ts +36 -0
- package/dist/core-lib/lib/services/entity-registry.service.d.ts +26 -0
- package/dist/core-lib/lib/services/entity.service.d.ts +9 -0
- package/dist/core-lib/lib/services/enum-registry.service.d.ts +41 -0
- package/dist/core-lib/lib/services/formly-config.service.d.ts +11 -0
- package/dist/core-lib/lib/services/http-utility.service.d.ts +130 -0
- package/dist/core-lib/lib/services/notification.service.d.ts +13 -0
- package/dist/core-lib/lib/services/reference-data-provider.service.d.ts +8 -0
- package/dist/core-lib/lib/services/utils.service.d.ts +72 -0
- package/ng-package.json +7 -0
- package/package.json +63 -0
- package/src/index.ts +44 -0
- package/src/lib/components/base-form.component.ts +328 -0
- package/src/lib/components/base-list-page.component.ts +327 -0
- package/src/lib/components/base-page.component.ts +33 -0
- package/src/lib/components/base-tabbed-form.component.ts +345 -0
- package/src/lib/components/formly/core-formly.module.ts +34 -0
- package/src/lib/components/formly/field.interface.ts +15 -0
- package/src/lib/components/formly/form-field.wrapper.ts +94 -0
- package/src/lib/components/formly/formly.constants.ts +14 -0
- package/src/lib/components/formly/primeng-formly.module.ts +77 -0
- package/src/lib/components/formly/types/checkbox.type.ts +43 -0
- package/src/lib/components/formly/types/date.type.ts +88 -0
- package/src/lib/components/formly/types/multiselect.type.ts +61 -0
- package/src/lib/components/formly/types/numeric-input.type.ts +45 -0
- package/src/lib/components/formly/types/radio.type.ts +47 -0
- package/src/lib/components/formly/types/select.type.ts +54 -0
- package/src/lib/components/formly/types/text-input.type.ts +33 -0
- package/src/lib/components/formly/types/textarea.type.ts +37 -0
- package/src/lib/constants/app.constants.ts +15 -0
- package/src/lib/constants/app.messages.ts +36 -0
- package/src/lib/enums/component-context.enum.ts +6 -0
- package/src/lib/enums/tabbed-form-type.enum.ts +4 -0
- package/src/lib/models/pagination/filter-criteria.interface.ts +19 -0
- package/src/lib/models/pagination/page-request.interface.ts +13 -0
- package/src/lib/models/pagination/page.interface.ts +9 -0
- package/src/lib/models/pagination/sort-criteria.interface.ts +4 -0
- package/src/lib/pipes/indian-date.pipe.ts +46 -0
- package/src/lib/services/base-crud.service.ts +81 -0
- package/src/lib/services/entity-registry.service.ts +51 -0
- package/src/lib/services/entity.service.ts +10 -0
- package/src/lib/services/enum-registry.service.ts +66 -0
- package/src/lib/services/formly-config.service.ts +50 -0
- package/src/lib/services/http-utility.service.example.ts +253 -0
- package/src/lib/services/http-utility.service.md +175 -0
- package/src/lib/services/http-utility.service.ts +389 -0
- package/src/lib/services/notification.service.ts +35 -0
- package/src/lib/services/reference-data-provider.service.ts +28 -0
- package/src/lib/services/utils.service.ts +154 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { MessageService } from 'primeng/api';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
/**
|
|
6
|
+
* A general-purpose HTTP utility service that provides enhanced HTTP operations
|
|
7
|
+
* beyond the standard Angular HttpClient. This service includes features such as:
|
|
8
|
+
* - Loading state management
|
|
9
|
+
* - Standardized error handling
|
|
10
|
+
* - Request timeouts and retries
|
|
11
|
+
* - File upload capabilities
|
|
12
|
+
* - Batch request handling
|
|
13
|
+
*/
|
|
14
|
+
export declare class HttpUtilityService {
|
|
15
|
+
private http;
|
|
16
|
+
private messageService?;
|
|
17
|
+
private loading;
|
|
18
|
+
loading$: Observable<boolean>;
|
|
19
|
+
constructor(http: HttpClient, messageService?: MessageService);
|
|
20
|
+
/**
|
|
21
|
+
* Perform a GET request with enhanced options
|
|
22
|
+
* @param url The endpoint URL
|
|
23
|
+
* @param options Additional request options
|
|
24
|
+
* @returns An observable of the response
|
|
25
|
+
*/
|
|
26
|
+
get<T>(url: string, options?: {
|
|
27
|
+
params?: HttpParams | Record<string, string | number | boolean | readonly (string | number | boolean)[]>;
|
|
28
|
+
headers?: HttpHeaders | Record<string, string | string[]>;
|
|
29
|
+
responseType?: 'json';
|
|
30
|
+
withCredentials?: boolean;
|
|
31
|
+
retries?: number;
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
}): Observable<T>;
|
|
34
|
+
/**
|
|
35
|
+
* Perform a POST request with enhanced options
|
|
36
|
+
* @param url The endpoint URL
|
|
37
|
+
* @param body The request payload
|
|
38
|
+
* @param options Additional request options
|
|
39
|
+
* @returns An observable of the response
|
|
40
|
+
*/
|
|
41
|
+
post<T>(url: string, body: any, options?: {
|
|
42
|
+
params?: HttpParams | Record<string, string | number | boolean | readonly (string | number | boolean)[]>;
|
|
43
|
+
headers?: HttpHeaders | Record<string, string | string[]>;
|
|
44
|
+
responseType?: 'json';
|
|
45
|
+
withCredentials?: boolean;
|
|
46
|
+
retries?: number;
|
|
47
|
+
timeoutMs?: number;
|
|
48
|
+
}): Observable<T>;
|
|
49
|
+
/**
|
|
50
|
+
* Perform a PUT request with enhanced options
|
|
51
|
+
* @param url The endpoint URL
|
|
52
|
+
* @param body The request payload
|
|
53
|
+
* @param options Additional request options
|
|
54
|
+
* @returns An observable of the response
|
|
55
|
+
*/
|
|
56
|
+
put<T>(url: string, body: any, options?: {
|
|
57
|
+
params?: HttpParams | Record<string, string | number | boolean | readonly (string | number | boolean)[]>;
|
|
58
|
+
headers?: HttpHeaders | Record<string, string | string[]>;
|
|
59
|
+
responseType?: 'json';
|
|
60
|
+
withCredentials?: boolean;
|
|
61
|
+
retries?: number;
|
|
62
|
+
timeoutMs?: number;
|
|
63
|
+
}): Observable<T>;
|
|
64
|
+
/**
|
|
65
|
+
* Perform a DELETE request with enhanced options
|
|
66
|
+
* @param url The endpoint URL
|
|
67
|
+
* @param options Additional request options
|
|
68
|
+
* @returns An observable of the response
|
|
69
|
+
*/
|
|
70
|
+
delete<T>(url: string, options?: {
|
|
71
|
+
params?: HttpParams | Record<string, string | number | boolean | readonly (string | number | boolean)[]>;
|
|
72
|
+
headers?: HttpHeaders | Record<string, string | string[]>;
|
|
73
|
+
responseType?: 'json';
|
|
74
|
+
withCredentials?: boolean;
|
|
75
|
+
retries?: number;
|
|
76
|
+
timeoutMs?: number;
|
|
77
|
+
}): Observable<T>;
|
|
78
|
+
/**
|
|
79
|
+
* Upload a file with optional additional data
|
|
80
|
+
* @param url The upload endpoint URL
|
|
81
|
+
* @param file The file to upload
|
|
82
|
+
* @param additionalData Additional form data to include
|
|
83
|
+
* @param options Additional request options
|
|
84
|
+
* @returns An observable of the response
|
|
85
|
+
*/
|
|
86
|
+
uploadFile<T>(url: string, file: File, additionalData?: Record<string, any>, options?: {
|
|
87
|
+
headers?: HttpHeaders | Record<string, string | string[]>;
|
|
88
|
+
withCredentials?: boolean;
|
|
89
|
+
retries?: number;
|
|
90
|
+
timeoutMs?: number;
|
|
91
|
+
onProgress?: (percentage: number) => void;
|
|
92
|
+
}): Observable<T>;
|
|
93
|
+
/**
|
|
94
|
+
* Execute multiple HTTP requests in parallel
|
|
95
|
+
* @param requests Array of HTTP request observables
|
|
96
|
+
* @returns An observable that emits when all requests complete
|
|
97
|
+
*/
|
|
98
|
+
batchRequests<T>(requests: Observable<T>[]): Observable<T[]>;
|
|
99
|
+
/**
|
|
100
|
+
* Download a file from the server
|
|
101
|
+
* @param url The file URL
|
|
102
|
+
* @param filename Optional filename to save as
|
|
103
|
+
* @param options Additional request options
|
|
104
|
+
*/
|
|
105
|
+
downloadFile(url: string, filename?: string, options?: {
|
|
106
|
+
params?: HttpParams | Record<string, string | number | boolean | readonly (string | number | boolean)[]>;
|
|
107
|
+
headers?: HttpHeaders | Record<string, string | string[]>;
|
|
108
|
+
withCredentials?: boolean;
|
|
109
|
+
timeoutMs?: number;
|
|
110
|
+
}): Observable<Blob>;
|
|
111
|
+
/**
|
|
112
|
+
* Create parameter string from an object
|
|
113
|
+
* @param params Object containing parameters
|
|
114
|
+
* @returns HttpParams object
|
|
115
|
+
*/
|
|
116
|
+
createParams(params: Record<string, any>): HttpParams;
|
|
117
|
+
/**
|
|
118
|
+
* Standardized error handler for HTTP requests
|
|
119
|
+
* @param error The HTTP error
|
|
120
|
+
* @returns An observable that errors with the processed error object
|
|
121
|
+
*/
|
|
122
|
+
private handleError;
|
|
123
|
+
/**
|
|
124
|
+
* Display an error message using the message service
|
|
125
|
+
* @param message Error message to display
|
|
126
|
+
*/
|
|
127
|
+
private showError;
|
|
128
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<HttpUtilityService, never>;
|
|
129
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<HttpUtilityService>;
|
|
130
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { MessageService } from 'primeng/api';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export declare class NotificationService {
|
|
4
|
+
private messageService;
|
|
5
|
+
private messageSubject;
|
|
6
|
+
message$: import("rxjs").Observable<any>;
|
|
7
|
+
constructor(messageService: MessageService);
|
|
8
|
+
showSuccess(message: string): void;
|
|
9
|
+
showError(message: string): void;
|
|
10
|
+
clearMessage(): void;
|
|
11
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<NotificationService, never>;
|
|
12
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<NotificationService>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
export declare class ReferenceDataProviderService {
|
|
3
|
+
private endpoints;
|
|
4
|
+
setEndpoints(endpoints: Record<string, string>): void;
|
|
5
|
+
getReferenceEndpoint(entityName: string): string | null;
|
|
6
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ReferenceDataProviderService, never>;
|
|
7
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<ReferenceDataProviderService>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { MessageService } from 'primeng/api';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
/**
|
|
4
|
+
* A utility service that provides common reusable functionality
|
|
5
|
+
* such as UUID validation and error message display
|
|
6
|
+
*/
|
|
7
|
+
export declare class UtilsService {
|
|
8
|
+
/**
|
|
9
|
+
* Validates if a string is a valid UUID
|
|
10
|
+
* @param uuid The string to validate
|
|
11
|
+
* @returns boolean indicating if the string is a valid UUID
|
|
12
|
+
*/
|
|
13
|
+
isValidUUID(uuid: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Shows an error message using PrimeNG's MessageService
|
|
16
|
+
* @param message The error message to display
|
|
17
|
+
* @param messageService The MessageService instance to use
|
|
18
|
+
*/
|
|
19
|
+
showError(message: string, messageService?: MessageService): void;
|
|
20
|
+
/**
|
|
21
|
+
* Shows a success message using PrimeNG's MessageService
|
|
22
|
+
* @param message The success message to display
|
|
23
|
+
* @param messageService The MessageService instance to use
|
|
24
|
+
*/
|
|
25
|
+
showSuccess(message: string, messageService?: MessageService): void;
|
|
26
|
+
/**
|
|
27
|
+
* Gets the display value for an enum key or array of enum keys
|
|
28
|
+
* @param enumType The enum type
|
|
29
|
+
* @param key The enum key or array of enum keys
|
|
30
|
+
* @returns The display value for the enum key or comma-separated list of display values for array of keys
|
|
31
|
+
*/
|
|
32
|
+
getEnumValue<T>(enumType: T, key: string | string[]): string;
|
|
33
|
+
/**
|
|
34
|
+
* Gets all values from an enum type for use in formly select fields
|
|
35
|
+
* @param enumType The enum type to get values from
|
|
36
|
+
* @returns Array of {label, value} pairs for formly select options
|
|
37
|
+
*/
|
|
38
|
+
getEnumValues<T extends {
|
|
39
|
+
[key: string]: string | number;
|
|
40
|
+
}>(enumType: T): Array<{
|
|
41
|
+
label: string;
|
|
42
|
+
value: string;
|
|
43
|
+
}>;
|
|
44
|
+
/**
|
|
45
|
+
* Converts a camelCase string to Title Case with spaces
|
|
46
|
+
* @param camelCase The camelCase string to convert
|
|
47
|
+
* @returns The Title Case string
|
|
48
|
+
*/
|
|
49
|
+
camelCaseToTitleCase(camelCase: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Converts a string to kebab-case
|
|
52
|
+
* @param input The string to convert
|
|
53
|
+
* @returns The kebab-case string
|
|
54
|
+
*/
|
|
55
|
+
toKebabCase(input: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* Converts a string to camelCase.
|
|
58
|
+
* Example: "loan-application" becomes "loanApplication"
|
|
59
|
+
* @param input The string to convert
|
|
60
|
+
* @returns The camelCase string
|
|
61
|
+
*/
|
|
62
|
+
toCamelCase(input: string): string;
|
|
63
|
+
/**
|
|
64
|
+
* Removes double forward slashes from an endpoint string while preserving the protocol
|
|
65
|
+
* e.g. "http://localhost:8080//api//v1//users" -> "http://localhost:8080/api/v1/users"
|
|
66
|
+
* @param endpoint The endpoint string to clean
|
|
67
|
+
* @returns The cleaned endpoint string without double slashes
|
|
68
|
+
*/
|
|
69
|
+
removeDoubleSlashes(endpoint: string): string;
|
|
70
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<UtilsService, never>;
|
|
71
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<UtilsService>;
|
|
72
|
+
}
|
package/ng-package.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codex-ts/core-lib",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "ng-packagr -p ng-package.json"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [],
|
|
10
|
+
"author": "",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"tslib": "^2.3.0"
|
|
14
|
+
},
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"@angular/animations": "^19.1.5",
|
|
17
|
+
"@angular/common": "^19.1.5",
|
|
18
|
+
"@angular/compiler": "^19.1.5",
|
|
19
|
+
"@angular/core": "^19.1.5",
|
|
20
|
+
"@angular/forms": "^19.1.5",
|
|
21
|
+
"@angular/platform-browser": "^19.1.5",
|
|
22
|
+
"@angular/platform-browser-dynamic": "^19.1.5",
|
|
23
|
+
"@angular/router": "^19.1.5",
|
|
24
|
+
"@angular/cdk": "^19.1.5",
|
|
25
|
+
"@ngx-formly/core": "^6.3.12",
|
|
26
|
+
"@primeng/themes": "^19.0.6",
|
|
27
|
+
"primeflex": "^3.3.1",
|
|
28
|
+
"primeicons": "^7.0.0",
|
|
29
|
+
"primeng": "^19.0.6",
|
|
30
|
+
"rxjs": "~7.8.0",
|
|
31
|
+
"zone.js": "~0.15.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@angular-devkit/build-angular": "^19.1.6",
|
|
35
|
+
"@angular/cli": "^19.0.6",
|
|
36
|
+
"@angular/compiler-cli": "^19.1.5",
|
|
37
|
+
"@types/jasmine": "~5.1.0",
|
|
38
|
+
"jasmine-core": "~5.4.0",
|
|
39
|
+
"karma": "~6.4.0",
|
|
40
|
+
"karma-chrome-launcher": "~3.2.0",
|
|
41
|
+
"karma-coverage": "~2.2.0",
|
|
42
|
+
"karma-jasmine": "~5.1.0",
|
|
43
|
+
"karma-jasmine-html-reporter": "~2.1.0",
|
|
44
|
+
"ng-packagr": "^19.1.0",
|
|
45
|
+
"typescript": "~5.6.2",
|
|
46
|
+
"@angular/animations": "^19.1.5",
|
|
47
|
+
"@angular/common": "^19.1.5",
|
|
48
|
+
"@angular/compiler": "^19.1.5",
|
|
49
|
+
"@angular/core": "^19.1.5",
|
|
50
|
+
"@angular/forms": "^19.1.5",
|
|
51
|
+
"@angular/platform-browser": "^19.1.5",
|
|
52
|
+
"@angular/platform-browser-dynamic": "^19.1.5",
|
|
53
|
+
"@angular/router": "^19.1.5",
|
|
54
|
+
"@angular/cdk": "^19.1.5",
|
|
55
|
+
"@ngx-formly/core": "^6.3.12",
|
|
56
|
+
"@primeng/themes": "^19.0.6",
|
|
57
|
+
"primeflex": "^3.3.1",
|
|
58
|
+
"primeicons": "^7.0.0",
|
|
59
|
+
"primeng": "^19.0.6",
|
|
60
|
+
"rxjs": "~7.8.0",
|
|
61
|
+
"zone.js": "~0.15.0"
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Config
|
|
2
|
+
|
|
3
|
+
// Services
|
|
4
|
+
export * from './lib/services/formly-config.service';
|
|
5
|
+
export * from './lib/services/utils.service';
|
|
6
|
+
export * from './lib/services/notification.service';
|
|
7
|
+
export * from './lib/services/enum-registry.service';
|
|
8
|
+
export * from './lib/services/entity-registry.service';
|
|
9
|
+
export * from './lib/services/reference-data-provider.service';
|
|
10
|
+
export * from './lib/services/base-crud.service';
|
|
11
|
+
export * from './lib/services/http-utility.service';
|
|
12
|
+
export * from './lib/services/entity.service';
|
|
13
|
+
|
|
14
|
+
// Enums
|
|
15
|
+
export * from './lib/enums/component-context.enum';
|
|
16
|
+
export * from './lib/enums/tabbed-form-type.enum';
|
|
17
|
+
|
|
18
|
+
// Components
|
|
19
|
+
export * from './lib/components/base-form.component';
|
|
20
|
+
export * from './lib/components/base-page.component';
|
|
21
|
+
export * from './lib/components/base-list-page.component';
|
|
22
|
+
export * from './lib/components/base-tabbed-form.component';
|
|
23
|
+
|
|
24
|
+
// Constants
|
|
25
|
+
export * from './lib/constants/app.constants';
|
|
26
|
+
export * from './lib/constants/app.messages';
|
|
27
|
+
|
|
28
|
+
// Models
|
|
29
|
+
export * from './lib/models/pagination/page.interface';
|
|
30
|
+
export * from './lib/models/pagination/page-request.interface';
|
|
31
|
+
|
|
32
|
+
// Pipes
|
|
33
|
+
export * from './lib/pipes/indian-date.pipe';
|
|
34
|
+
|
|
35
|
+
// Formly Components
|
|
36
|
+
export * from './lib/components/formly/core-formly.module';
|
|
37
|
+
export * from './lib/components/formly/form-field.wrapper';
|
|
38
|
+
export * from './lib/components/formly/types/text-input.type';
|
|
39
|
+
export * from './lib/components/formly/types/select.type';
|
|
40
|
+
export * from './lib/components/formly/types/radio.type';
|
|
41
|
+
export * from './lib/components/formly/types/numeric-input.type';
|
|
42
|
+
export * from './lib/components/formly/types/multiselect.type';
|
|
43
|
+
export * from './lib/components/formly/types/date.type';
|
|
44
|
+
export * from './lib/components/formly/types/checkbox.type';
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { Component, OnInit, Injectable, Input } from '@angular/core';
|
|
2
|
+
import { ComponentContext } from '../enums/component-context.enum';
|
|
3
|
+
import { FormGroup } from '@angular/forms';
|
|
4
|
+
import { ActivatedRoute, Router } from '@angular/router';
|
|
5
|
+
import { FormlyFieldConfig } from '@ngx-formly/core';
|
|
6
|
+
import { BasePageComponent } from './base-page.component';
|
|
7
|
+
import { UtilsService } from '../services/utils.service';
|
|
8
|
+
import { NotificationService } from '../services/notification.service';
|
|
9
|
+
import { AppMessages } from '../constants/app.messages';
|
|
10
|
+
import { AppConstants, PageMode } from '../constants/app.constants';
|
|
11
|
+
import { Observable, filter, take } from 'rxjs';
|
|
12
|
+
import { FormlyConfigService } from '../services/formly-config.service';
|
|
13
|
+
import { EntityService } from '../services/entity.service';
|
|
14
|
+
|
|
15
|
+
@Injectable()
|
|
16
|
+
@Component({
|
|
17
|
+
template: ''
|
|
18
|
+
})
|
|
19
|
+
export abstract class BaseFormComponent<T extends { uuid: string }>
|
|
20
|
+
extends BasePageComponent implements OnInit {
|
|
21
|
+
|
|
22
|
+
form = new FormGroup({});
|
|
23
|
+
fields: FormlyFieldConfig[] = [];
|
|
24
|
+
isNew = true;
|
|
25
|
+
mode: PageMode = AppConstants.PAGE_MODE.CREATE;
|
|
26
|
+
currentFormData: any = {};
|
|
27
|
+
protected originalFormData: T | null = null;
|
|
28
|
+
protected hasChanges: boolean = false;
|
|
29
|
+
@Input() context: ComponentContext = ComponentContext.STANDALONE;
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
protected route: ActivatedRoute,
|
|
33
|
+
protected router: Router,
|
|
34
|
+
protected override notificationService: NotificationService,
|
|
35
|
+
protected override utilsService: UtilsService,
|
|
36
|
+
protected formlyConfigService: FormlyConfigService
|
|
37
|
+
) {
|
|
38
|
+
super(utilsService, notificationService);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
ngOnInit() {
|
|
42
|
+
try {
|
|
43
|
+
this.fields = this.getFormlyFieldConfig();
|
|
44
|
+
|
|
45
|
+
this.form.valueChanges.subscribe(() => {
|
|
46
|
+
console.log('[BaseFormComponent] Form value changed:', this.form.value);
|
|
47
|
+
this.currentFormData = { ...this.form.value };
|
|
48
|
+
this.hasChanges = this.hasFormChanges(this.currentFormData);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Use queryParams instead of params
|
|
52
|
+
this.route.queryParams.pipe(take(1)).subscribe({
|
|
53
|
+
next: params => this.initializeForm(params),
|
|
54
|
+
error: error => {
|
|
55
|
+
console.error('Error initializing form:', error);
|
|
56
|
+
this.showError(AppMessages.FORM.ERROR.INIT);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error('Error in ngOnInit:', error);
|
|
61
|
+
this.showError(AppMessages.FORM.ERROR.INIT);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private initializeFormData() {
|
|
66
|
+
console.log('[BaseFormComponent] Initializing form data');
|
|
67
|
+
console.log('[BaseFormComponent] Current mode:', this.mode);
|
|
68
|
+
|
|
69
|
+
if (this.mode === AppConstants.PAGE_MODE.CREATE) {
|
|
70
|
+
console.log('[BaseFormComponent] Create mode, using defaults');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const state = {
|
|
75
|
+
...this.router.getCurrentNavigation()?.extras?.state,
|
|
76
|
+
...window.history.state,
|
|
77
|
+
...this.route.snapshot.data
|
|
78
|
+
};
|
|
79
|
+
console.log('[BaseFormComponent] Combined state:', state);
|
|
80
|
+
console.log('[BaseFormComponent] Final state being used:', state);
|
|
81
|
+
|
|
82
|
+
if (state && 'formData' in state) {
|
|
83
|
+
console.log('[BaseFormComponent] Found form data in state:', state['formData']);
|
|
84
|
+
this.patchFormData(state['formData']);
|
|
85
|
+
} else {
|
|
86
|
+
console.log('[BaseFormComponent] No form data, loading from UUID');
|
|
87
|
+
this.loadFromUuid();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private loadFromUuid() {
|
|
92
|
+
this.route.queryParams.pipe(take(1)).subscribe(params => {
|
|
93
|
+
const uuid = params[AppConstants.FORM.UUID];
|
|
94
|
+
if (!uuid) {
|
|
95
|
+
throw new Error(`UUID is required for ${this.mode} mode when no formData is provided`);
|
|
96
|
+
}
|
|
97
|
+
this.loadEntity(uuid);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private initializeForm(params: any) {
|
|
102
|
+
console.log('[BaseFormComponent] Initializing form with params:', params);
|
|
103
|
+
const routeMode = params[AppConstants.FORM.MODE];
|
|
104
|
+
this.initializeFormMode(routeMode);
|
|
105
|
+
this.setupFormState();
|
|
106
|
+
|
|
107
|
+
// Check for context in router state
|
|
108
|
+
const state = window.history.state;
|
|
109
|
+
if (state?.context) {
|
|
110
|
+
this.context = state.context;
|
|
111
|
+
console.log('[BaseFormComponent] Context set from router state:', this.context);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this.initializeFormData();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
protected patchFormData(data: any) {
|
|
118
|
+
console.log('[BaseFormComponent] Patching form data:', data);
|
|
119
|
+
if (data) {
|
|
120
|
+
if (!this.fields || this.fields.length === 0) {
|
|
121
|
+
console.warn('[BaseFormComponent] Fields not initialized yet, reinitializing');
|
|
122
|
+
this.fields = this.getFormlyFieldConfig();
|
|
123
|
+
}
|
|
124
|
+
this.currentFormData = { ...data };
|
|
125
|
+
this.originalFormData = data;
|
|
126
|
+
this.isNew = false;
|
|
127
|
+
|
|
128
|
+
console.log('[BaseFormComponent] Form data updated:', this.currentFormData);
|
|
129
|
+
console.log('[BaseFormComponent] Fields available:', this.fields);
|
|
130
|
+
|
|
131
|
+
setTimeout(() => {
|
|
132
|
+
if (!this.form.valid) {
|
|
133
|
+
console.warn('[BaseFormComponent] Form validation issues after data patch');
|
|
134
|
+
}
|
|
135
|
+
console.log('[BaseFormComponent] Form state after sync:', {
|
|
136
|
+
valid: this.form.valid,
|
|
137
|
+
values: this.form.value,
|
|
138
|
+
currentData: this.currentFormData
|
|
139
|
+
});
|
|
140
|
+
}, 100);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private initializeFormMode(routeMode: string | undefined) {
|
|
145
|
+
if (!routeMode) {
|
|
146
|
+
this.mode = AppConstants.PAGE_MODE.CREATE;
|
|
147
|
+
} else if (this.isValidPageMode(routeMode)) {
|
|
148
|
+
this.mode = routeMode;
|
|
149
|
+
} else {
|
|
150
|
+
throw new Error(`Invalid page mode: ${routeMode}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private setupFormState() {
|
|
155
|
+
console.log('[BaseFormComponent] Setting up form state, mode:', this.mode);
|
|
156
|
+
this.fields = this.getFormlyFieldConfig();
|
|
157
|
+
|
|
158
|
+
if (this.mode === AppConstants.PAGE_MODE.CREATE) {
|
|
159
|
+
console.log('[BaseFormComponent] Create mode setup');
|
|
160
|
+
this.isNew = true;
|
|
161
|
+
this.loading = false;
|
|
162
|
+
this.originalFormData = null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (this.mode === AppConstants.PAGE_MODE.VIEW) {
|
|
166
|
+
console.log('[BaseFormComponent] View mode setup');
|
|
167
|
+
this.form.disable();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
protected getFormlyFieldConfig(): FormlyFieldConfig[] {
|
|
172
|
+
return this.formlyConfigService.getFormConfig(
|
|
173
|
+
this.getJsonFields(),
|
|
174
|
+
this.mode,
|
|
175
|
+
this.getDropdownOptions()
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
protected onSubmitRoute(): string[] {
|
|
180
|
+
return [`/${this.getEntityName().toLowerCase()}s`];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
protected getEntityOperations(): {
|
|
184
|
+
create: (data: Omit<T, typeof AppConstants.FORM.UUID>) => Observable<T>;
|
|
185
|
+
update: (uuid: string, data: Omit<T, typeof AppConstants.FORM.UUID>) => Observable<T>;
|
|
186
|
+
getById: (uuid: string) => Observable<T>;
|
|
187
|
+
} {
|
|
188
|
+
const entityService = this.getEntityService();
|
|
189
|
+
return {
|
|
190
|
+
create: (data) => entityService.create(data),
|
|
191
|
+
update: (uuid, data) => entityService.update(uuid, { ...data, uuid } as T),
|
|
192
|
+
getById: (uuid) => entityService.getById(uuid)
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
protected onSubmit() {
|
|
197
|
+
console.log('[BaseFormComponent] Form submission:', {
|
|
198
|
+
formValid: this.form.valid,
|
|
199
|
+
formData: this.currentFormData,
|
|
200
|
+
formState: this.form.value
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
if (!this.currentFormData || Object.keys(this.currentFormData).length === 0) {
|
|
204
|
+
this.notificationService.showError('No data to save');
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (this.form.valid) {
|
|
209
|
+
if (this.isNew) {
|
|
210
|
+
console.log('[BaseFormComponent] Saving new entity');
|
|
211
|
+
this.saveEntity();
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const hasChanges = this.hasFormChanges(this.currentFormData);
|
|
216
|
+
console.log('[BaseFormComponent] Change detection:', {
|
|
217
|
+
hasChanges,
|
|
218
|
+
currentData: this.currentFormData,
|
|
219
|
+
originalData: this.originalFormData
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
if (!hasChanges) {
|
|
223
|
+
this.notificationService.showSuccess('No changes to save');
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
console.log('[BaseFormComponent] Saving changes');
|
|
228
|
+
this.saveEntity();
|
|
229
|
+
} else {
|
|
230
|
+
console.warn('[BaseFormComponent] Form validation failed:', this.form.errors);
|
|
231
|
+
this.notificationService.showError('Please check form errors');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
protected hasFormChanges(currentData: any): boolean {
|
|
236
|
+
if (!this.originalFormData || this.isNew) {
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
console.log('[BaseFormComponent] Checking for changes:', {
|
|
241
|
+
currentData,
|
|
242
|
+
originalData: this.originalFormData
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const hasChanges = Object.keys(currentData).some(key => {
|
|
246
|
+
const currentValue = currentData[key];
|
|
247
|
+
const originalValue = (this.originalFormData as any)[key];
|
|
248
|
+
const changed = currentValue !== originalValue;
|
|
249
|
+
|
|
250
|
+
if (changed) {
|
|
251
|
+
console.log(`[BaseFormComponent] Field '${key}' changed:`, {
|
|
252
|
+
from: originalValue,
|
|
253
|
+
to: currentValue
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return changed;
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
console.log('[BaseFormComponent] Changes detected:', hasChanges);
|
|
261
|
+
return hasChanges;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
protected saveEntity() {
|
|
265
|
+
this.loading = true;
|
|
266
|
+
const formData = this.currentFormData as Omit<T, typeof AppConstants.FORM.UUID>;
|
|
267
|
+
const entityName = this.getEntityName();
|
|
268
|
+
const entityOperations = this.getEntityOperations();
|
|
269
|
+
|
|
270
|
+
const request$ = this.isNew
|
|
271
|
+
? entityOperations.create(formData)
|
|
272
|
+
: entityOperations.update(this.originalFormData!.uuid, formData);
|
|
273
|
+
|
|
274
|
+
request$.subscribe({
|
|
275
|
+
next: () => {
|
|
276
|
+
this.loading = false;
|
|
277
|
+
this.notificationService.showSuccess(`${entityName} ${AppMessages.FORM.SUCCESS.SAVE}`);
|
|
278
|
+
this.router.navigate(this.onSubmitRoute());
|
|
279
|
+
},
|
|
280
|
+
error: (error: any) => {
|
|
281
|
+
const errorMessage = error.error?.message || `${AppMessages.FORM.ERROR.SAVE} ${entityName}`;
|
|
282
|
+
this.showError(errorMessage);
|
|
283
|
+
this.loading = false;
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
protected loadEntity(uuid: string) {
|
|
289
|
+
this.loading = true;
|
|
290
|
+
this.isNew = false;
|
|
291
|
+
const entityName = this.getEntityName();
|
|
292
|
+
|
|
293
|
+
this.getEntityOperations().getById(uuid).subscribe({
|
|
294
|
+
next: (data: T) => {
|
|
295
|
+
this.originalFormData = data;
|
|
296
|
+
this.currentFormData = { ...data };
|
|
297
|
+
this.loading = false;
|
|
298
|
+
console.log('[BaseFormComponent] Original data loaded:', this.originalFormData);
|
|
299
|
+
console.log('[BaseFormComponent] Form data updated:', this.currentFormData);
|
|
300
|
+
},
|
|
301
|
+
error: () => {
|
|
302
|
+
this.showError(`${AppMessages.FORM.ERROR.LOAD} ${entityName}`);
|
|
303
|
+
this.loading = false;
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
protected abstract getEntityService(): EntityService<T>;
|
|
309
|
+
|
|
310
|
+
private isValidPageMode(mode: string): mode is PageMode {
|
|
311
|
+
return Object.values(AppConstants.PAGE_MODE).includes(mode as PageMode);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
protected getDropdownOptions(): Record<string, any> {
|
|
315
|
+
return {};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
protected abstract getJsonFields(): any;
|
|
319
|
+
protected abstract getEntityName(): string;
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Checks if the component is running in a specific context
|
|
323
|
+
* @param contextType The context type to check against
|
|
324
|
+
*/
|
|
325
|
+
protected isInContext(contextType: ComponentContext): boolean {
|
|
326
|
+
return this.context === contextType;
|
|
327
|
+
}
|
|
328
|
+
}
|