@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.
Files changed (76) hide show
  1. package/dist/core-lib/fesm2022/core-lib.mjs +2349 -0
  2. package/dist/core-lib/fesm2022/core-lib.mjs.map +1 -0
  3. package/dist/core-lib/index.d.ts +29 -0
  4. package/dist/core-lib/lib/components/base-form.component.d.ts +62 -0
  5. package/dist/core-lib/lib/components/base-list-page.component.d.ts +132 -0
  6. package/dist/core-lib/lib/components/base-page.component.d.ts +25 -0
  7. package/dist/core-lib/lib/components/base-tabbed-form.component.d.ts +54 -0
  8. package/dist/core-lib/lib/components/formly/core-formly.module.d.ts +7 -0
  9. package/dist/core-lib/lib/components/formly/form-field.wrapper.d.ts +21 -0
  10. package/dist/core-lib/lib/components/formly/formly.constants.d.ts +13 -0
  11. package/dist/core-lib/lib/components/formly/types/checkbox.type.d.ts +12 -0
  12. package/dist/core-lib/lib/components/formly/types/date.type.d.ts +20 -0
  13. package/dist/core-lib/lib/components/formly/types/multiselect.type.d.ts +26 -0
  14. package/dist/core-lib/lib/components/formly/types/numeric-input.type.d.ts +17 -0
  15. package/dist/core-lib/lib/components/formly/types/radio.type.d.ts +13 -0
  16. package/dist/core-lib/lib/components/formly/types/select.type.d.ts +25 -0
  17. package/dist/core-lib/lib/components/formly/types/text-input.type.d.ts +12 -0
  18. package/dist/core-lib/lib/components/formly/types/textarea.type.d.ts +14 -0
  19. package/dist/core-lib/lib/constants/app.constants.d.ts +13 -0
  20. package/dist/core-lib/lib/constants/app.messages.d.ts +36 -0
  21. package/dist/core-lib/lib/enums/component-context.enum.d.ts +6 -0
  22. package/dist/core-lib/lib/enums/tabbed-form-type.enum.d.ts +4 -0
  23. package/dist/core-lib/lib/models/pagination/filter-criteria.interface.d.ts +9 -0
  24. package/dist/core-lib/lib/models/pagination/page-request.interface.d.ts +10 -0
  25. package/dist/core-lib/lib/models/pagination/page.interface.d.ts +9 -0
  26. package/dist/core-lib/lib/models/pagination/sort-criteria.interface.d.ts +4 -0
  27. package/dist/core-lib/lib/pipes/indian-date.pipe.d.ts +12 -0
  28. package/dist/core-lib/lib/services/base-crud.service.d.ts +36 -0
  29. package/dist/core-lib/lib/services/entity-registry.service.d.ts +26 -0
  30. package/dist/core-lib/lib/services/entity.service.d.ts +9 -0
  31. package/dist/core-lib/lib/services/enum-registry.service.d.ts +41 -0
  32. package/dist/core-lib/lib/services/formly-config.service.d.ts +11 -0
  33. package/dist/core-lib/lib/services/http-utility.service.d.ts +130 -0
  34. package/dist/core-lib/lib/services/notification.service.d.ts +13 -0
  35. package/dist/core-lib/lib/services/reference-data-provider.service.d.ts +8 -0
  36. package/dist/core-lib/lib/services/utils.service.d.ts +72 -0
  37. package/ng-package.json +7 -0
  38. package/package.json +63 -0
  39. package/src/index.ts +44 -0
  40. package/src/lib/components/base-form.component.ts +328 -0
  41. package/src/lib/components/base-list-page.component.ts +327 -0
  42. package/src/lib/components/base-page.component.ts +33 -0
  43. package/src/lib/components/base-tabbed-form.component.ts +345 -0
  44. package/src/lib/components/formly/core-formly.module.ts +34 -0
  45. package/src/lib/components/formly/field.interface.ts +15 -0
  46. package/src/lib/components/formly/form-field.wrapper.ts +94 -0
  47. package/src/lib/components/formly/formly.constants.ts +14 -0
  48. package/src/lib/components/formly/primeng-formly.module.ts +77 -0
  49. package/src/lib/components/formly/types/checkbox.type.ts +43 -0
  50. package/src/lib/components/formly/types/date.type.ts +88 -0
  51. package/src/lib/components/formly/types/multiselect.type.ts +61 -0
  52. package/src/lib/components/formly/types/numeric-input.type.ts +45 -0
  53. package/src/lib/components/formly/types/radio.type.ts +47 -0
  54. package/src/lib/components/formly/types/select.type.ts +54 -0
  55. package/src/lib/components/formly/types/text-input.type.ts +33 -0
  56. package/src/lib/components/formly/types/textarea.type.ts +37 -0
  57. package/src/lib/constants/app.constants.ts +15 -0
  58. package/src/lib/constants/app.messages.ts +36 -0
  59. package/src/lib/enums/component-context.enum.ts +6 -0
  60. package/src/lib/enums/tabbed-form-type.enum.ts +4 -0
  61. package/src/lib/models/pagination/filter-criteria.interface.ts +19 -0
  62. package/src/lib/models/pagination/page-request.interface.ts +13 -0
  63. package/src/lib/models/pagination/page.interface.ts +9 -0
  64. package/src/lib/models/pagination/sort-criteria.interface.ts +4 -0
  65. package/src/lib/pipes/indian-date.pipe.ts +46 -0
  66. package/src/lib/services/base-crud.service.ts +81 -0
  67. package/src/lib/services/entity-registry.service.ts +51 -0
  68. package/src/lib/services/entity.service.ts +10 -0
  69. package/src/lib/services/enum-registry.service.ts +66 -0
  70. package/src/lib/services/formly-config.service.ts +50 -0
  71. package/src/lib/services/http-utility.service.example.ts +253 -0
  72. package/src/lib/services/http-utility.service.md +175 -0
  73. package/src/lib/services/http-utility.service.ts +389 -0
  74. package/src/lib/services/notification.service.ts +35 -0
  75. package/src/lib/services/reference-data-provider.service.ts +28 -0
  76. 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
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "../../dist/@codex-ts/core-lib",
4
+ "lib": {
5
+ "entryFile": "src/index.ts"
6
+ }
7
+ }
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
+ }