@flusys/ng-shared 1.1.0-beta → 1.1.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:** 1.1.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`
@@ -144,6 +145,7 @@ interface IFileUploadOptions {
144
145
  }
145
146
 
146
147
  interface IUploadedFile {
148
+ id?: string; // File manager ID (UUID) - available when registered
147
149
  name: string;
148
150
  key: string;
149
151
  size: number;
@@ -178,7 +180,7 @@ FILE_TYPE_FILTERS.ALL // [] (allows all)
178
180
  getAcceptString(['image/*']) // 'image/*'
179
181
  isFileTypeAllowed(file, ['image/*']) // true/false
180
182
  getFileIconClass('image/png') // 'pi pi-image'
181
- formatFileSize('1024') // '1 KB'
183
+ formatFileSize(1024) // '1 KB'
182
184
  ```
183
185
 
184
186
  ### Response Interfaces
@@ -310,12 +312,57 @@ enum IconTypeEnum {
310
312
 
311
313
  ---
312
314
 
313
- ## 3. Services
315
+ ## 3. Constants
316
+
317
+ ### Permission Constants
318
+
319
+ Centralized permission codes for type-safe permission checks. Single source of truth to prevent typos.
320
+
321
+ ```typescript
322
+ import { PERMISSIONS, USER_PERMISSIONS, ROLE_PERMISSIONS } from '@flusys/ng-shared';
323
+
324
+ // Use constants instead of strings
325
+ *hasPermission="PERMISSIONS.USER.READ"
326
+
327
+ // Individual permission groups available:
328
+ USER_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
329
+ COMPANY_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
330
+ BRANCH_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
331
+ ACTION_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
332
+ ROLE_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
333
+ ROLE_ACTION_PERMISSIONS // { READ, ASSIGN }
334
+ USER_ROLE_PERMISSIONS // { READ, ASSIGN }
335
+ USER_ACTION_PERMISSIONS // { READ, ASSIGN }
336
+ COMPANY_ACTION_PERMISSIONS // { READ, ASSIGN }
337
+ FILE_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
338
+ FOLDER_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
339
+ STORAGE_CONFIG_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
340
+ EMAIL_CONFIG_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
341
+ EMAIL_TEMPLATE_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
342
+ FORM_PERMISSIONS // { CREATE, READ, UPDATE, DELETE }
343
+
344
+ // Aggregated object with all permissions
345
+ PERMISSIONS.USER.READ // 'user.read'
346
+ PERMISSIONS.ROLE.CREATE // 'role.create'
347
+ PERMISSIONS.FILE.DELETE // 'file.delete'
348
+ ```
349
+
350
+ **Type:** `PermissionCode` - Union type of all valid permission code strings.
351
+
352
+ ---
353
+
354
+ ## 4. Services
314
355
 
315
356
  ### ApiResourceService
316
357
 
317
358
  Signal-based CRUD service using Angular 21 `resource()` API. All endpoints use POST (RPC-style).
318
359
 
360
+ **ServiceName Type:**
361
+
362
+ ```typescript
363
+ type ServiceName = 'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email';
364
+ ```
365
+
319
366
  **Define a service:**
320
367
 
321
368
  ```typescript
@@ -326,11 +373,29 @@ import { ApiResourceService } from '@flusys/ng-shared';
326
373
  @Injectable({ providedIn: 'root' })
327
374
  export class UserService extends ApiResourceService<UserDto, IUser> {
328
375
  constructor(http: HttpClient) {
329
- super('users', http); // Base URL: APP_CONFIG.apiBaseUrl + '/users'
376
+ // Option 1: Use global apiBaseUrl (default)
377
+ super('users', http);
378
+ // Base URL: APP_CONFIG.apiBaseUrl + '/users'
379
+
380
+ // Option 2: Use feature-specific service URL (recommended)
381
+ super('users', http, 'administration');
382
+ // Base URL: APP_CONFIG.services.administration.baseUrl + '/users'
330
383
  }
331
384
  }
332
385
  ```
333
386
 
387
+ **Constructor Parameters:**
388
+
389
+ | Parameter | Type | Required | Description |
390
+ |-----------|------|----------|-------------|
391
+ | `moduleApiName` | `string` | Yes | API path segment (e.g., 'users', 'file-manager') |
392
+ | `http` | `HttpClient` | Yes | Angular HttpClient instance |
393
+ | `serviceName` | `ServiceName` | No | Feature service name for URL resolution |
394
+
395
+ **Service URL Resolution:**
396
+ - If `serviceName` is provided, uses `getServiceUrl(config, serviceName)` to resolve the base URL
397
+ - Falls back to `APP_CONFIG.apiBaseUrl` if service not found or `serviceName` not provided
398
+
334
399
  **Endpoint mapping:**
335
400
 
336
401
  | Method | HTTP | Endpoint | Response |
@@ -501,7 +566,7 @@ if (!platform.isServer) {
501
566
 
502
567
  ---
503
568
 
504
- ## 4. Components
569
+ ## 5. Components
505
570
 
506
571
  ### IconComponent
507
572
 
@@ -517,7 +582,7 @@ Flexible icon renderer supporting PrimeNG icons, image files, and SVG.
517
582
  <!-- Image file -->
518
583
  <lib-icon icon="/assets/logo.png" [iconType]="IconTypeEnum.IMAGE_FILE_LINK" />
519
584
 
520
- <!-- SVG tag -->
585
+ <!-- SVG tag (shows fallback icon - full SVG rendering coming soon) -->
521
586
  <lib-icon icon="<svg>...</svg>" [iconType]="IconTypeEnum.DIRECT_TAG_SVG" />
522
587
  ```
523
588
 
@@ -782,7 +847,7 @@ export class MyComponent {
782
847
 
783
848
  ---
784
849
 
785
- ## 5. Directives
850
+ ## 6. Directives
786
851
 
787
852
  ### HasPermissionDirective
788
853
 
@@ -856,7 +921,7 @@ Prevents default browser behavior on specified events and emits the event.
856
921
 
857
922
  ---
858
923
 
859
- ## 6. Guards
924
+ ## 7. Guards
860
925
 
861
926
  Route-level guards for permission-based access control. All guards deny access when permissions are not loaded (fail-closed).
862
927
 
@@ -904,7 +969,7 @@ AND logic - allows access only if user has ALL specified permissions.
904
969
 
905
970
  ---
906
971
 
907
- ## 7. Utilities
972
+ ## 8. Utilities
908
973
 
909
974
  ### Permission Evaluator
910
975
 
@@ -927,9 +992,34 @@ hasAnyPermission(['user.view', 'user.delete'], userPermissions); // true (has u
927
992
  hasAllPermissions(['user.view', 'user.delete'], userPermissions); // false (missing user.delete)
928
993
  ```
929
994
 
995
+ ### Scroll Pagination
996
+
997
+ Utility for lazy-loading dropdowns with scroll detection.
998
+
999
+ ```typescript
1000
+ import { checkScrollPagination, ScrollPaginationConfig } from '@flusys/ng-shared';
1001
+
1002
+ // In a component
1003
+ onScroll(event: Event): void {
1004
+ const nextPagination = checkScrollPagination(event, {
1005
+ pagination: this.pagination(),
1006
+ total: this.total(),
1007
+ isLoading: this.isLoading(),
1008
+ threshold: 50, // pixels from bottom (default: 50)
1009
+ });
1010
+ if (nextPagination) {
1011
+ this.onPagination.emit(nextPagination);
1012
+ }
1013
+ }
1014
+ ```
1015
+
1016
+ **Interface:** `ScrollPaginationConfig` - `{ threshold?, pagination, total, isLoading }`
1017
+
1018
+ **Returns:** `IPagination | null` - Next page pagination or null if not needed.
1019
+
930
1020
  ---
931
1021
 
932
- ## 8. Classes
1022
+ ## 9. Classes
933
1023
 
934
1024
  ### BaseFormControl
935
1025
 
@@ -965,9 +1055,78 @@ export class MySelectComponent extends BaseFormControl<string | null> {
965
1055
 
966
1056
  **Helper:** `provideValueAccessor(ComponentClass)` - Factory for `NG_VALUE_ACCESSOR` provider
967
1057
 
1058
+ ### BaseFormPage
1059
+
1060
+ Abstract directive for form page components (create/edit).
1061
+
1062
+ ```typescript
1063
+ import { BaseFormPage } from '@flusys/ng-shared';
1064
+
1065
+ @Component({ ... })
1066
+ export class ProductFormComponent extends BaseFormPage<IProduct, IProductFormModel> {
1067
+ private readonly productService = inject(ProductApiService);
1068
+ private readonly _formModel = signal<IProductFormModel>({ name: '', price: 0 });
1069
+ readonly formModel = this._formModel.asReadonly();
1070
+
1071
+ getFormModel(): Signal<IProductFormModel> { return this.formModel; }
1072
+ getResourceRoute(): string { return '/products'; }
1073
+ getResourceName(): string { return 'Product'; }
1074
+ isFormValid(): boolean { return this.formModel().name.trim().length > 0; }
1075
+
1076
+ loadItem(id: string): void {
1077
+ this.productService.findById(id).subscribe(res => {
1078
+ if (res.success && res.data) {
1079
+ this.existingItem.set(res.data);
1080
+ this._formModel.set({ name: res.data.name, price: res.data.price });
1081
+ }
1082
+ });
1083
+ }
1084
+
1085
+ createItem(model: IProductFormModel): Observable<unknown> {
1086
+ return this.productService.insert(model);
1087
+ }
1088
+
1089
+ updateItem(model: IProductFormModel): Observable<unknown> {
1090
+ return this.productService.update({ id: this.existingItem()!.id, ...model });
1091
+ }
1092
+ }
1093
+ ```
1094
+
1095
+ **Signals:** `isLoading`, `existingItem`, `isEditMode` (computed)
1096
+
1097
+ **Methods:** `onSubmit()`, `onCancel()`, `showSuccess()`, `showError()`, `showValidationError()`
1098
+
1099
+ ### BaseListPage
1100
+
1101
+ Abstract directive for list page components with pagination and CRUD operations.
1102
+
1103
+ ```typescript
1104
+ import { BaseListPage } from '@flusys/ng-shared';
1105
+
1106
+ @Component({ ... })
1107
+ export class UserListComponent extends BaseListPage<IUser> {
1108
+ private readonly userService = inject(UserApiService);
1109
+
1110
+ getResourceRoute(): string { return '/users'; }
1111
+ getDeleteConfirmMessage(user: IUser): string { return `Delete "${user.name}"?`; }
1112
+
1113
+ async loadData(): Promise<void> {
1114
+ this.isLoading.set(true);
1115
+ const res = await this.userService.findByIdAsync(...);
1116
+ this.items.set(res.data ?? []);
1117
+ this.total.set(res.meta?.total ?? 0);
1118
+ this.isLoading.set(false);
1119
+ }
1120
+ }
1121
+ ```
1122
+
1123
+ **Signals:** `items`, `isLoading`, `total`, `pageSize`, `first`, `currentPage` (computed), `showCompanyInfo` (computed)
1124
+
1125
+ **Methods:** `onCreate()`, `onEdit(id)`, `onPageChange(event)`, `onDelete()`, `onDeleteAsync()`, `showSuccess()`, `showError()`, `showInfo()`, `showWarn()`
1126
+
968
1127
  ---
969
1128
 
970
- ## 9. Modules
1129
+ ## 10. Modules
971
1130
 
972
1131
  ### AngularModule
973
1132
 
@@ -991,7 +1150,7 @@ import { PrimeModule } from '@flusys/ng-shared';
991
1150
 
992
1151
  ---
993
1152
 
994
- ## 10. Provider Interfaces (Package Independence)
1153
+ ## 11. Provider Interfaces (Package Independence)
995
1154
 
996
1155
  ng-shared defines **provider interfaces** to enable feature packages (ng-iam, ng-storage) to access auth functionality without direct dependencies.
997
1156
 
@@ -1098,6 +1257,101 @@ interface IProfilePermissionProvider {
1098
1257
  }
1099
1258
  ```
1100
1259
 
1260
+ #### IAuthStateProvider / `AUTH_STATE_PROVIDER`
1261
+
1262
+ Auth state access for feature packages (form-builder, etc.) that need to check authentication without depending on ng-auth directly.
1263
+
1264
+ ```typescript
1265
+ interface IAuthStateProvider {
1266
+ /** Signal indicating if user is currently authenticated */
1267
+ isAuthenticated: Signal<boolean>;
1268
+
1269
+ /** Initialize auth state (restore session from cookies/storage) */
1270
+ initialize(): Observable<void>;
1271
+ }
1272
+ ```
1273
+
1274
+ **Usage:**
1275
+
1276
+ ```typescript
1277
+ import { AUTH_STATE_PROVIDER } from '@flusys/ng-shared';
1278
+
1279
+ @Component({...})
1280
+ export class PublicFormComponent {
1281
+ private readonly authState = inject(AUTH_STATE_PROVIDER);
1282
+
1283
+ ngOnInit() {
1284
+ this.authState.initialize().subscribe(() => {
1285
+ if (this.authState.isAuthenticated()) {
1286
+ // User is logged in
1287
+ }
1288
+ });
1289
+ }
1290
+ }
1291
+ ```
1292
+
1293
+ #### IUserListProvider / `USER_LIST_PROVIDER`
1294
+
1295
+ Extends user list pages with extra columns, actions, and data enrichment. Optional provider.
1296
+
1297
+ ```typescript
1298
+ interface IUserListItem {
1299
+ id: string;
1300
+ name: string;
1301
+ email: string;
1302
+ phone?: string;
1303
+ isActive?: boolean;
1304
+ [key: string]: unknown;
1305
+ }
1306
+
1307
+ interface IUserListAction<T = IUserListItem> {
1308
+ id: string;
1309
+ label: string;
1310
+ icon?: string;
1311
+ severity?: 'primary' | 'secondary' | 'success' | 'info' | 'warn' | 'danger';
1312
+ permission?: string;
1313
+ tooltip?: string;
1314
+ disabled?: boolean | ((user: T) => boolean);
1315
+ visible?: boolean | ((user: T) => boolean);
1316
+ }
1317
+
1318
+ interface IUserListColumn {
1319
+ field: string;
1320
+ header: string;
1321
+ width?: string;
1322
+ sortable?: boolean;
1323
+ templateType?: 'text' | 'badge' | 'date' | 'boolean' | 'custom';
1324
+ }
1325
+
1326
+ interface IUserListProvider<T extends IUserListItem = IUserListItem> {
1327
+ getExtraColumns?(): IUserListColumn[];
1328
+ getExtraRowActions?(): IUserListAction<T>[];
1329
+ getExtraToolbarActions?(): IUserListAction<T>[];
1330
+ onRowAction?(actionId: string, user: T): void;
1331
+ onToolbarAction?(actionId: string, selectedUsers: T[]): void;
1332
+ enrichListData?(users: T[]): Observable<T[]>;
1333
+ }
1334
+ ```
1335
+
1336
+ **Usage:**
1337
+
1338
+ ```typescript
1339
+ // In app.config.ts
1340
+ providers: [
1341
+ { provide: USER_LIST_PROVIDER, useClass: MyUserListProvider },
1342
+ ]
1343
+
1344
+ // Implementation
1345
+ @Injectable({ providedIn: 'root' })
1346
+ export class MyUserListProvider implements IUserListProvider {
1347
+ getExtraRowActions() {
1348
+ return [
1349
+ { id: 'assign-role', label: 'Assign Role', icon: 'pi pi-users' },
1350
+ ];
1351
+ }
1352
+ }
1353
+ ```
1354
+
1101
1355
  ### Usage in Consuming Packages
1102
1356
 
1103
1357
  ```typescript
@@ -1211,12 +1465,31 @@ export const appConfig: ApplicationConfig = {
1211
1465
 
1212
1466
  | Service | Description |
1213
1467
  | ------------------------------ | ------------------------------------- |
1214
- | `ApiResourceService<DTO, T>` | Signal-based CRUD with resource() API |
1468
+ | `ApiResourceService<DTO, T>` | Signal-based CRUD with resource() API (accepts optional `serviceName` for feature-specific URLs) |
1215
1469
  | `FileUrlService` | Cloud storage URL fetching |
1216
1470
  | `PermissionValidatorService` | Permission state management |
1217
1471
  | `CookieService` | SSR-aware cookie reading |
1218
1472
  | `PlatformService` | SSR environment detection |
1219
1473
 
1474
+ ### Classes
1475
+
1476
+ | Class | Description |
1477
+ | ------------------------ | -------------------------------------------- |
1478
+ | `ApiResourceService` | Signal-based CRUD base class (alias: `ApiService`) |
1479
+ | `BaseFormControl` | Abstract base for custom form controls |
1480
+ | `BaseFormPage` | Abstract directive for create/edit pages |
1481
+ | `BaseListPage` | Abstract directive for list pages |
1482
+
1483
+ ### Constants
1484
+
1485
+ | Constant | Description |
1486
+ | -------------- | --------------------------------------------- |
1487
+ | `PERMISSIONS` | Aggregated permission codes by module |
1488
+ | `USER_PERMISSIONS` | `{ CREATE, READ, UPDATE, DELETE }` for users |
1489
+ | `ROLE_PERMISSIONS` | `{ CREATE, READ, UPDATE, DELETE }` for roles |
1490
+ | `FILE_PERMISSIONS` | `{ CREATE, READ, UPDATE, DELETE }` for files |
1491
+ | `FILE_TYPE_FILTERS` | Predefined MIME type arrays (IMAGES, DOCUMENTS, etc.) |
1492
+
1220
1493
  ### Components
1221
1494
 
1222
1495
  | Component | Selector | Description |
@@ -1269,6 +1542,12 @@ export const appConfig: ApplicationConfig = {
1269
1542
  | `IFileSelectFilter` | File select filter params |
1270
1543
  | `LoadFilesFn` | File loading function type |
1271
1544
  | `UploadFileFn` | File upload function type |
1545
+ | `IAuthStateProvider`| Auth state provider interface |
1546
+ | `IUserListProvider` | User list extensions provider |
1547
+ | `IUserListItem` | Base user for list operations |
1548
+ | `IUserListAction` | User list action definition |
1549
+ | `IUserListColumn` | Extra column for user list |
1550
+ | `ServiceName` | `'auth' \| 'administration' \| 'iam' \| 'storage' \| 'formBuilder' \| 'email'` |
1272
1551
 
1273
1552
  ### Injection Tokens
1274
1553
 
@@ -1279,6 +1558,8 @@ export const appConfig: ApplicationConfig = {
1279
1558
  | `USER_PERMISSION_PROVIDER` | `IUserPermissionProvider` | User permission queries |
1280
1559
  | `PROFILE_UPLOAD_PROVIDER` | `IProfileUploadProvider` | Profile picture upload (ng-storage) |
1281
1560
  | `PROFILE_PERMISSION_PROVIDER` | `IProfilePermissionProvider` | User permissions for profile (ng-iam) |
1561
+ | `AUTH_STATE_PROVIDER` | `IAuthStateProvider` | Auth state for feature packages |
1562
+ | `USER_LIST_PROVIDER` | `IUserListProvider` | User list extensions (optional) |
1282
1563
 
1283
1564
  ## See Also
1284
1565
 
@@ -1289,5 +1570,6 @@ export const appConfig: ApplicationConfig = {
1289
1570
 
1290
1571
  ---
1291
1572
 
1292
- **Last Updated:** 2026-02-16
1573
+ **Last Updated:** 2026-02-23
1574
+ **Version:** 1.1.0
1293
1575
  **Angular Version:** 21