@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 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
- interface IFilter { [key: string]: any }
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. Services
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. All endpoints use POST (RPC-style).
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
- super('users', http); // Base URL: APP_CONFIG.apiBaseUrl + '/users'
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.isPermissionsLoaded()) {
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` | Whether permissions have been set |
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
- ## 4. Components
626
+ ## 5. Components
505
627
 
506
628
  ### IconComponent
507
629
 
@@ -782,7 +904,7 @@ export class MyComponent {
782
904
 
783
905
  ---
784
906
 
785
- ## 5. Directives
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
- ## 6. Guards
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
- ## 7. Utilities
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
- ## 8. Classes
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
- ## 9. Modules
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, + directives
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: Button, Table, Card, Dialog, InputText, Select, MultiSelect,
989
- // DatePicker, Checkbox, FileUpload, Image, Tag, Tabs, TreeTable, and more (27 modules)
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
- ## 10. Provider Interfaces (Package Independence)
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<any>>;
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 provider.
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
- | `PROFILE_UPLOAD_PROVIDER` | `IProfileUploadProvider` | Profile picture upload (ng-storage) |
1381
- | `PROFILE_PERMISSION_PROVIDER` | `IProfilePermissionProvider` | User permissions for profile (ng-iam) |
1382
- | `AUTH_STATE_PROVIDER` | `IAuthStateProvider` | Auth state for feature packages |
1383
- | `USER_LIST_PROVIDER` | `IUserListProvider` | User list extensions (optional) |
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-21
1736
+ **Last Updated:** 2026-02-25
1737
+ **Version:** 3.0.0
1395
1738
  **Angular Version:** 21