@flusys/ng-shared 3.0.0-rc → 3.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/README.md +383 -40
- package/fesm2022/flusys-ng-shared.mjs +106 -77
- package/fesm2022/flusys-ng-shared.mjs.map +1 -1
- package/package.json +9 -9
- package/types/flusys-ng-shared.d.ts +24 -5
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
## Package Information
|
|
10
10
|
|
|
11
11
|
- **Package:** `@flusys/ng-shared`
|
|
12
|
+
- **Version:** 3.0.0
|
|
12
13
|
- **Dependencies:** ng-core
|
|
13
14
|
- **Dependents:** ng-layout, ng-auth, ng-iam, ng-storage, flusysng
|
|
14
15
|
- **Build Command:** `npm run build:ng-shared`
|
|
@@ -74,7 +75,11 @@ interface IPagination {
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
interface ISort { [key: string]: 'ASC' | 'DESC' }
|
|
77
|
-
|
|
78
|
+
|
|
79
|
+
// Filter supports primitives and arrays for multi-value filtering
|
|
80
|
+
interface IFilter {
|
|
81
|
+
[key: string]: string | number | boolean | null | undefined | string[] | number[];
|
|
82
|
+
}
|
|
78
83
|
|
|
79
84
|
interface IFilterData {
|
|
80
85
|
filter?: IFilter;
|
|
@@ -86,6 +91,8 @@ interface IFilterData {
|
|
|
86
91
|
}
|
|
87
92
|
```
|
|
88
93
|
|
|
94
|
+
**Note:** `IFilter` supports array values (`string[]`, `number[]`) for multi-value filtering (e.g., filtering by multiple status values or IDs).
|
|
95
|
+
|
|
89
96
|
#### IDeleteData
|
|
90
97
|
|
|
91
98
|
Delete request payload matching backend DeleteDto.
|
|
@@ -144,6 +151,7 @@ interface IFileUploadOptions {
|
|
|
144
151
|
}
|
|
145
152
|
|
|
146
153
|
interface IUploadedFile {
|
|
154
|
+
id?: string; // File manager ID (UUID) - available when registered
|
|
147
155
|
name: string;
|
|
148
156
|
key: string;
|
|
149
157
|
size: number;
|
|
@@ -267,6 +275,14 @@ interface IFileData {
|
|
|
267
275
|
thumbnailUrl?: string;
|
|
268
276
|
createdAt: Date;
|
|
269
277
|
}
|
|
278
|
+
|
|
279
|
+
// File URL service response DTO
|
|
280
|
+
interface FilesResponseDto {
|
|
281
|
+
id: string;
|
|
282
|
+
name: string;
|
|
283
|
+
contentType: string;
|
|
284
|
+
url: string | null;
|
|
285
|
+
}
|
|
270
286
|
```
|
|
271
287
|
|
|
272
288
|
### Permission Interfaces
|
|
@@ -310,11 +326,56 @@ enum IconTypeEnum {
|
|
|
310
326
|
|
|
311
327
|
---
|
|
312
328
|
|
|
313
|
-
## 3.
|
|
329
|
+
## 3. Constants
|
|
330
|
+
|
|
331
|
+
### Permission Constants
|
|
332
|
+
|
|
333
|
+
Centralized permission codes for type-safe permission checks. Single source of truth to prevent typos.
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
import { PERMISSIONS, USER_PERMISSIONS, ROLE_PERMISSIONS } from '@flusys/ng-shared';
|
|
337
|
+
|
|
338
|
+
// Use constants instead of strings
|
|
339
|
+
*hasPermission="PERMISSIONS.USER.READ"
|
|
340
|
+
|
|
341
|
+
// Individual permission groups available:
|
|
342
|
+
USER_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
|
|
343
|
+
COMPANY_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
|
|
344
|
+
BRANCH_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
|
|
345
|
+
ACTION_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
|
|
346
|
+
ROLE_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
|
|
347
|
+
ROLE_ACTION_PERMISSIONS // { READ, ASSIGN }
|
|
348
|
+
USER_ROLE_PERMISSIONS // { READ, ASSIGN }
|
|
349
|
+
USER_ACTION_PERMISSIONS // { READ, ASSIGN }
|
|
350
|
+
COMPANY_ACTION_PERMISSIONS // { READ, ASSIGN }
|
|
351
|
+
FILE_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
|
|
352
|
+
FOLDER_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
|
|
353
|
+
STORAGE_CONFIG_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
|
|
354
|
+
EMAIL_CONFIG_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
|
|
355
|
+
EMAIL_TEMPLATE_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
|
|
356
|
+
FORM_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
|
|
357
|
+
|
|
358
|
+
// Aggregated object with all permissions
|
|
359
|
+
PERMISSIONS.USER.READ // 'user.read'
|
|
360
|
+
PERMISSIONS.ROLE.CREATE // 'role.create'
|
|
361
|
+
PERMISSIONS.FILE.DELETE // 'file.delete'
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**Type:** `PermissionCode` - Union type of all valid permission code strings.
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## 4. Services
|
|
314
369
|
|
|
315
370
|
### ApiResourceService
|
|
316
371
|
|
|
317
|
-
Signal-based CRUD service using Angular 21 `resource()` API
|
|
372
|
+
Signal-based CRUD service using Angular 21 `resource()` API with **lazy initialization**. All endpoints use POST (RPC-style).
|
|
373
|
+
|
|
374
|
+
**ServiceName Type:**
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
type ServiceName = 'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email';
|
|
378
|
+
```
|
|
318
379
|
|
|
319
380
|
**Define a service:**
|
|
320
381
|
|
|
@@ -326,11 +387,56 @@ import { ApiResourceService } from '@flusys/ng-shared';
|
|
|
326
387
|
@Injectable({ providedIn: 'root' })
|
|
327
388
|
export class UserService extends ApiResourceService<UserDto, IUser> {
|
|
328
389
|
constructor(http: HttpClient) {
|
|
329
|
-
|
|
390
|
+
// Option 1: Use global apiBaseUrl (default)
|
|
391
|
+
super('users', http);
|
|
392
|
+
// Base URL: APP_CONFIG.apiBaseUrl + '/users'
|
|
393
|
+
|
|
394
|
+
// Option 2: Use feature-specific service URL (recommended)
|
|
395
|
+
super('users', http, 'administration');
|
|
396
|
+
// Base URL: APP_CONFIG.services.administration.baseUrl + '/users'
|
|
330
397
|
}
|
|
331
398
|
}
|
|
332
399
|
```
|
|
333
400
|
|
|
401
|
+
**Constructor Parameters:**
|
|
402
|
+
|
|
403
|
+
| Parameter | Type | Required | Description |
|
|
404
|
+
|-----------|------|----------|-------------|
|
|
405
|
+
| `moduleApiName` | `string` | Yes | API path segment (e.g., 'users', 'file-manager') |
|
|
406
|
+
| `http` | `HttpClient` | Yes | Angular HttpClient instance |
|
|
407
|
+
| `serviceName` | `ServiceName` | No | Feature service name for URL resolution |
|
|
408
|
+
|
|
409
|
+
**Service URL Resolution:**
|
|
410
|
+
- If `serviceName` is provided, uses `getServiceUrl(config, serviceName)` to resolve the base URL
|
|
411
|
+
- Falls back to `APP_CONFIG.apiBaseUrl` if service not found or `serviceName` not provided
|
|
412
|
+
|
|
413
|
+
**Lazy Initialization:**
|
|
414
|
+
|
|
415
|
+
The list resource is **lazy-initialized** to avoid unnecessary HTTP requests on service construction. The resource is only created when first needed:
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
// Resource is NOT created until one of these is called:
|
|
419
|
+
userService.fetchList('', { pagination: { currentPage: 0, pageSize: 10 } });
|
|
420
|
+
// OR
|
|
421
|
+
userService.initListResource(); // Explicit initialization
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
**Internal resource structure:**
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
private _listResource: ResourceRef<IListResponse<InterfaceT> | undefined> | null = null;
|
|
428
|
+
private _resourceInitialized = false;
|
|
429
|
+
private readonly _resourceInitSignal = signal(false);
|
|
430
|
+
|
|
431
|
+
// Initialize the list resource (called automatically by fetchList)
|
|
432
|
+
initListResource(): void {
|
|
433
|
+
if (this._resourceInitialized) return;
|
|
434
|
+
this._resourceInitialized = true;
|
|
435
|
+
this._resourceInitSignal.set(true);
|
|
436
|
+
// Creates the resource with linkedSignal for data, isLoading, etc.
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
334
440
|
**Endpoint mapping:**
|
|
335
441
|
|
|
336
442
|
| Method | HTTP | Endpoint | Response |
|
|
@@ -435,7 +541,7 @@ export class ProductComponent {
|
|
|
435
541
|
|
|
436
542
|
### PermissionValidatorService
|
|
437
543
|
|
|
438
|
-
Signal-based permission state management. Used by `HasPermissionDirective`, permission guards, and IAM.
|
|
544
|
+
Signal-based permission state management. Used by `HasPermissionDirective`, permission guards, and IAM. Supports **wildcard permissions**.
|
|
439
545
|
|
|
440
546
|
```typescript
|
|
441
547
|
import { PermissionValidatorService } from '@flusys/ng-shared';
|
|
@@ -453,8 +559,8 @@ export class MyComponent {
|
|
|
453
559
|
// User has permission
|
|
454
560
|
}
|
|
455
561
|
|
|
456
|
-
// Check loaded state
|
|
457
|
-
if (this.permissionValidator.
|
|
562
|
+
// Check loaded state (signal-based)
|
|
563
|
+
if (this.permissionValidator.isLoaded()) {
|
|
458
564
|
// Permissions have been loaded
|
|
459
565
|
}
|
|
460
566
|
}
|
|
@@ -467,14 +573,30 @@ export class MyComponent {
|
|
|
467
573
|
| ---------------------------- | --------- | ---------------------------------- |
|
|
468
574
|
| `setPermissions(codes[])` | `void` | Replace all permissions |
|
|
469
575
|
| `clearPermissions()` | `void` | Clear all permissions |
|
|
470
|
-
| `hasPermission(code)` | `boolean` | Check single permission
|
|
471
|
-
| `isPermissionsLoaded()` | `boolean` |
|
|
576
|
+
| `hasPermission(code)` | `boolean` | Check single permission (supports wildcards) |
|
|
577
|
+
| `isPermissionsLoaded()` | `boolean` | **Deprecated** - Use `isLoaded()` signal |
|
|
472
578
|
|
|
473
579
|
**Signals:**
|
|
474
580
|
|
|
475
581
|
| Signal | Type | Description |
|
|
476
582
|
| ------------- | ------------------ | ------------------------------ |
|
|
477
583
|
| `permissions` | `Signal<string[]>` | Readonly current permissions |
|
|
584
|
+
| `isLoaded` | `Signal<boolean>` | Whether permissions have been set |
|
|
585
|
+
|
|
586
|
+
**Wildcard Support:**
|
|
587
|
+
|
|
588
|
+
The `hasPermission()` method uses the permission evaluator utility which supports wildcards:
|
|
589
|
+
|
|
590
|
+
```typescript
|
|
591
|
+
// Exact match
|
|
592
|
+
hasPermission('user.read') // true if permissions include 'user.read'
|
|
593
|
+
|
|
594
|
+
// Global wildcard
|
|
595
|
+
hasPermission('user.read') // true if permissions include '*'
|
|
596
|
+
|
|
597
|
+
// Module wildcard
|
|
598
|
+
hasPermission('user.read') // true if permissions include 'user.*'
|
|
599
|
+
```
|
|
478
600
|
|
|
479
601
|
### CookieService
|
|
480
602
|
|
|
@@ -501,7 +623,7 @@ if (!platform.isServer) {
|
|
|
501
623
|
|
|
502
624
|
---
|
|
503
625
|
|
|
504
|
-
##
|
|
626
|
+
## 5. Components
|
|
505
627
|
|
|
506
628
|
### IconComponent
|
|
507
629
|
|
|
@@ -782,7 +904,7 @@ export class MyComponent {
|
|
|
782
904
|
|
|
783
905
|
---
|
|
784
906
|
|
|
785
|
-
##
|
|
907
|
+
## 6. Directives
|
|
786
908
|
|
|
787
909
|
### HasPermissionDirective
|
|
788
910
|
|
|
@@ -856,7 +978,7 @@ Prevents default browser behavior on specified events and emits the event.
|
|
|
856
978
|
|
|
857
979
|
---
|
|
858
980
|
|
|
859
|
-
##
|
|
981
|
+
## 7. Guards
|
|
860
982
|
|
|
861
983
|
Route-level guards for permission-based access control. All guards deny access when permissions are not loaded (fail-closed).
|
|
862
984
|
|
|
@@ -904,16 +1026,21 @@ AND logic - allows access only if user has ALL specified permissions.
|
|
|
904
1026
|
|
|
905
1027
|
---
|
|
906
1028
|
|
|
907
|
-
##
|
|
1029
|
+
## 8. Utilities
|
|
908
1030
|
|
|
909
1031
|
### Permission Evaluator
|
|
910
1032
|
|
|
911
|
-
Pure functions for permission logic evaluation. Used internally by `HasPermissionDirective` and guards.
|
|
1033
|
+
Pure functions for permission logic evaluation. Used internally by `HasPermissionDirective` and guards. **Supports wildcard permissions.**
|
|
912
1034
|
|
|
913
1035
|
```typescript
|
|
914
|
-
import { evaluatePermission, evaluateLogicNode, hasAnyPermission, hasAllPermissions } from '@flusys/ng-shared';
|
|
1036
|
+
import { evaluatePermission, evaluateLogicNode, hasAnyPermission, hasAllPermissions, hasPermission } from '@flusys/ng-shared';
|
|
915
1037
|
|
|
916
|
-
const userPermissions = ['user.view', 'user.create'];
|
|
1038
|
+
const userPermissions = ['user.view', 'user.create', 'admin.*'];
|
|
1039
|
+
|
|
1040
|
+
// Low-level hasPermission check with wildcard support
|
|
1041
|
+
hasPermission('user.view', userPermissions); // true (exact match)
|
|
1042
|
+
hasPermission('admin.manage', userPermissions); // true (matches 'admin.*')
|
|
1043
|
+
hasPermission('settings.view', ['*']); // true (global wildcard)
|
|
917
1044
|
|
|
918
1045
|
// Evaluate string or ILogicNode
|
|
919
1046
|
evaluatePermission('user.view', userPermissions); // true
|
|
@@ -922,14 +1049,70 @@ evaluatePermission(null, userPermissions); // false
|
|
|
922
1049
|
// Evaluate ILogicNode tree recursively
|
|
923
1050
|
evaluateLogicNode(logicNode, userPermissions);
|
|
924
1051
|
|
|
925
|
-
// Simple OR/AND checks
|
|
1052
|
+
// Simple OR/AND checks (also support wildcards)
|
|
926
1053
|
hasAnyPermission(['user.view', 'user.delete'], userPermissions); // true (has user.view)
|
|
927
1054
|
hasAllPermissions(['user.view', 'user.delete'], userPermissions); // false (missing user.delete)
|
|
928
1055
|
```
|
|
929
1056
|
|
|
1057
|
+
**Wildcard Rules:**
|
|
1058
|
+
|
|
1059
|
+
| Pattern | Matches |
|
|
1060
|
+
|---------|---------|
|
|
1061
|
+
| `*` | All permissions (global wildcard) |
|
|
1062
|
+
| `module.*` | All permissions starting with `module.` |
|
|
1063
|
+
| `user.read` | Exact match only |
|
|
1064
|
+
|
|
1065
|
+
**Implementation Details:**
|
|
1066
|
+
|
|
1067
|
+
```typescript
|
|
1068
|
+
export function hasPermission(requiredPermission: string, userPermissions: string[]): boolean {
|
|
1069
|
+
// Exact match
|
|
1070
|
+
if (userPermissions.includes(requiredPermission)) return true;
|
|
1071
|
+
|
|
1072
|
+
// Wildcard matching
|
|
1073
|
+
for (const permission of userPermissions) {
|
|
1074
|
+
// Global wildcard
|
|
1075
|
+
if (permission === '*') return true;
|
|
1076
|
+
|
|
1077
|
+
// Module wildcard (e.g., 'user.*' matches 'user.read')
|
|
1078
|
+
if (permission.endsWith('.*')) {
|
|
1079
|
+
const prefix = permission.slice(0, -1); // 'user.'
|
|
1080
|
+
if (requiredPermission.startsWith(prefix)) return true;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
return false;
|
|
1085
|
+
}
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
### Scroll Pagination
|
|
1089
|
+
|
|
1090
|
+
Utility for lazy-loading dropdowns with scroll detection.
|
|
1091
|
+
|
|
1092
|
+
```typescript
|
|
1093
|
+
import { checkScrollPagination, ScrollPaginationConfig } from '@flusys/ng-shared';
|
|
1094
|
+
|
|
1095
|
+
// In a component
|
|
1096
|
+
onScroll(event: Event): void {
|
|
1097
|
+
const nextPagination = checkScrollPagination(event, {
|
|
1098
|
+
pagination: this.pagination(),
|
|
1099
|
+
total: this.total(),
|
|
1100
|
+
isLoading: this.isLoading(),
|
|
1101
|
+
threshold: 50, // pixels from bottom (default: 50)
|
|
1102
|
+
});
|
|
1103
|
+
if (nextPagination) {
|
|
1104
|
+
this.onPagination.emit(nextPagination);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
**Interface:** `ScrollPaginationConfig` - `{ threshold?, pagination, total, isLoading }`
|
|
1110
|
+
|
|
1111
|
+
**Returns:** `IPagination | null` - Next page pagination or null if not needed.
|
|
1112
|
+
|
|
930
1113
|
---
|
|
931
1114
|
|
|
932
|
-
##
|
|
1115
|
+
## 9. Classes
|
|
933
1116
|
|
|
934
1117
|
### BaseFormControl
|
|
935
1118
|
|
|
@@ -965,9 +1148,78 @@ export class MySelectComponent extends BaseFormControl<string | null> {
|
|
|
965
1148
|
|
|
966
1149
|
**Helper:** `provideValueAccessor(ComponentClass)` - Factory for `NG_VALUE_ACCESSOR` provider
|
|
967
1150
|
|
|
1151
|
+
### BaseFormPage
|
|
1152
|
+
|
|
1153
|
+
Abstract directive for form page components (create/edit).
|
|
1154
|
+
|
|
1155
|
+
```typescript
|
|
1156
|
+
import { BaseFormPage } from '@flusys/ng-shared';
|
|
1157
|
+
|
|
1158
|
+
@Component({ ... })
|
|
1159
|
+
export class ProductFormComponent extends BaseFormPage<IProduct, IProductFormModel> {
|
|
1160
|
+
private readonly productService = inject(ProductApiService);
|
|
1161
|
+
private readonly _formModel = signal<IProductFormModel>({ name: '', price: 0 });
|
|
1162
|
+
readonly formModel = this._formModel.asReadonly();
|
|
1163
|
+
|
|
1164
|
+
getFormModel(): Signal<IProductFormModel> { return this.formModel; }
|
|
1165
|
+
getResourceRoute(): string { return '/products'; }
|
|
1166
|
+
getResourceName(): string { return 'Product'; }
|
|
1167
|
+
isFormValid(): boolean { return this.formModel().name.trim().length > 0; }
|
|
1168
|
+
|
|
1169
|
+
loadItem(id: string): void {
|
|
1170
|
+
this.productService.findById(id).subscribe(res => {
|
|
1171
|
+
if (res.success && res.data) {
|
|
1172
|
+
this.existingItem.set(res.data);
|
|
1173
|
+
this._formModel.set({ name: res.data.name, price: res.data.price });
|
|
1174
|
+
}
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
createItem(model: IProductFormModel): Observable<unknown> {
|
|
1179
|
+
return this.productService.insert(model);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
updateItem(model: IProductFormModel): Observable<unknown> {
|
|
1183
|
+
return this.productService.update({ id: this.existingItem()!.id, ...model });
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
```
|
|
1187
|
+
|
|
1188
|
+
**Signals:** `isLoading`, `existingItem`, `isEditMode` (computed)
|
|
1189
|
+
|
|
1190
|
+
**Methods:** `onSubmit()`, `onCancel()`, `showSuccess()`, `showError()`, `showValidationError()`
|
|
1191
|
+
|
|
1192
|
+
### BaseListPage
|
|
1193
|
+
|
|
1194
|
+
Abstract directive for list page components with pagination and CRUD operations.
|
|
1195
|
+
|
|
1196
|
+
```typescript
|
|
1197
|
+
import { BaseListPage } from '@flusys/ng-shared';
|
|
1198
|
+
|
|
1199
|
+
@Component({ ... })
|
|
1200
|
+
export class UserListComponent extends BaseListPage<IUser> {
|
|
1201
|
+
private readonly userService = inject(UserApiService);
|
|
1202
|
+
|
|
1203
|
+
getResourceRoute(): string { return '/users'; }
|
|
1204
|
+
getDeleteConfirmMessage(user: IUser): string { return `Delete "${user.name}"?`; }
|
|
1205
|
+
|
|
1206
|
+
async loadData(): Promise<void> {
|
|
1207
|
+
this.isLoading.set(true);
|
|
1208
|
+
const res = await this.userService.findByIdAsync(...);
|
|
1209
|
+
this.items.set(res.data ?? []);
|
|
1210
|
+
this.total.set(res.meta?.total ?? 0);
|
|
1211
|
+
this.isLoading.set(false);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
```
|
|
1215
|
+
|
|
1216
|
+
**Signals:** `items`, `isLoading`, `total`, `pageSize`, `first`, `currentPage` (computed), `showCompanyInfo` (computed)
|
|
1217
|
+
|
|
1218
|
+
**Methods:** `onCreate()`, `onEdit(id)`, `onPageChange(event)`, `onDelete()`, `onDeleteAsync()`, `showSuccess()`, `showError()`, `showInfo()`, `showWarn()`
|
|
1219
|
+
|
|
968
1220
|
---
|
|
969
1221
|
|
|
970
|
-
##
|
|
1222
|
+
## 10. Modules
|
|
971
1223
|
|
|
972
1224
|
### AngularModule
|
|
973
1225
|
|
|
@@ -975,7 +1227,8 @@ Re-exports common Angular modules for convenience.
|
|
|
975
1227
|
|
|
976
1228
|
```typescript
|
|
977
1229
|
import { AngularModule } from '@flusys/ng-shared';
|
|
978
|
-
// Includes: CommonModule, FormsModule, ReactiveFormsModule, RouterLink, RouterOutlet,
|
|
1230
|
+
// Includes: CommonModule, FormsModule, ReactiveFormsModule, RouterLink, RouterOutlet,
|
|
1231
|
+
// RouterLinkActive, NgOptimizedImage, NgComponentOutlet, + directives (IsEmptyImageDirective, PreventDefaultDirective)
|
|
979
1232
|
// Providers: DatePipe
|
|
980
1233
|
```
|
|
981
1234
|
|
|
@@ -985,13 +1238,21 @@ Re-exports PrimeNG component modules for convenience.
|
|
|
985
1238
|
|
|
986
1239
|
```typescript
|
|
987
1240
|
import { PrimeModule } from '@flusys/ng-shared';
|
|
988
|
-
// Includes
|
|
989
|
-
//
|
|
1241
|
+
// Includes 30 modules:
|
|
1242
|
+
// - Layout: AccordionModule, CardModule, DividerModule, PanelModule, SplitterModule, TabsModule, ToolbarModule
|
|
1243
|
+
// - Form: AutoCompleteModule, CheckboxModule, DatePickerModule, InputTextModule, InputTextareaModule,
|
|
1244
|
+
// MultiSelectModule, RadioButtonModule, SelectModule, ToggleSwitchModule
|
|
1245
|
+
// - Button: ButtonModule, SpeedDialModule, SplitButtonModule
|
|
1246
|
+
// - Data: PaginatorModule, TableModule, TreeTableModule
|
|
1247
|
+
// - Overlay: DialogModule, DrawerModule, PopoverModule, TooltipModule
|
|
1248
|
+
// - File: FileUploadModule
|
|
1249
|
+
// - Media: ImageModule
|
|
1250
|
+
// - Misc: BadgeModule, TagModule
|
|
990
1251
|
```
|
|
991
1252
|
|
|
992
1253
|
---
|
|
993
1254
|
|
|
994
|
-
##
|
|
1255
|
+
## 11. Provider Interfaces (Package Independence)
|
|
995
1256
|
|
|
996
1257
|
ng-shared defines **provider interfaces** to enable feature packages (ng-iam, ng-storage) to access auth functionality without direct dependencies.
|
|
997
1258
|
|
|
@@ -1022,6 +1283,8 @@ interface IUserProvider {
|
|
|
1022
1283
|
}
|
|
1023
1284
|
```
|
|
1024
1285
|
|
|
1286
|
+
**Token Error Message:** `'USER_PROVIDER not configured. Please provide an implementation in app.config.ts'`
|
|
1287
|
+
|
|
1025
1288
|
#### ICompanyApiProvider / `COMPANY_API_PROVIDER`
|
|
1026
1289
|
|
|
1027
1290
|
Company list access for IAM company selection.
|
|
@@ -1036,19 +1299,29 @@ interface ICompanyApiProvider {
|
|
|
1036
1299
|
}
|
|
1037
1300
|
```
|
|
1038
1301
|
|
|
1302
|
+
**Token Error Message:** `'COMPANY_API_PROVIDER not configured. Please provide an implementation in app.config.ts'`
|
|
1303
|
+
|
|
1039
1304
|
#### IUserPermissionProvider / `USER_PERMISSION_PROVIDER`
|
|
1040
1305
|
|
|
1041
1306
|
User permission queries for IAM.
|
|
1042
1307
|
|
|
1043
1308
|
```typescript
|
|
1309
|
+
interface IUserBranchPermission {
|
|
1310
|
+
branchId: string;
|
|
1311
|
+
branchName: string;
|
|
1312
|
+
permissions: string[];
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1044
1315
|
interface IUserPermissionProvider {
|
|
1045
|
-
getUserBranchPermissions(userId: string): Observable<ISingleResponse<
|
|
1316
|
+
getUserBranchPermissions(userId: string): Observable<ISingleResponse<IUserBranchPermission[]>>;
|
|
1046
1317
|
}
|
|
1047
1318
|
```
|
|
1048
1319
|
|
|
1320
|
+
**Token Error Message:** `'USER_PERMISSION_PROVIDER not configured. Please provide an implementation in app.config.ts'`
|
|
1321
|
+
|
|
1049
1322
|
#### IProfileUploadProvider / `PROFILE_UPLOAD_PROVIDER`
|
|
1050
1323
|
|
|
1051
|
-
Profile picture upload for ng-auth profile page. Implemented by ng-storage.
|
|
1324
|
+
Profile picture upload for ng-auth profile page. Implemented by ng-storage. **Optional token** - use with `inject(..., { optional: true })`.
|
|
1052
1325
|
|
|
1053
1326
|
```typescript
|
|
1054
1327
|
interface IProfileUploadResult {
|
|
@@ -1076,7 +1349,7 @@ interface IProfileUploadProvider {
|
|
|
1076
1349
|
|
|
1077
1350
|
#### IProfilePermissionProvider / `PROFILE_PERMISSION_PROVIDER`
|
|
1078
1351
|
|
|
1079
|
-
User permission queries for ng-auth profile page. Implemented by ng-iam.
|
|
1352
|
+
User permission queries for ng-auth profile page. Implemented by ng-iam. **Optional token** - use with `inject(..., { optional: true })`.
|
|
1080
1353
|
|
|
1081
1354
|
```typescript
|
|
1082
1355
|
interface IProfileRoleInfo {
|
|
@@ -1112,6 +1385,8 @@ interface IAuthStateProvider {
|
|
|
1112
1385
|
}
|
|
1113
1386
|
```
|
|
1114
1387
|
|
|
1388
|
+
**Token Error Message:** `'AUTH_STATE_PROVIDER not configured. Please provide an implementation in app.config.ts'`
|
|
1389
|
+
|
|
1115
1390
|
**Usage:**
|
|
1116
1391
|
|
|
1117
1392
|
```typescript
|
|
@@ -1133,7 +1408,7 @@ export class PublicFormComponent {
|
|
|
1133
1408
|
|
|
1134
1409
|
#### IUserListProvider / `USER_LIST_PROVIDER`
|
|
1135
1410
|
|
|
1136
|
-
Extends user list pages with extra columns, actions, and data enrichment. Optional
|
|
1411
|
+
Extends user list pages with extra columns, actions, and data enrichment. **Optional token** - use with `inject(..., { optional: true })`.
|
|
1137
1412
|
|
|
1138
1413
|
```typescript
|
|
1139
1414
|
interface IUserListItem {
|
|
@@ -1164,6 +1439,16 @@ interface IUserListColumn {
|
|
|
1164
1439
|
templateType?: 'text' | 'badge' | 'date' | 'boolean' | 'custom';
|
|
1165
1440
|
}
|
|
1166
1441
|
|
|
1442
|
+
interface IUserListFilter {
|
|
1443
|
+
page?: number;
|
|
1444
|
+
pageSize?: number;
|
|
1445
|
+
search?: string;
|
|
1446
|
+
isActive?: boolean;
|
|
1447
|
+
companyId?: string;
|
|
1448
|
+
branchId?: string;
|
|
1449
|
+
[key: string]: unknown;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1167
1452
|
interface IUserListProvider<T extends IUserListItem = IUserListItem> {
|
|
1168
1453
|
getExtraColumns?(): IUserListColumn[];
|
|
1169
1454
|
getExtraRowActions?(): IUserListAction<T>[];
|
|
@@ -1236,8 +1521,9 @@ export const appConfig: ApplicationConfig = {
|
|
|
1236
1521
|
|
|
1237
1522
|
- **Extend `ApiResourceService`** for new services (signal-based)
|
|
1238
1523
|
- Use reactive signals (`data`, `isLoading`, `total`) in templates
|
|
1239
|
-
- Use `fetchList()` to trigger queries, `reload()` to refresh
|
|
1524
|
+
- Use `fetchList()` to trigger queries (also initializes resource), `reload()` to refresh
|
|
1240
1525
|
- Use async methods (`insertAsync`, `updateAsync`) for one-off operations
|
|
1526
|
+
- Resource is lazy-initialized - no HTTP requests until first `fetchList()` or `initListResource()`
|
|
1241
1527
|
|
|
1242
1528
|
### File URLs
|
|
1243
1529
|
|
|
@@ -1263,6 +1549,7 @@ export const appConfig: ApplicationConfig = {
|
|
|
1263
1549
|
- Use permission guards for route-level access control
|
|
1264
1550
|
- Use `PermissionValidatorService` for programmatic checks in services
|
|
1265
1551
|
- Permissions follow fail-closed model: no access by default
|
|
1552
|
+
- Wildcards supported: `*` (all), `module.*` (module-scoped)
|
|
1266
1553
|
|
|
1267
1554
|
---
|
|
1268
1555
|
|
|
@@ -1298,6 +1585,37 @@ export const appConfig: ApplicationConfig = {
|
|
|
1298
1585
|
|
|
1299
1586
|
**Solution:** ng-shared must NEVER import from ng-layout. Move shared components to ng-shared, layout-specific components to ng-layout.
|
|
1300
1587
|
|
|
1588
|
+
### Provider Token Errors
|
|
1589
|
+
|
|
1590
|
+
**Problem:** `'XXX_PROVIDER not configured'` error at runtime.
|
|
1591
|
+
|
|
1592
|
+
**Solution:** Ensure the provider is registered in `app.config.ts`:
|
|
1593
|
+
|
|
1594
|
+
```typescript
|
|
1595
|
+
// Required providers
|
|
1596
|
+
providers: [
|
|
1597
|
+
{ provide: USER_PROVIDER, useClass: AuthUserProvider },
|
|
1598
|
+
{ provide: COMPANY_API_PROVIDER, useClass: AuthCompanyApiProvider },
|
|
1599
|
+
{ provide: USER_PERMISSION_PROVIDER, useClass: AuthUserPermissionProvider },
|
|
1600
|
+
{ provide: AUTH_STATE_PROVIDER, useClass: AuthStateProviderAdapter },
|
|
1601
|
+
]
|
|
1602
|
+
|
|
1603
|
+
// OR use the convenience function
|
|
1604
|
+
providers: [
|
|
1605
|
+
...provideAuthProviders(),
|
|
1606
|
+
]
|
|
1607
|
+
```
|
|
1608
|
+
|
|
1609
|
+
### Permissions Not Working
|
|
1610
|
+
|
|
1611
|
+
**Problem:** `hasPermission()` returns false even though user should have access.
|
|
1612
|
+
|
|
1613
|
+
**Solution:**
|
|
1614
|
+
|
|
1615
|
+
1. Check if permissions are loaded: `permissionValidator.isLoaded()`
|
|
1616
|
+
2. Verify permission codes match exactly (case-sensitive)
|
|
1617
|
+
3. For wildcard access, ensure user has `*` or `module.*` in their permissions
|
|
1618
|
+
|
|
1301
1619
|
---
|
|
1302
1620
|
|
|
1303
1621
|
## API Reference
|
|
@@ -1306,12 +1624,31 @@ export const appConfig: ApplicationConfig = {
|
|
|
1306
1624
|
|
|
1307
1625
|
| Service | Description |
|
|
1308
1626
|
| ------------------------------ | ------------------------------------- |
|
|
1309
|
-
| `ApiResourceService<DTO, T>` | Signal-based CRUD with resource() API |
|
|
1627
|
+
| `ApiResourceService<DTO, T>` | Signal-based CRUD with resource() API (lazy-initialized, accepts optional `serviceName`) |
|
|
1310
1628
|
| `FileUrlService` | Cloud storage URL fetching |
|
|
1311
|
-
| `PermissionValidatorService` | Permission state management
|
|
1629
|
+
| `PermissionValidatorService` | Permission state management with wildcards |
|
|
1312
1630
|
| `CookieService` | SSR-aware cookie reading |
|
|
1313
1631
|
| `PlatformService` | SSR environment detection |
|
|
1314
1632
|
|
|
1633
|
+
### Classes
|
|
1634
|
+
|
|
1635
|
+
| Class | Description |
|
|
1636
|
+
| ------------------------ | -------------------------------------------- |
|
|
1637
|
+
| `ApiResourceService` | Signal-based CRUD base class (alias: `ApiService`) |
|
|
1638
|
+
| `BaseFormControl` | Abstract base for custom form controls |
|
|
1639
|
+
| `BaseFormPage` | Abstract directive for create/edit pages |
|
|
1640
|
+
| `BaseListPage` | Abstract directive for list pages |
|
|
1641
|
+
|
|
1642
|
+
### Constants
|
|
1643
|
+
|
|
1644
|
+
| Constant | Description |
|
|
1645
|
+
| -------------- | --------------------------------------------- |
|
|
1646
|
+
| `PERMISSIONS` | Aggregated permission codes by module |
|
|
1647
|
+
| `USER_PERMISSIONS` | `{ CREATE, READ, UPDATE, DELETE }` for users |
|
|
1648
|
+
| `ROLE_PERMISSIONS` | `{ CREATE, READ, UPDATE, DELETE }` for roles |
|
|
1649
|
+
| `FILE_PERMISSIONS` | `{ CREATE, READ, UPDATE, DELETE }` for files |
|
|
1650
|
+
| `FILE_TYPE_FILTERS` | Predefined MIME type arrays (IMAGES, DOCUMENTS, etc.) |
|
|
1651
|
+
|
|
1315
1652
|
### Components
|
|
1316
1653
|
|
|
1317
1654
|
| Component | Selector | Description |
|
|
@@ -1348,6 +1685,7 @@ export const appConfig: ApplicationConfig = {
|
|
|
1348
1685
|
| `IBaseEntity` | Base entity with ID and timestamps |
|
|
1349
1686
|
| `ILoggedUserInfo` | Current user info with company ctx |
|
|
1350
1687
|
| `IFilterData` | Filter, pagination, sort payload |
|
|
1688
|
+
| `IFilter` | Filter object (supports arrays) |
|
|
1351
1689
|
| `IDeleteData` | Delete request payload |
|
|
1352
1690
|
| `IDropDown` | Simple label/value pair |
|
|
1353
1691
|
| `ISingleResponse<T>` | Single item response |
|
|
@@ -1364,23 +1702,27 @@ export const appConfig: ApplicationConfig = {
|
|
|
1364
1702
|
| `IFileSelectFilter` | File select filter params |
|
|
1365
1703
|
| `LoadFilesFn` | File loading function type |
|
|
1366
1704
|
| `UploadFileFn` | File upload function type |
|
|
1705
|
+
| `FilesResponseDto` | File URL service response |
|
|
1367
1706
|
| `IAuthStateProvider`| Auth state provider interface |
|
|
1368
1707
|
| `IUserListProvider` | User list extensions provider |
|
|
1369
1708
|
| `IUserListItem` | Base user for list operations |
|
|
1370
1709
|
| `IUserListAction` | User list action definition |
|
|
1371
1710
|
| `IUserListColumn` | Extra column for user list |
|
|
1711
|
+
| `IUserListFilter` | User list filter parameters |
|
|
1712
|
+
| `IUserBranchPermission` | User permissions per branch |
|
|
1713
|
+
| `ServiceName` | `'auth' \| 'administration' \| 'iam' \| 'storage' \| 'formBuilder' \| 'email'` |
|
|
1372
1714
|
|
|
1373
1715
|
### Injection Tokens
|
|
1374
1716
|
|
|
1375
|
-
| Token | Interface | Description |
|
|
1376
|
-
| -------------------------- | ------------------------- | ---------------------------- |
|
|
1377
|
-
| `USER_PROVIDER` | `IUserProvider` | User list for IAM |
|
|
1378
|
-
| `COMPANY_API_PROVIDER` | `ICompanyApiProvider` | Company list for IAM |
|
|
1379
|
-
| `USER_PERMISSION_PROVIDER` | `IUserPermissionProvider` | User permission queries |
|
|
1380
|
-
| `
|
|
1381
|
-
| `
|
|
1382
|
-
| `
|
|
1383
|
-
| `USER_LIST_PROVIDER` | `IUserListProvider` | User list extensions
|
|
1717
|
+
| Token | Interface | Optional | Description |
|
|
1718
|
+
| -------------------------- | ------------------------- | -------- | ---------------------------- |
|
|
1719
|
+
| `USER_PROVIDER` | `IUserProvider` | No | User list for IAM |
|
|
1720
|
+
| `COMPANY_API_PROVIDER` | `ICompanyApiProvider` | No | Company list for IAM |
|
|
1721
|
+
| `USER_PERMISSION_PROVIDER` | `IUserPermissionProvider` | No | User permission queries |
|
|
1722
|
+
| `AUTH_STATE_PROVIDER` | `IAuthStateProvider` | No | Auth state for feature packages |
|
|
1723
|
+
| `PROFILE_UPLOAD_PROVIDER` | `IProfileUploadProvider` | Yes | Profile picture upload (ng-storage) |
|
|
1724
|
+
| `PROFILE_PERMISSION_PROVIDER` | `IProfilePermissionProvider` | Yes | User permissions for profile (ng-iam) |
|
|
1725
|
+
| `USER_LIST_PROVIDER` | `IUserListProvider` | Yes | User list extensions |
|
|
1384
1726
|
|
|
1385
1727
|
## See Also
|
|
1386
1728
|
|
|
@@ -1391,5 +1733,6 @@ export const appConfig: ApplicationConfig = {
|
|
|
1391
1733
|
|
|
1392
1734
|
---
|
|
1393
1735
|
|
|
1394
|
-
**Last Updated:** 2026-02-
|
|
1736
|
+
**Last Updated:** 2026-02-25
|
|
1737
|
+
**Version:** 3.0.0
|
|
1395
1738
|
**Angular Version:** 21
|