@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,36 @@
|
|
|
1
|
+
export const AppMessages = {
|
|
2
|
+
COMMON: {
|
|
3
|
+
ERROR: {
|
|
4
|
+
GENERAL: 'An error occurred',
|
|
5
|
+
INVALID_INPUT: 'Invalid input provided',
|
|
6
|
+
REQUIRED_FIELD: 'This field is required'
|
|
7
|
+
},
|
|
8
|
+
SUCCESS: {
|
|
9
|
+
SAVE: 'saved successfully',
|
|
10
|
+
UPDATE: 'updated successfully',
|
|
11
|
+
DELETE: 'deleted successfully'
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
FORM: {
|
|
15
|
+
ERROR: {
|
|
16
|
+
INIT: 'Failed to initialize form',
|
|
17
|
+
SAVE: 'Failed to save',
|
|
18
|
+
LOAD: 'Failed to load',
|
|
19
|
+
VALIDATION: 'Please fill in all required fields correctly'
|
|
20
|
+
},
|
|
21
|
+
SUCCESS: {
|
|
22
|
+
SAVE: 'saved successfully'
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
LIST: {
|
|
26
|
+
ERROR: {
|
|
27
|
+
LOAD: 'Failed to load',
|
|
28
|
+
DELETE: 'Failed to delete',
|
|
29
|
+
NAVIGATION: 'Navigation failed',
|
|
30
|
+
INVALID_ID: 'Cannot perform action without ID'
|
|
31
|
+
},
|
|
32
|
+
SUCCESS: {
|
|
33
|
+
DELETE: 'deleted successfully'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
} as const;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matches backend FilterCriteria and FilterOperator
|
|
3
|
+
*/
|
|
4
|
+
export type FilterOperator =
|
|
5
|
+
| 'EQUALS'
|
|
6
|
+
| 'NOT_EQUALS'
|
|
7
|
+
| 'CONTAINS'
|
|
8
|
+
| 'STARTS_WITH'
|
|
9
|
+
| 'ENDS_WITH'
|
|
10
|
+
| 'LESS_THAN'
|
|
11
|
+
| 'LESS_THAN_EQUALS'
|
|
12
|
+
| 'GREATER_THAN'
|
|
13
|
+
| 'GREATER_THAN_EQUALS';
|
|
14
|
+
|
|
15
|
+
export interface FilterCriteria {
|
|
16
|
+
field: string;
|
|
17
|
+
value: string; // Backend expects all values as strings
|
|
18
|
+
operator: FilterOperator;
|
|
19
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FilterCriteria } from './filter-criteria.interface';
|
|
2
|
+
import { SortCriteria } from './sort-criteria.interface';
|
|
3
|
+
|
|
4
|
+
export interface PageRequest {
|
|
5
|
+
page?: number;
|
|
6
|
+
size?: number;
|
|
7
|
+
sorts?: SortCriteria[];
|
|
8
|
+
filters?: FilterCriteria[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Re-export for convenience
|
|
12
|
+
export type { FilterCriteria } from './filter-criteria.interface';
|
|
13
|
+
export type { SortCriteria } from './sort-criteria.interface';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Pipe, PipeTransform } from '@angular/core';
|
|
2
|
+
import { DatePipe } from '@angular/common';
|
|
3
|
+
|
|
4
|
+
type DateFormat = 'default' | 'short' | 'medium';
|
|
5
|
+
|
|
6
|
+
@Pipe({
|
|
7
|
+
name: 'indianDate',
|
|
8
|
+
standalone: true
|
|
9
|
+
})
|
|
10
|
+
export class IndianDatePipe implements PipeTransform {
|
|
11
|
+
constructor(private datePipe: DatePipe) {}
|
|
12
|
+
|
|
13
|
+
transform(value: string | null, format: DateFormat = 'default'): string | null {
|
|
14
|
+
if (!value) return null;
|
|
15
|
+
|
|
16
|
+
// First validate dd-MM-yyyy format
|
|
17
|
+
const dateRegex = /^(0[1-9]|[12][0-9]|3[01])-(0[1-9]|1[0-2])-\d{4}$/;
|
|
18
|
+
if (!dateRegex.test(value)) {
|
|
19
|
+
throw new Error(`Invalid date format: ${value}. Expected format: dd-MM-yyyy`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Validate real date
|
|
23
|
+
const [day, month, year] = value.split('-').map(Number);
|
|
24
|
+
const date = new Date(year, month - 1, day);
|
|
25
|
+
if (
|
|
26
|
+
date.getFullYear() !== year ||
|
|
27
|
+
date.getMonth() !== month - 1 ||
|
|
28
|
+
date.getDate() !== day
|
|
29
|
+
) {
|
|
30
|
+
throw new Error(`Invalid date: ${value}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Apply requested format
|
|
34
|
+
switch (format) {
|
|
35
|
+
case 'short':
|
|
36
|
+
// Pad with leading zeros for consistent format
|
|
37
|
+
const paddedDay = day.toString().padStart(2, '0');
|
|
38
|
+
const paddedMonth = month.toString().padStart(2, '0');
|
|
39
|
+
return `${paddedDay}-${paddedMonth}-${year}`; // 10-03-2025
|
|
40
|
+
case 'medium':
|
|
41
|
+
return this.datePipe.transform(date, 'dd MMM yyyy'); // 10 Jan 2019
|
|
42
|
+
default:
|
|
43
|
+
return value; // original dd-MM-yyyy
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { HttpParams, HttpErrorResponse } from '@angular/common/http';
|
|
2
|
+
import { Observable, throwError } from 'rxjs';
|
|
3
|
+
import { HttpUtilityService } from './http-utility.service';
|
|
4
|
+
import { UtilsService } from './utils.service';
|
|
5
|
+
import { MessageService } from 'primeng/api';
|
|
6
|
+
import { Page } from '../models/pagination/page.interface';
|
|
7
|
+
import { PageRequest } from '../models/pagination/page-request.interface';
|
|
8
|
+
import { EntityService } from './entity.service';
|
|
9
|
+
|
|
10
|
+
export abstract class BaseCrudService<T extends { uuid: string }> implements EntityService<T> {
|
|
11
|
+
// Use the loading state from HttpUtilityService
|
|
12
|
+
get loading$() {
|
|
13
|
+
return this.httpUtility.loading$;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
protected apiUrl: string;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates an instance of BaseCrudService.
|
|
20
|
+
* @param httpUtility - Service for making HTTP requests
|
|
21
|
+
* @param utilsService - Utility service for common operations
|
|
22
|
+
* @param resourcePath - API resource path (e.g., 'customers', 'products')
|
|
23
|
+
* @param backendBaseUrl - Base URL for the backend API
|
|
24
|
+
* @param messageService - Optional service for showing messages
|
|
25
|
+
*/
|
|
26
|
+
protected constructor(
|
|
27
|
+
protected httpUtility: HttpUtilityService,
|
|
28
|
+
protected utilsService: UtilsService,
|
|
29
|
+
protected resourcePath: string,
|
|
30
|
+
protected backendBaseUrl: string,
|
|
31
|
+
protected messageService: MessageService | undefined
|
|
32
|
+
) {
|
|
33
|
+
this.apiUrl = this.joinUrls(backendBaseUrl, resourcePath);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Joins URL parts ensuring no double slashes
|
|
38
|
+
*/
|
|
39
|
+
private joinUrls(...parts: string[]): string {
|
|
40
|
+
return parts.map(part => part.replace(/^\/+|\/+$/g, '')).join('/');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getAll(pageRequest?: PageRequest): Observable<Page<T>> {
|
|
44
|
+
if (pageRequest) {
|
|
45
|
+
return this.httpUtility.post<Page<T>>(`${this.apiUrl}/page`, pageRequest);
|
|
46
|
+
} else {
|
|
47
|
+
return this.httpUtility.get<Page<T>>(this.apiUrl);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
getById(uuid: string): Observable<T> {
|
|
52
|
+
if (!this.utilsService.isValidUUID(uuid)) {
|
|
53
|
+
this.utilsService.showError('Invalid UUID format', this.messageService);
|
|
54
|
+
return throwError(() => new Error('Invalid UUID format'));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return this.httpUtility.get<T>(`${this.apiUrl}/${uuid}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
create(entity: Omit<T, 'uuid'>): Observable<T> {
|
|
61
|
+
return this.httpUtility.post<T>(this.apiUrl, entity);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
update(uuid: string, entity: Omit<T, 'uuid'>): Observable<T> {
|
|
65
|
+
if (!this.utilsService.isValidUUID(uuid)) {
|
|
66
|
+
this.utilsService.showError('Invalid UUID format', this.messageService);
|
|
67
|
+
return throwError(() => new Error('Invalid UUID format'));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return this.httpUtility.put<T>(`${this.apiUrl}/${uuid}`, entity);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
delete(uuid: string): Observable<void> {
|
|
74
|
+
if (!this.utilsService.isValidUUID(uuid)) {
|
|
75
|
+
this.utilsService.showError('Invalid UUID format', this.messageService);
|
|
76
|
+
return throwError(() => new Error('Invalid UUID format'));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return this.httpUtility.delete<void>(`${this.apiUrl}/${uuid}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { Observable, map } from 'rxjs';
|
|
3
|
+
import { HttpUtilityService } from './http-utility.service';
|
|
4
|
+
import { ReferenceDataProviderService } from './reference-data-provider.service';
|
|
5
|
+
|
|
6
|
+
export interface ReferenceDto {
|
|
7
|
+
key: string;
|
|
8
|
+
displayLabel: string;
|
|
9
|
+
data?: any;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@Injectable({
|
|
13
|
+
providedIn: 'root'
|
|
14
|
+
})
|
|
15
|
+
export class EntityRegistryService {
|
|
16
|
+
constructor(
|
|
17
|
+
private httpUtility: HttpUtilityService,
|
|
18
|
+
private dataProvider: ReferenceDataProviderService
|
|
19
|
+
) {}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Gets reference options for an entity, suitable for select/dropdown components
|
|
23
|
+
* @param entityName The name of the entity (e.g., 'customers', 'departments')
|
|
24
|
+
* @returns Observable of options array with label/value pairs
|
|
25
|
+
*/
|
|
26
|
+
getEntityOptions(entityName: string): Observable<{ label: string; value: string; data?: any; }[]> {
|
|
27
|
+
console.log('getEntityOptions', entityName);
|
|
28
|
+
const url = this.dataProvider.getReferenceEndpoint(entityName);
|
|
29
|
+
|
|
30
|
+
if (!url) {
|
|
31
|
+
console.warn(`No endpoint configured for entity: ${entityName}`);
|
|
32
|
+
return new Observable(subscriber => {
|
|
33
|
+
subscriber.next([]);
|
|
34
|
+
subscriber.complete();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return this.httpUtility.get<ReferenceDto[]>(url, {
|
|
39
|
+
retries: 1,
|
|
40
|
+
timeoutMs: 10000,
|
|
41
|
+
}).pipe(
|
|
42
|
+
map((items) =>
|
|
43
|
+
items?.map((item) => ({
|
|
44
|
+
label: item.displayLabel,
|
|
45
|
+
value: item.key,
|
|
46
|
+
data: item.data,
|
|
47
|
+
})) || []
|
|
48
|
+
)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interface for basic CRUD operations that all entity services must implement
|
|
5
|
+
*/
|
|
6
|
+
export interface EntityService<T> {
|
|
7
|
+
create(data: Omit<T, 'uuid'>): Observable<T>;
|
|
8
|
+
update(uuid: string, data: Omit<T, 'uuid'>): Observable<T>;
|
|
9
|
+
getById(uuid: string): Observable<T>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { UtilsService } from './utils.service';
|
|
3
|
+
|
|
4
|
+
@Injectable({
|
|
5
|
+
providedIn: 'root'
|
|
6
|
+
})
|
|
7
|
+
export class EnumRegistryService {
|
|
8
|
+
private enumMap = new Map<string, any>();
|
|
9
|
+
|
|
10
|
+
constructor(private utilsService: UtilsService) {}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Initialize global enums
|
|
14
|
+
*/
|
|
15
|
+
static registerGlobalEnums(enums: Record<string, any>) {
|
|
16
|
+
return {
|
|
17
|
+
provide: EnumRegistryService,
|
|
18
|
+
useFactory: (utilsService: UtilsService) => {
|
|
19
|
+
const service = new EnumRegistryService(utilsService);
|
|
20
|
+
Object.entries(enums).forEach(([name, enumType]) => {
|
|
21
|
+
service.register(name, enumType);
|
|
22
|
+
});
|
|
23
|
+
return service;
|
|
24
|
+
},
|
|
25
|
+
deps: [UtilsService]
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Register an enum type with the service
|
|
31
|
+
* @param name Name to register the enum under
|
|
32
|
+
* @param enumType The enum type to register
|
|
33
|
+
*/
|
|
34
|
+
register(name: string, enumType: any) {
|
|
35
|
+
this.enumMap.set(name, enumType);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get formly-compatible options array for a registered enum
|
|
40
|
+
* @param name Name of the registered enum
|
|
41
|
+
* @returns Array of {label, value} pairs suitable for formly select fields
|
|
42
|
+
*/
|
|
43
|
+
getEnumValues(name: string) {
|
|
44
|
+
const enumType = this.enumMap.get(name);
|
|
45
|
+
if (!enumType) {
|
|
46
|
+
console.warn(`Enum type "${name}" not found in registry`);
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
return this.utilsService.getEnumValues(enumType);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if an enum type is registered
|
|
54
|
+
* @param name Name of the enum to check
|
|
55
|
+
*/
|
|
56
|
+
hasEnum(name: string): boolean {
|
|
57
|
+
return this.enumMap.has(name);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Clear all registered enums
|
|
62
|
+
*/
|
|
63
|
+
clear() {
|
|
64
|
+
this.enumMap.clear();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { FormlyFieldConfig } from '@ngx-formly/core';
|
|
3
|
+
import { PageMode } from '../constants/app.constants';
|
|
4
|
+
import { EnumRegistryService } from './enum-registry.service';
|
|
5
|
+
|
|
6
|
+
@Injectable({
|
|
7
|
+
providedIn: 'root'
|
|
8
|
+
})
|
|
9
|
+
export class FormlyConfigService {
|
|
10
|
+
constructor(private enumRegistry: EnumRegistryService) {}
|
|
11
|
+
|
|
12
|
+
getFormConfig(
|
|
13
|
+
fields: FormlyFieldConfig[],
|
|
14
|
+
mode: PageMode,
|
|
15
|
+
dynamicOptions?: Record<string, any>
|
|
16
|
+
): FormlyFieldConfig[] {
|
|
17
|
+
const commonConfig = {
|
|
18
|
+
props: {
|
|
19
|
+
disabled: mode === 'view'
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return fields.map((field: FormlyFieldConfig) => {
|
|
24
|
+
// Apply dynamic options from child component
|
|
25
|
+
if (dynamicOptions && field.key && typeof field.key === 'string') {
|
|
26
|
+
const fieldOptions = dynamicOptions[field.key];
|
|
27
|
+
if (fieldOptions) {
|
|
28
|
+
field.props = {
|
|
29
|
+
...field.props,
|
|
30
|
+
...fieldOptions
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Handle field groups recursively
|
|
36
|
+
if (field.fieldGroup) {
|
|
37
|
+
field.fieldGroup = this.getFormConfig(field.fieldGroup, mode, dynamicOptions);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
...field,
|
|
42
|
+
...commonConfig,
|
|
43
|
+
props: {
|
|
44
|
+
...field.props,
|
|
45
|
+
...commonConfig.props
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { Component, OnInit } from '@angular/core';
|
|
2
|
+
import { HttpHeaders } from '@angular/common/http';
|
|
3
|
+
import { HttpUtilityService } from './http-utility.service';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Example component showing how to use the HttpUtilityService
|
|
7
|
+
* This is just a demonstration and not an actual component to use.
|
|
8
|
+
*/
|
|
9
|
+
@Component({
|
|
10
|
+
selector: 'app-http-utility-example',
|
|
11
|
+
template: `
|
|
12
|
+
<div>
|
|
13
|
+
<h2>HTTP Utility Service Example</h2>
|
|
14
|
+
<div *ngIf="loading">Loading...</div>
|
|
15
|
+
<div *ngIf="user">
|
|
16
|
+
<p>User ID: {{ user.id }}</p>
|
|
17
|
+
<p>Name: {{ user.name }}</p>
|
|
18
|
+
<p>Email: {{ user.email }}</p>
|
|
19
|
+
</div>
|
|
20
|
+
<div *ngIf="errorMessage" class="error">{{ errorMessage }}</div>
|
|
21
|
+
<button (click)="loadUser()">Load User</button>
|
|
22
|
+
<button (click)="createUser()">Create User</button>
|
|
23
|
+
<button (click)="updateUser()">Update User</button>
|
|
24
|
+
<button (click)="deleteUser()">Delete User</button>
|
|
25
|
+
<button (click)="uploadUserPhoto()">Upload Photo</button>
|
|
26
|
+
<button (click)="loadMultipleResources()">Load Multiple Resources</button>
|
|
27
|
+
<button (click)="downloadReport()">Download Report</button>
|
|
28
|
+
</div>
|
|
29
|
+
`
|
|
30
|
+
})
|
|
31
|
+
export class HttpUtilityExampleComponent implements OnInit {
|
|
32
|
+
loading = false;
|
|
33
|
+
user: any;
|
|
34
|
+
errorMessage = '';
|
|
35
|
+
|
|
36
|
+
// Make sure to inject the service in the constructor
|
|
37
|
+
constructor(private httpUtility: HttpUtilityService) {
|
|
38
|
+
// Subscribe to the loading state observable
|
|
39
|
+
this.httpUtility.loading$.subscribe(isLoading => {
|
|
40
|
+
this.loading = isLoading;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
ngOnInit(): void {
|
|
45
|
+
// Initial data loading
|
|
46
|
+
this.loadUser();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Example of a GET request with parameters
|
|
51
|
+
*/
|
|
52
|
+
loadUser(): void {
|
|
53
|
+
// Clear previous error messages
|
|
54
|
+
this.errorMessage = '';
|
|
55
|
+
|
|
56
|
+
// Creating parameters with the utility method
|
|
57
|
+
const params = this.httpUtility.createParams({
|
|
58
|
+
id: '12345',
|
|
59
|
+
includeDetails: true
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Example API call with options
|
|
63
|
+
this.httpUtility.get<any>('/api/users/current', {
|
|
64
|
+
params,
|
|
65
|
+
retries: 3,
|
|
66
|
+
timeoutMs: 10000
|
|
67
|
+
}).subscribe({
|
|
68
|
+
next: (data) => {
|
|
69
|
+
console.log('User data loaded:', data);
|
|
70
|
+
this.user = data;
|
|
71
|
+
},
|
|
72
|
+
error: (error) => {
|
|
73
|
+
console.error('Error loading user:', error);
|
|
74
|
+
this.errorMessage = error.message;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Example of a POST request with custom headers
|
|
81
|
+
*/
|
|
82
|
+
createUser(): void {
|
|
83
|
+
this.errorMessage = '';
|
|
84
|
+
|
|
85
|
+
// Example user data
|
|
86
|
+
const newUser = {
|
|
87
|
+
name: 'John Doe',
|
|
88
|
+
email: 'john.doe@example.com',
|
|
89
|
+
role: 'user'
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Custom headers
|
|
93
|
+
const headers = new HttpHeaders({
|
|
94
|
+
'X-Custom-Header': 'some-value'
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
this.httpUtility.post<any>('/api/users', newUser, {
|
|
98
|
+
headers,
|
|
99
|
+
withCredentials: true
|
|
100
|
+
}).subscribe({
|
|
101
|
+
next: (response) => {
|
|
102
|
+
console.log('User created:', response);
|
|
103
|
+
this.user = response;
|
|
104
|
+
},
|
|
105
|
+
error: (error) => {
|
|
106
|
+
console.error('Error creating user:', error);
|
|
107
|
+
this.errorMessage = error.message;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Example of a PUT request
|
|
114
|
+
*/
|
|
115
|
+
updateUser(): void {
|
|
116
|
+
if (!this.user) {
|
|
117
|
+
this.errorMessage = 'No user loaded to update';
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const updatedUser = {
|
|
122
|
+
...this.user,
|
|
123
|
+
name: 'Updated Name'
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
this.httpUtility.put<any>(`/api/users/${this.user.id}`, updatedUser)
|
|
127
|
+
.subscribe({
|
|
128
|
+
next: (response) => {
|
|
129
|
+
console.log('User updated:', response);
|
|
130
|
+
this.user = response;
|
|
131
|
+
},
|
|
132
|
+
error: (error) => {
|
|
133
|
+
console.error('Error updating user:', error);
|
|
134
|
+
this.errorMessage = error.message;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Example of a DELETE request
|
|
141
|
+
*/
|
|
142
|
+
deleteUser(): void {
|
|
143
|
+
if (!this.user) {
|
|
144
|
+
this.errorMessage = 'No user loaded to delete';
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
this.httpUtility.delete<void>(`/api/users/${this.user.id}`)
|
|
149
|
+
.subscribe({
|
|
150
|
+
next: () => {
|
|
151
|
+
console.log('User deleted successfully');
|
|
152
|
+
this.user = null;
|
|
153
|
+
},
|
|
154
|
+
error: (error) => {
|
|
155
|
+
console.error('Error deleting user:', error);
|
|
156
|
+
this.errorMessage = error.message;
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Example of file upload with progress tracking
|
|
163
|
+
*/
|
|
164
|
+
uploadUserPhoto(): void {
|
|
165
|
+
// In a real application, you would get this from a file input
|
|
166
|
+
// This is just for demonstration purposes
|
|
167
|
+
const file = new File(['dummy content'], 'profile-photo.jpg', { type: 'image/jpeg' });
|
|
168
|
+
|
|
169
|
+
// Additional form data
|
|
170
|
+
const additionalData = {
|
|
171
|
+
userId: this.user?.id || 'unknown',
|
|
172
|
+
description: 'Profile photo'
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// Define the expected response type
|
|
176
|
+
interface PhotoUploadResponse {
|
|
177
|
+
photoUrl: string;
|
|
178
|
+
timestamp: string;
|
|
179
|
+
fileSize: number;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
this.httpUtility.uploadFile<PhotoUploadResponse>('/api/users/photo', file, additionalData, {
|
|
183
|
+
onProgress: (percentage) => {
|
|
184
|
+
console.log(`Upload progress: ${percentage}%`);
|
|
185
|
+
// Update UI with progress
|
|
186
|
+
}
|
|
187
|
+
}).subscribe({
|
|
188
|
+
next: (response) => {
|
|
189
|
+
console.log('Photo uploaded:', response);
|
|
190
|
+
// Update user with new photo URL
|
|
191
|
+
this.user = {
|
|
192
|
+
...this.user,
|
|
193
|
+
photoUrl: response.photoUrl
|
|
194
|
+
};
|
|
195
|
+
},
|
|
196
|
+
error: (error) => {
|
|
197
|
+
console.error('Error uploading photo:', error);
|
|
198
|
+
this.errorMessage = error.message;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Example of batch requests
|
|
205
|
+
*/
|
|
206
|
+
loadMultipleResources(): void {
|
|
207
|
+
// Create multiple requests
|
|
208
|
+
const userRequest = this.httpUtility.get<any>('/api/users/current');
|
|
209
|
+
const postsRequest = this.httpUtility.get<any[]>('/api/posts');
|
|
210
|
+
const notificationsRequest = this.httpUtility.get<any[]>('/api/notifications');
|
|
211
|
+
|
|
212
|
+
// Execute all requests in parallel
|
|
213
|
+
this.httpUtility.batchRequests([
|
|
214
|
+
userRequest,
|
|
215
|
+
postsRequest,
|
|
216
|
+
notificationsRequest
|
|
217
|
+
]).subscribe({
|
|
218
|
+
next: ([user, posts, notifications]) => {
|
|
219
|
+
console.log('All data loaded:', { user, posts, notifications });
|
|
220
|
+
this.user = user;
|
|
221
|
+
// Handle other data
|
|
222
|
+
},
|
|
223
|
+
error: (error) => {
|
|
224
|
+
console.error('Error loading batch data:', error);
|
|
225
|
+
this.errorMessage = error.message;
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Example of file download
|
|
232
|
+
*/
|
|
233
|
+
downloadReport(): void {
|
|
234
|
+
this.httpUtility.downloadFile('/api/reports/user-activity', 'user-activity.pdf')
|
|
235
|
+
.subscribe({
|
|
236
|
+
next: (blob) => {
|
|
237
|
+
// Create a download link and trigger it
|
|
238
|
+
const url = window.URL.createObjectURL(blob);
|
|
239
|
+
const a = document.createElement('a');
|
|
240
|
+
a.href = url;
|
|
241
|
+
a.download = 'user-activity.pdf';
|
|
242
|
+
document.body.appendChild(a);
|
|
243
|
+
a.click();
|
|
244
|
+
window.URL.revokeObjectURL(url);
|
|
245
|
+
a.remove();
|
|
246
|
+
},
|
|
247
|
+
error: (error) => {
|
|
248
|
+
console.error('Error downloading file:', error);
|
|
249
|
+
this.errorMessage = error.message;
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|