@flusys/ng-iam 1.1.0 → 1.2.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +44 -1179
  2. package/fesm2022/flusys-ng-iam-action-form-page.component-Vc5Toouu.mjs +502 -0
  3. package/fesm2022/flusys-ng-iam-action-form-page.component-Vc5Toouu.mjs.map +1 -0
  4. package/fesm2022/flusys-ng-iam-action-list-page.component-UGKclpFx.mjs +348 -0
  5. package/fesm2022/flusys-ng-iam-action-list-page.component-UGKclpFx.mjs.map +1 -0
  6. package/fesm2022/{flusys-ng-iam-flusys-ng-iam-CJAQT60K.mjs → flusys-ng-iam-flusys-ng-iam-CrycDULz.mjs} +1814 -1622
  7. package/fesm2022/flusys-ng-iam-flusys-ng-iam-CrycDULz.mjs.map +1 -0
  8. package/fesm2022/flusys-ng-iam-iam-container.component-D0m8u6nu.mjs +105 -0
  9. package/fesm2022/flusys-ng-iam-iam-container.component-D0m8u6nu.mjs.map +1 -0
  10. package/fesm2022/{flusys-ng-iam-permission-page.component-DcgT7L3_.mjs → flusys-ng-iam-permission-page.component-x88X4AgJ.mjs} +38 -32
  11. package/fesm2022/flusys-ng-iam-permission-page.component-x88X4AgJ.mjs.map +1 -0
  12. package/fesm2022/flusys-ng-iam-role-form-page.component-InsxdriD.mjs +335 -0
  13. package/fesm2022/flusys-ng-iam-role-form-page.component-InsxdriD.mjs.map +1 -0
  14. package/fesm2022/flusys-ng-iam-role-list-page.component-8NGXCeSZ.mjs +389 -0
  15. package/fesm2022/flusys-ng-iam-role-list-page.component-8NGXCeSZ.mjs.map +1 -0
  16. package/fesm2022/flusys-ng-iam.mjs +1 -1
  17. package/package.json +11 -10
  18. package/types/flusys-ng-iam.d.ts +53 -252
  19. package/fesm2022/flusys-ng-iam-action-form-page.component-eXpZNJ_H.mjs +0 -389
  20. package/fesm2022/flusys-ng-iam-action-form-page.component-eXpZNJ_H.mjs.map +0 -1
  21. package/fesm2022/flusys-ng-iam-action-list-page.component-BtJlGcTj.mjs +0 -262
  22. package/fesm2022/flusys-ng-iam-action-list-page.component-BtJlGcTj.mjs.map +0 -1
  23. package/fesm2022/flusys-ng-iam-flusys-ng-iam-CJAQT60K.mjs.map +0 -1
  24. package/fesm2022/flusys-ng-iam-iam-container.component-UYJjqYV9.mjs +0 -92
  25. package/fesm2022/flusys-ng-iam-iam-container.component-UYJjqYV9.mjs.map +0 -1
  26. package/fesm2022/flusys-ng-iam-permission-page.component-DcgT7L3_.mjs.map +0 -1
  27. package/fesm2022/flusys-ng-iam-role-form-page.component-D_AAEay2.mjs +0 -272
  28. package/fesm2022/flusys-ng-iam-role-form-page.component-D_AAEay2.mjs.map +0 -1
  29. package/fesm2022/flusys-ng-iam-role-list-page.component-D4J1by6Q.mjs +0 -299
  30. package/fesm2022/flusys-ng-iam-role-list-page.component-D4J1by6Q.mjs.map +0 -1
package/README.md CHANGED
@@ -1,1230 +1,95 @@
1
- # @flusys/ng-iam Package Guide
1
+ # @flusys/ng-iam
2
2
 
3
- ## Overview
3
+ Identity and Access Management UI for FLUSYS Angular apps — role management, permission assignment, and reactive permission checking via signals.
4
4
 
5
- `@flusys/ng-iam` provides Identity and Access Management (IAM) for FLUSYS applications. It includes permission state management, role-based access control (RBAC), action management with AND/OR prerequisite logic, and dynamic menu filtering based on user permissions.
6
-
7
- **Key Principle:** ng-iam is **100% independent** from ng-auth through the **Provider Interface Pattern**. No compile-time or runtime dependencies on ng-auth.
8
-
9
- ## Package Information
10
-
11
- | Item | Value |
12
- |------|-------|
13
- | Package | `@flusys/ng-iam` |
14
- | Version | 1.0.1 |
15
- | Dependencies | ng-core, ng-shared, ng-layout (IMenuItem only) |
16
- | NO dependency on | ng-auth (fully independent via Provider Pattern) |
17
- | CSS Framework | Tailwind CSS (not PrimeFlex) |
18
- | Build | `npm run build:ng-iam` |
19
- | Entry point | `public-api.ts` |
20
-
21
- ---
22
-
23
- ## Table of Contents
24
-
25
- 1. [Architecture](#architecture)
26
- 2. [Provider Interfaces](#provider-interfaces)
27
- 3. [Integration Options](#integration-options)
28
- 4. [Permission Modes](#permission-modes)
29
- 5. [Routes](#routes)
30
- 6. [Services](#services)
31
- 7. [Components](#components)
32
- 8. [Page Components](#page-components)
33
- 9. [Interfaces](#interfaces)
34
- 10. [Utils & Constants](#utils--constants)
35
- 11. [Backend Integration](#backend-integration)
36
- 12. [Best Practices](#best-practices)
37
- 13. [Common Issues](#common-issues)
38
-
39
- ---
40
-
41
- ## Architecture
42
-
43
- ```
44
- ng-shared (defines interfaces + tokens)
45
- |
46
- Your Auth System (implements interfaces with adapters)
47
- |
48
- ng-iam (consumes interfaces via DI)
49
- ```
50
-
51
- ng-iam resolves all auth-related data at runtime via DI tokens defined in ng-shared. If a required token is not provided, Angular throws a clear error at injection time.
5
+ [![npm version](https://img.shields.io/npm/v/@flusys/ng-iam.svg)](https://www.npmjs.com/package/@flusys/ng-iam)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
52
7
 
53
8
  ---
54
9
 
55
- ## Provider Interfaces
56
-
57
- ng-iam requires these provider interfaces:
58
-
59
- | Token | Source | Purpose | Required? |
60
- |-------|--------|---------|-----------|
61
- | `USER_PROVIDER` | `@flusys/ng-shared` | User list for role/action assignment | Yes |
62
- | `LAYOUT_AUTH_STATE` | `@flusys/ng-layout` | Current company/branch context | Yes (if company feature enabled) |
63
- | `COMPANY_API_PROVIDER` | `@flusys/ng-shared` | Company list for whitelisting | Optional (for CompanyActionSelector) |
64
- | `USER_PERMISSION_PROVIDER` | `@flusys/ng-shared` | User branch permissions | Optional (for branch-scoped permissions) |
65
-
66
- ### How Components Use Providers
67
-
68
- User selection uses `lib-user-select` component from ng-shared (encapsulates USER_PROVIDER):
69
-
70
- ```html
71
- <!-- UserActionSelectorComponent / UserRoleSelectorComponent -->
72
- <lib-user-select
73
- [(value)]="selectedUserId"
74
- [isEditMode]="true"
75
- placeHolder="Select a user"
76
- />
77
- ```
78
-
79
- Company context is accessed via `LAYOUT_AUTH_STATE`:
80
-
81
- ```typescript
82
- private readonly companyContext = inject(LAYOUT_AUTH_STATE);
83
-
84
- // Get current company
85
- const companyId = this.companyContext.currentCompanyInfo()?.id;
86
- ```
87
-
88
- Branch permissions loaded from `USER_PERMISSION_PROVIDER`:
10
+ ## Installation
89
11
 
90
- ```typescript
91
- private readonly userPermissionProvider = inject(USER_PERMISSION_PROVIDER);
92
-
93
- const response = await this.userPermissionProvider
94
- .getUserBranchPermissions(userId)
95
- .toPromise();
12
+ ```bash
13
+ npm install @flusys/ng-iam @flusys/ng-core @flusys/ng-shared
96
14
  ```
97
15
 
98
- ---
99
-
100
- ## Integration Options
101
-
102
- ### Option 1: With FLUSYS ng-auth (Recommended)
103
-
104
- ng-auth provides pre-built adapters for all required interfaces.
16
+ ## 1. Register Providers
105
17
 
106
18
  ```typescript
107
19
  // app.config.ts
108
- import { provideAuthProviders } from '@flusys/ng-auth';
20
+ import { provideIamProviders } from '@flusys/ng-iam';
109
21
 
110
22
  export const appConfig: ApplicationConfig = {
111
23
  providers: [
112
- provideAuthProviders(), // Registers all adapters
24
+ ...provideIamProviders(), // registers PROFILE_PERMISSION_PROVIDER for ng-shared's permission directives/guards
113
25
  ],
114
26
  };
115
27
  ```
116
28
 
117
- **Reference adapters in ng-auth:**
118
-
119
- | Adapter | Interface | Path |
120
- |---------|-----------|------|
121
- | `AuthUserProviderAdapter` | `IUserProvider` | `projects/ng-auth/adapters/` |
122
- | `AuthLayoutStateAdapter` | `ILayoutAuthState` | `projects/ng-auth/adapters/` |
123
- | `AuthCompanyApiProviderAdapter` | `ICompanyApiProvider` | `projects/ng-auth/adapters/` |
124
- | `AuthUserPermissionProviderAdapter` | `IUserPermissionProvider` | `projects/ng-auth/adapters/` |
125
-
126
- ### Option 2: With Custom Auth System
127
-
128
- Implement the required interfaces with your own auth system. No ng-auth dependency needed.
129
-
130
- ```typescript
131
- // Minimum configuration (basic IAM)
132
- providers: [
133
- { provide: USER_PROVIDER, useClass: YourUserProvider },
134
- { provide: LAYOUT_AUTH_STATE, useClass: YourCompanyContextProvider },
135
- ]
136
-
137
- // Full configuration (company + branch permissions)
138
- providers: [
139
- { provide: USER_PROVIDER, useClass: YourUserProvider },
140
- { provide: LAYOUT_AUTH_STATE, useClass: YourCompanyContextProvider },
141
- { provide: COMPANY_API_PROVIDER, useClass: YourCompanyApiProvider },
142
- { provide: USER_PERMISSION_PROVIDER, useClass: YourUserPermissionProvider },
143
- ]
144
- ```
145
-
146
- **Feature matrix:**
147
-
148
- | Feature | Providers Needed |
149
- |---------|-----------------|
150
- | Basic role assignments | USER_PROVIDER only |
151
- | Company-scoped permissions | + LAYOUT_AUTH_STATE, COMPANY_API_PROVIDER |
152
- | Branch-scoped permissions | + LAYOUT_AUTH_STATE, USER_PERMISSION_PROVIDER |
153
- | Direct user permissions | + USER_PERMISSION_PROVIDER |
154
-
155
- All adapters must return `IListResponse<T>` or `ISingleResponse<T>` format from `@flusys/ng-shared`.
156
-
157
- ---
158
-
159
- ## Permission Modes
160
-
161
- FLUSYS IAM supports three permission modes configured via `APP_CONFIG.iam.permissionMode`:
162
-
163
- | Mode | Structure | Tabs Available | Use Case |
164
- |------|-----------|---------------|----------|
165
- | **DIRECT** | User -> Actions | User-Actions | Small apps, per-user permissions |
166
- | **RBAC** | User -> Role -> Actions | Role-Actions, User-Roles | Standard apps, role templates |
167
- | **FULL** | Both combined | Role-Actions, User-Roles, User-Actions | Maximum flexibility |
168
-
169
- When company feature is enabled, a **Company-Actions** tab is added for whitelisting actions per company.
170
-
171
- ---
172
-
173
- ## Routes
174
-
175
- **Export:** `IAM_ROUTES` from `@flusys/ng-iam`
176
-
177
- ```
178
- /iam (IamContainerComponent - tabbed navigation)
179
- /actions (ActionListPageComponent - hierarchical tree)
180
- /actions/new (ActionFormPageComponent - create)
181
- /actions/:id (ActionFormPageComponent - edit)
182
- /roles (RoleListPageComponent - paginated table)
183
- /roles/new (RoleFormPageComponent - create)
184
- /roles/:id (RoleFormPageComponent - edit)
185
- /permissions (PermissionPageComponent - tabbed assignment UI)
186
- (default redirect -> /actions)
187
- ```
188
-
189
- **Usage in app routes:**
29
+ ## 2. Add IAM Routes
190
30
 
191
31
  ```typescript
32
+ // app.routes.ts
192
33
  import { IAM_ROUTES } from '@flusys/ng-iam';
193
34
 
194
35
  export const routes: Routes = [
195
- { path: 'iam', children: IAM_ROUTES },
36
+ {
37
+ path: 'iam',
38
+ loadChildren: () => IAM_ROUTES,
39
+ },
196
40
  ];
197
41
  ```
198
42
 
199
- ---
200
-
201
- ## Services
202
-
203
- ### PermissionStateService
204
-
205
- Manages current user's permissions with signal-based state. Provided at root level.
206
-
207
- **Dependencies:** `MyPermissionsApiService`, `PermissionValidatorService` (from ng-shared)
208
-
209
- **Signals:**
210
-
211
- | Signal | Type | Description |
212
- |--------|------|-------------|
213
- | `permissions` | `Signal<IMyPermissionsResponseDto \| null>` | Current user permissions (readonly) |
214
- | `isLoading` | `Signal<boolean>` | Loading state (readonly) |
215
-
216
- **Methods:**
217
-
218
- | Method | Returns | Description |
219
- |--------|---------|-------------|
220
- | `loadPermissions(dto?)` | `Observable<void>` | Load permissions from `/iam/permissions/my-permissions` |
221
- | `isLoaded()` | `boolean` | Check if permissions have been loaded |
222
-
223
- Permission checking is delegated to `PermissionValidatorService` from ng-shared (hasAction, hasAllActions, hasAnyAction, getActionCodes).
224
-
225
- ```typescript
226
- const permissionState = inject(PermissionStateService);
227
-
228
- // Load permissions (typically in guard)
229
- await firstValueFrom(permissionState.loadPermissions());
230
-
231
- // Check permissions (via PermissionValidatorService)
232
- const validator = inject(PermissionValidatorService);
233
- validator.hasAction('user.create');
234
- validator.hasAllActions(['user.create', 'user.update']);
235
- validator.hasAnyAction(['user.view', 'user.list']);
236
- ```
237
-
238
- ---
239
-
240
- ### ActionApiService
241
-
242
- Extends `ApiResourceService<IUpdateActionDto, IAction>` for action CRUD.
243
-
244
- **Resource:** `iam/actions`
245
-
246
- **Additional Methods:**
247
-
248
- | Method | Endpoint | Description |
249
- |--------|----------|-------------|
250
- | `getTree(search?, isActive?, withDeleted?)` | `POST /iam/actions/tree` | Get hierarchical action tree |
251
- | `getActionsForPermission()` | `POST /iam/actions/tree-for-permission` | Get actions filtered by company whitelist |
252
-
253
- Inherits standard CRUD from `ApiResourceService`: `getAll`, `getById`, `insert`, `update`, `delete`.
254
-
255
- ---
256
-
257
- ### RoleApiService
258
-
259
- Extends `ApiResourceService<IUpdateRoleDto, IRole>` for role CRUD.
260
-
261
- **Resource:** `iam/roles`
262
-
263
- Inherits standard CRUD from `ApiResourceService`. Only available in RBAC/FULL mode.
264
-
265
- ---
266
-
267
- ### PermissionApiService
268
-
269
- Extends `BaseApiService` for all permission assignment operations.
270
-
271
- **Base:** `super('iam')` - all paths resolved via `this.getUrl(...)` relative to `/iam/`
272
-
273
- **User -> Action (Direct Permissions):**
274
-
275
- | Method | Endpoint | Description |
276
- |--------|----------|-------------|
277
- | `assignUserActions(data)` | `POST /iam/permissions/user-actions/assign` | Assign/remove actions for user |
278
- | `getUserActions(userId, query?)` | `GET /iam/permissions/user-actions/{userId}` | Get user's direct actions |
279
-
280
- **User -> Role:**
281
-
282
- | Method | Endpoint | Description |
283
- |--------|----------|-------------|
284
- | `assignUserRoles(data)` | `POST /iam/permissions/user-roles/assign` | Assign/remove roles for user |
285
- | `getUserRoles(userId, query?)` | `GET /iam/permissions/user-roles/{userId}` | Get user's roles |
286
-
287
- **Role -> Action:**
288
-
289
- | Method | Endpoint | Description |
290
- |--------|----------|-------------|
291
- | `assignRoleActions(data)` | `POST /iam/permissions/role-actions/assign` | Assign/remove actions for role |
292
- | `getRoleActions(roleId, query?)` | `GET /iam/permissions/role-actions/{roleId}` | Get role's actions |
293
-
294
- **Company -> Action (Whitelisting):**
295
-
296
- | Method | Endpoint | Description |
297
- |--------|----------|-------------|
298
- | `assignCompanyActions(data)` | `POST /iam/permissions/company-actions/assign` | Assign/remove company whitelist |
299
- | `getCompanyActions(companyId)` | `GET /iam/permissions/company-actions/{companyId}` | Get company's whitelisted actions |
300
-
301
- > **Note:** Read operations use GET (exception to the POST-only RPC convention). Write operations use POST.
302
-
303
- ---
304
-
305
- ### MyPermissionsApiService
306
-
307
- Fetches current user's permissions.
308
-
309
- **Method:**
310
-
311
- | Method | Endpoint | Description |
312
- |--------|----------|-------------|
313
- | `getMyPermissions(dto?)` | `POST /iam/permissions/my-permissions` | Get current user's frontend actions and cached endpoint count |
314
-
315
- ---
316
-
317
- ### ActionPermissionLogicService
318
-
319
- Manages smart prerequisite validation using AND/OR logic trees. Uses PrimeNG `ConfirmationService` and `MessageService` for dialogs.
320
-
321
- **Core Methods:**
322
-
323
- | Method | Description |
324
- |--------|-------------|
325
- | `handleCheck(action, selection, allActions, onUpdate, onCancel)` | Handle action selection with recursive prerequisite validation |
326
- | `handleUncheck(action, selection, allActions, onUpdate)` | Handle deselection with dependency detection |
327
- | `hasUnmetPrerequisites(action, selection, allActions)` | Check if action has unmet prerequisites |
328
- | `getActionsWithUnmetPrerequisites(selection, allActions)` | Get all selected actions with unmet prerequisites |
329
- | `showValidationErrorDialog(invalid, selection, allActions, onUpdate)` | Pre-save validation error dialog with auto-fix |
330
- | `getPrerequisiteTooltip(action, selection, allActions)` | Get tooltip text with prerequisite info |
331
-
332
- **Features:**
333
- - Recursive deep-scan for cascading prerequisites
334
- - Smart AND/OR optimization (selects minimum required actions)
335
- - Visual formatting with status indicators
336
- - Alternative suggestions for OR logic branches
337
- - Pre-save validation with auto-fix capability
338
-
339
- ---
340
-
341
- ## Components
342
-
343
- ### LogicBuilderComponent
344
-
345
- Visual AND/OR permission logic tree editor.
346
-
347
- | Property | Value |
348
- |----------|-------|
349
- | Selector | `lib-logic-builder` |
350
- | Standalone | Yes |
351
- | Change Detection | OnPush |
352
-
353
- **Inputs:**
354
-
355
- | Input | Type | Description |
356
- |-------|------|-------------|
357
- | `logic` | `ILogicNode \| null` | Current permission logic tree |
358
- | `actions` | `Array<{id, name}>` | Available actions for dropdowns |
359
-
360
- **Outputs:**
361
-
362
- | Output | Type | Description |
363
- |--------|------|-------------|
364
- | `logicChange` | `ILogicNode \| null` | Emitted when logic changes |
365
-
366
- **Features:** Recursive tree builder, AND/OR operator toggling, action selector dropdowns, node add/remove.
367
-
368
- ---
369
-
370
- ### UserActionSelectorComponent
371
-
372
- Assigns actions directly to users (DIRECT/FULL mode).
373
-
374
- | Property | Value |
375
- |----------|-------|
376
- | Selector | `flusys-user-action-selector` |
377
- | Standalone | Yes |
378
- | Change Detection | OnPush |
379
-
380
- **Dependencies:** `APP_CONFIG`, `LAYOUT_AUTH_STATE`, `USER_PERMISSION_PROVIDER`, `ActionApiService`, `ActionPermissionLogicService`, `PermissionApiService`, `MessageService`
381
-
382
- **Features:**
383
- - User dropdown (`lib-user-select` component from ng-shared)
384
- - Branch selector (conditional, loaded from USER_PERMISSION_PROVIDER)
385
- - Hierarchical TreeTable with checkboxes
386
- - Responsive table with `overflow-x-auto` container
387
- - Responsive columns (Code hidden on mobile, Description hidden until lg)
388
- - Action type tags (backend/frontend/both)
389
- - Prerequisite validation with tooltips
390
- - Change tracking with pending add/remove summary
391
- - Select/deselect all
392
- - Pre-save validation
393
-
394
- ---
395
-
396
- ### UserRoleSelectorComponent
397
-
398
- Assigns roles to users (RBAC/FULL mode).
399
-
400
- | Property | Value |
401
- |----------|-------|
402
- | Selector | `flusys-user-role-selector` |
403
- | Standalone | Yes |
404
- | Change Detection | OnPush |
405
-
406
- **Dependencies:** `APP_CONFIG`, `LAYOUT_AUTH_STATE`, `USER_PERMISSION_PROVIDER`, `RoleApiService`, `PermissionApiService`, `MessageService`
407
-
408
- **Features:**
409
- - User dropdown (`lib-user-select` component from ng-shared)
410
- - Branch selector (conditional)
411
- - DataTable with pagination and `overflow-x-auto` container
412
- - Responsive columns (Code hidden on mobile, Description hidden until md)
413
- - Change tracking with pending add/remove summary
414
- - Select/deselect all
415
- - No prerequisite validation (roles are simple assignments)
416
-
417
- ---
418
-
419
- ### RoleActionSelectorComponent
420
-
421
- Assigns actions to roles (RBAC/FULL mode).
422
-
423
- | Property | Value |
424
- |----------|-------|
425
- | Selector | `flusys-role-action-selector` |
426
- | Standalone | Yes |
427
- | Change Detection | OnPush |
428
-
429
- **Dependencies:** `RoleApiService`, `ActionApiService`, `PermissionApiService`, `MessageService`, `ActionPermissionLogicService`
430
-
431
- **Features:**
432
- - Role dropdown with filter
433
- - Hierarchical TreeTable with checkboxes and `overflow-x-auto` container
434
- - Responsive columns (Code hidden until md, Requirements hidden until lg)
435
- - Prerequisite validation with smart dependency handling
436
- - Request cancellation with AbortController
437
- - Pre-save validation with auto-fix dialog
438
- - Change tracking with pending add/remove summary
439
-
440
- ---
441
-
442
- ### CompanyActionSelectorComponent
443
-
444
- Manages company action whitelisting (when company feature enabled).
445
-
446
- | Property | Value |
447
- |----------|-------|
448
- | Selector | `flusys-company-action-selector` |
449
- | Standalone | Yes |
450
- | Change Detection | OnPush |
451
-
452
- **Dependencies:** `COMPANY_API_PROVIDER`, `ActionApiService`, `PermissionApiService`, `MessageService`, `ConfirmationService`, `ActionPermissionLogicService`
453
-
454
- **Features:**
455
- - Company dropdown with filter (loaded from COMPANY_API_PROVIDER)
456
- - Full action tree (not filtered by company whitelist)
457
- - Hierarchical TreeTable with `overflow-x-auto` container
458
- - Responsive columns (Code hidden until md, Description hidden until lg)
459
- - Prerequisite validation with tooltips
460
- - Backend prerequisite error handling with auto-fix
461
- - Request cancellation with AbortController
462
- - Change tracking with pending whitelist/remove summary
463
-
464
- ---
465
-
466
- ## Page Components
467
-
468
- > Page components are **not exported** from the public API. They are internal and accessed via `IAM_ROUTES` (lazy-loaded by the router).
469
-
470
- ### IamContainerComponent
471
-
472
- Tabbed navigation container for IAM pages.
473
-
474
- | Property | Value |
475
- |----------|-------|
476
- | Selector | `lib-iam-container` |
477
- | Tabs | Actions (always), Roles (RBAC/FULL), Permissions (always) |
478
- | Change Detection | OnPush |
479
-
480
- **Features:**
481
- - Responsive page title (`text-xl sm:text-2xl`)
482
- - Horizontally scrollable tabs on mobile (hidden scrollbar)
483
- - Uses `RouterLink` and `RouterLinkActive` for navigation
484
- - Tab visibility based on permission mode
485
-
486
- **Responsive Patterns:**
487
-
488
- ```html
489
- <!-- Page title -->
490
- <h1 class="text-xl sm:text-2xl font-bold m-0">Identity & Access Management</h1>
491
-
492
- <!-- Horizontally scrollable tabs (hidden scrollbar) -->
493
- <div class="scrollbar-hide flex gap-1 overflow-x-auto flex-nowrap">
494
- <!-- tabs -->
495
- </div>
496
- ```
497
-
498
- **Scrollbar hide CSS:**
499
- ```css
500
- .scrollbar-hide {
501
- -ms-overflow-style: none;
502
- scrollbar-width: none;
503
- &::-webkit-scrollbar { display: none; }
504
- }
505
- ```
506
-
507
- ---
508
-
509
- ### ActionListPageComponent
510
-
511
- Hierarchical action list with CRUD operations.
512
-
513
- | Property | Value |
514
- |----------|-------|
515
- | Selector | `lib-action-list-page` |
516
- | Features | TreeTable display, create/edit/delete, read-only indicators, company context awareness |
517
-
518
- **Responsive features:**
519
- - TreeTable with `overflow-x-auto -mx-4 sm:mx-0` container
520
- - Responsive columns: Code (hidden until md), Active (hidden until sm), Read Only (hidden until lg)
521
- - Responsive header with stacked layout on mobile
522
-
523
- Reloads actions when company context changes via effect.
524
-
525
- ---
526
-
527
- ### ActionFormPageComponent
528
-
529
- Action create/edit form with LogicBuilder integration.
530
-
531
- | Property | Value |
532
- |----------|-------|
533
- | Selector | `lib-action-form-page` |
534
- | Features | Create/edit mode, parent action selector, ActionType enum, permissionLogic editor, display order, active flag |
535
-
536
- **Form fields:** name (required), description, code, actionType, permissionLogic, parentId, serial, isActive, metadata.
537
-
538
- ---
539
-
540
- ### RoleListPageComponent
541
-
542
- Paginated role list with CRUD operations.
543
-
544
- | Property | Value |
545
- |----------|-------|
546
- | Selector | `lib-role-list-page` |
547
- | Features | Paginated table (10/25/50), create/edit/delete, company context filtering, read-only indicators |
548
-
549
- **Responsive features:**
550
- - Table with `overflow-x-auto -mx-4 sm:mx-0` container
551
- - Responsive columns: Description (hidden until md), Read Only (hidden until sm)
552
- - Conditional paginator (only shown when data exists)
43
+ All routes are protected by `permissionGuard` using constants from `ng-shared`.
553
44
 
554
- ---
555
-
556
- ### RoleFormPageComponent
557
-
558
- Role create/edit form.
559
-
560
- | Property | Value |
561
- |----------|-------|
562
- | Selector | `lib-role-form-page` |
563
- | Features | Create/edit mode, company auto-assignment on create, active flag, display order |
564
-
565
- **Form fields:** name (required), description, serial, isActive. Company assigned automatically from context.
566
-
567
- ---
568
-
569
- ### PermissionPageComponent
570
-
571
- Main permission management interface with tabbed layout.
572
-
573
- | Property | Value |
574
- |----------|-------|
575
- | Selector | `lib-permission-page` |
576
- | Features | Dynamic tabs based on permission mode and company feature |
577
-
578
- **Tabs:**
579
- - Role-Actions (RBAC/FULL) - `RoleActionSelectorComponent`
580
- - User-Roles (RBAC/FULL) - `UserRoleSelectorComponent`
581
- - User-Actions (DIRECT/FULL) - `UserActionSelectorComponent`
582
- - Company-Actions (if company feature enabled) - `CompanyActionSelectorComponent`
583
-
584
- ---
585
-
586
- ## Interfaces
45
+ ## 3. Load Permissions
587
46
 
588
- ### ActionType (Enum)
47
+ Call `PermissionStateService.loadPermissions()` after login (e.g., in `appInitGuard` or after session restore). It fetches the current user's effective permissions from `POST /iam/permissions/my-permissions` and populates `PermissionValidatorService` from `ng-shared`.
589
48
 
590
49
  ```typescript
591
- enum ActionType {
592
- BACKEND = 'backend', // API endpoint permissions (cached by PermissionGuard)
593
- FRONTEND = 'frontend', // UI feature permissions (returned in my-permissions)
594
- BOTH = 'both', // Both backend and frontend
595
- }
596
- ```
50
+ import { PermissionStateService } from '@flusys/ng-iam';
597
51
 
598
- ### IAction (extends IBaseEntity)
52
+ @Component({ ... })
53
+ export class AppShellComponent {
54
+ private readonly permState = inject(PermissionStateService);
599
55
 
600
- ```typescript
601
- interface IAction extends IBaseEntity {
602
- readOnly: boolean;
603
- name: string;
604
- description: string | null;
605
- code: string | null;
606
- actionType: ActionType;
607
- permissionLogic: ILogicNode | null; // AND/OR prerequisite rules
608
- parentId: string | null; // For hierarchy
609
- serial: number | null; // Display order
610
- isActive: boolean;
611
- metadata: Record<string, any> | null;
612
- }
613
- ```
614
-
615
- ### IActionTreeDto (extends IAction)
56
+ readonly permissions = this.permState.permissions; // Signal<IMyPermissionsResponseDto | null>
57
+ readonly isLoading = this.permState.isLoading; // Signal<boolean>
616
58
 
617
- ```typescript
618
- interface IActionTreeDto extends IAction {
619
- children: IActionTreeDto[];
620
- }
621
- ```
622
-
623
- ### ICreateActionDto / IUpdateActionDto
624
-
625
- ```typescript
626
- interface ICreateActionDto {
627
- name: string;
628
- description?: string;
629
- code?: string;
630
- actionType?: ActionType;
631
- permissionLogic?: ILogicNode;
632
- parentId?: string;
633
- serial?: number;
634
- isActive?: boolean;
635
- metadata?: Record<string, any>;
636
- }
637
-
638
- interface IUpdateActionDto extends Partial<ICreateActionDto> {
639
- id: string;
640
- }
641
- ```
642
-
643
- ### IRole (extends IBaseEntity)
644
-
645
- ```typescript
646
- interface IRole extends IBaseEntity {
647
- readOnly: boolean;
648
- name: string;
649
- description: string | null;
650
- companyId: string | null;
651
- isActive: boolean;
652
- serial: number | null;
653
- metadata: Record<string, any> | null;
654
- }
655
- ```
656
-
657
- ### ICreateRoleDto / IUpdateRoleDto / IRoleQueryDto
658
-
659
- ```typescript
660
- interface ICreateRoleDto {
661
- name: string;
662
- description?: string;
663
- companyId?: string;
664
- isActive?: boolean;
665
- serial?: number;
666
- metadata?: Record<string, any>;
667
- }
668
-
669
- interface IUpdateRoleDto extends Partial<ICreateRoleDto> {
670
- id: string;
671
- }
672
-
673
- interface IRoleQueryDto {
674
- companyId?: string;
675
- isActive?: boolean;
676
- }
677
- ```
678
-
679
- ### Permission Interfaces
680
-
681
- ```typescript
682
- type PermissionAction = 'add' | 'remove';
683
- type PermissionMode = 'rbac' | 'direct' | 'full';
684
-
685
- interface IPermissionItemDto {
686
- id: string;
687
- action: PermissionAction;
688
- }
689
-
690
- // Assignment DTOs
691
- interface IAssignUserActionsDto {
692
- userId: string;
693
- companyId?: string;
694
- branchId?: string; // null = company-wide
695
- items: IPermissionItemDto[];
696
- }
697
-
698
- interface IAssignCompanyActionsDto {
699
- companyId: string;
700
- items: IPermissionItemDto[];
701
- }
702
-
703
- interface IAssignRoleActionsDto {
704
- roleId: string;
705
- items: IPermissionItemDto[];
706
- }
707
-
708
- interface IAssignUserRolesDto {
709
- userId: string;
710
- companyId?: string;
711
- branchId?: string;
712
- items: IPermissionItemDto[];
713
- }
714
- ```
715
-
716
- ### Response DTOs
717
-
718
- ```typescript
719
- interface IUserActionResponseDto {
720
- id: string;
721
- userId: string;
722
- actionId: string;
723
- actionCode: string;
724
- actionName: string;
725
- branchId: string | null;
726
- createdAt: Date;
727
- }
728
-
729
- interface IRoleActionResponseDto {
730
- id: string;
731
- roleId: string;
732
- actionId: string;
733
- actionCode: string;
734
- actionName: string;
735
- createdAt: Date;
736
- }
737
-
738
- interface ICompanyActionResponseDto {
739
- id: string;
740
- companyId: string;
741
- actionId: string;
742
- actionCode: string;
743
- actionName: string;
744
- createdAt: Date;
745
- }
746
-
747
- interface IUserRoleResponseDto {
748
- id: string;
749
- userId: string;
750
- roleId: string;
751
- roleCode: string;
752
- roleName: string;
753
- branchId: string | null; // Always null - roles are NOT branch-scoped
754
- createdAt: Date;
755
- }
756
- ```
757
-
758
- ### My Permissions Response
759
-
760
- ```typescript
761
- interface IMyPermissionsResponseDto {
762
- frontendActions: Array<{ id: string; code: string; name: string; description: string }>;
763
- cachedEndpoints: number; // Endpoint count for backend PermissionGuard
764
- }
765
- ```
766
-
767
- ### Menu & Logic Interfaces
768
-
769
- ```typescript
770
- interface IMenuAction {
771
- id: string;
772
- code: string;
773
- name: string;
774
- route: string | null;
775
- icon: string | null;
776
- iconType: number | null;
777
- serial: number | null;
778
- parentId: string | null;
779
- children?: IMenuAction[];
780
- }
781
-
782
- // ILogicNode is imported from @flusys/ng-shared
783
- ```
784
-
785
- ### Prerequisite Interfaces
786
-
787
- ```typescript
788
- interface IPrerequisiteActionDto {
789
- actionId: string;
790
- actionCode: string;
791
- actionName: string;
792
- }
793
-
794
- interface IPrerequisiteValidationError {
795
- actionId: string;
796
- actionCode: string;
797
- actionName: string;
798
- requiredActions: IPrerequisiteActionDto[];
799
- }
800
-
801
- interface IPermissionOperationResultDto {
802
- success: boolean;
803
- added: number;
804
- removed: number;
805
- message: string;
806
- prerequisiteErrors?: IPrerequisiteValidationError[];
807
- }
808
- ```
809
-
810
- ### Simple Models
811
-
812
- ```typescript
813
- interface IUser {
814
- id: string;
815
- name: string;
816
- email: string;
817
- }
818
-
819
- interface IBranch {
820
- id: string;
821
- name: string;
822
- companyId: string;
823
- }
824
-
825
- interface ICompany {
826
- id: string;
827
- name: string;
828
- slug?: string;
829
- }
830
- ```
831
-
832
- ### Query DTOs
833
-
834
- ```typescript
835
- interface IGetUserActionsDto {
836
- branchId?: string;
837
- }
838
-
839
- interface IGetRoleActionsDto {}
840
-
841
- interface IGetUserRolesDto {
842
- branchId?: string; // Ignored by backend
843
- }
844
-
845
- interface IGetMyPermissionsDto {
846
- parentCodes?: string[];
847
- }
848
- ```
849
-
850
- ---
851
-
852
- ## Utils & Constants
853
-
854
- > Utils are **internal** (not exported from the public API). They are used by components and services within the package.
855
-
856
- ### permission-logic.util.ts
857
-
858
- | Function | Description |
859
- |----------|-------------|
860
- | `validateActionPrerequisites(action, selectedIds, allActions)` | Validate prerequisites respecting AND/OR. Returns `{valid, missingActions}` |
861
- | `extractRequiredActionIds(logic, allActions)` | Extract all action IDs from a logic tree |
862
- | `describePrerequisites(logic, allActions)` | Human-readable prerequisite description |
863
-
864
- ### tree-utils.ts
865
-
866
- | Function | Description |
867
- |----------|-------------|
868
- | `flattenTree<T>(tree)` | Flatten hierarchical tree into flat array |
869
- | `buildItemMap<T>(items)` | Build ID-to-item `Map` for O(1) lookup |
870
- | `buildTreeFromFlat<T>(flatList)` | Build tree from flat list using `parentId` |
871
- | `convertActionToTreeNode(actions)` | Convert `IActionTreeDto[]` to PrimeNG `TreeNode<IAction>[]` |
872
-
873
- ### Constants
874
-
875
- ```typescript
876
- export const MAX_DROPDOWN_ITEMS = 100; // Max items for dropdown lists (users, companies, roles, branches)
877
- ```
878
-
879
- Exported directly as `MAX_DROPDOWN_ITEMS` from the package.
880
-
881
- ---
882
-
883
- ## Backend Integration
884
-
885
- ### Required Endpoints
886
-
887
- **Action CRUD:**
888
-
889
- ```
890
- POST /iam/actions/tree # Get hierarchical action tree
891
- POST /iam/actions/tree-for-permission # Get actions filtered by company whitelist
892
- POST /iam/actions/get-all # List all actions (flat)
893
- POST /iam/actions/get/:id # Get action by ID
894
- POST /iam/actions/insert # Create action
895
- POST /iam/actions/update # Update action
896
- POST /iam/actions/delete # Delete action
897
- ```
898
-
899
- **Role CRUD:**
900
-
901
- ```
902
- POST /iam/roles/get-all # List roles (with pagination + company filter)
903
- POST /iam/roles/get/:id # Get role by ID
904
- POST /iam/roles/insert # Create role
905
- POST /iam/roles/update # Update role
906
- POST /iam/roles/delete # Delete role
907
- ```
908
-
909
- **Permission Assignments:**
910
-
911
- ```
912
- POST /iam/permissions/my-permissions # Current user's permissions
913
- POST /iam/permissions/user-actions/assign # Assign/remove user actions
914
- GET /iam/permissions/user-actions/:id # Get user's direct actions
915
- POST /iam/permissions/user-roles/assign # Assign/remove user roles
916
- GET /iam/permissions/user-roles/:id # Get user's roles
917
- POST /iam/permissions/role-actions/assign # Assign/remove role actions
918
- GET /iam/permissions/role-actions/:id # Get role's actions
919
- POST /iam/permissions/company-actions/assign # Assign/remove company whitelist
920
- GET /iam/permissions/company-actions/:id # Get company's whitelisted actions
921
- ```
922
-
923
- ### My Permissions Response Format
924
-
925
- ```json
926
- {
927
- "success": true,
928
- "data": {
929
- "frontendActions": [
930
- {
931
- "id": "uuid",
932
- "code": "user.create",
933
- "name": "Create User",
934
- "description": "Allows creating new users"
935
- }
936
- ],
937
- "cachedEndpoints": 42
59
+ loadOnInit() {
60
+ this.permState.loadPermissions().subscribe();
938
61
  }
939
- }
940
- ```
941
-
942
- ---
943
-
944
- ## Responsive Table Patterns
945
-
946
- All IAM tables and tree tables use consistent responsive patterns with Tailwind CSS.
947
-
948
- ### Table Container Pattern
949
-
950
- ```html
951
- <!-- Wrapper enables horizontal scroll on mobile, full-width on desktop -->
952
- <div class="overflow-x-auto -mx-4 sm:mx-0">
953
- <p-treeTable
954
- [value]="treeNodes()"
955
- dataKey="id"
956
- styleClass="p-treetable-sm"
957
- [tableStyle]="{ 'min-width': '50rem' }"
958
- >
959
- <!-- ... -->
960
- </p-treeTable>
961
- </div>
962
- ```
963
-
964
- **Key properties:**
965
- - `overflow-x-auto` - Enables horizontal scroll when content exceeds container
966
- - `-mx-4 sm:mx-0` - Edge-to-edge on mobile, normal margins on desktop
967
- - `[tableStyle]="{ 'min-width': '50rem' }"` - Minimum width ensures consistent layout
968
-
969
- ### Responsive Column Visibility
970
-
971
- ```html
972
- <ng-template #header>
973
- <tr>
974
- <th class="w-12"><!-- checkbox --></th>
975
- <th>Name</th> <!-- Always visible -->
976
- <th class="hidden sm:table-cell">Code</th> <!-- Hidden on xs -->
977
- <th>Type</th> <!-- Always visible -->
978
- <th class="hidden md:table-cell">Status</th> <!-- Hidden until md -->
979
- <th class="hidden lg:table-cell">Description</th> <!-- Hidden until lg -->
980
- </tr>
981
- </ng-template>
982
- ```
983
-
984
- **Breakpoint classes:**
985
- | Class | Visibility |
986
- |-------|------------|
987
- | (none) | Always visible |
988
- | `hidden sm:table-cell` | Visible at 640px+ |
989
- | `hidden md:table-cell` | Visible at 768px+ |
990
- | `hidden lg:table-cell` | Visible at 1024px+ |
991
-
992
- ### Header Layout Pattern
993
-
994
- ```html
995
- <div class="flex flex-col md:flex-row justify-between items-start md:items-center gap-3 mb-4">
996
- <div>
997
- <h5 class="m-0 mb-1">Section Title</h5>
998
- <p class="text-sm text-muted-color m-0">{{ count }} items available</p>
999
- </div>
1000
- <div class="flex flex-wrap gap-2">
1001
- <!-- Action buttons -->
1002
- </div>
1003
- </div>
1004
- ```
1005
-
1006
- ### Dark Mode Styling
1007
-
1008
- ```css
1009
- /* Light mode */
1010
- .validation-warning {
1011
- background-color: var(--p-yellow-50, #fefce8);
1012
- border-left: 4px solid var(--p-yellow-500, #eab308);
1013
- color: var(--p-yellow-700, #a16207);
1014
- }
1015
-
1016
- /* Dark mode */
1017
- :host-context(.p-dark) .validation-warning {
1018
- background-color: rgba(234, 179, 8, 0.1);
1019
- color: var(--p-yellow-400, #facc15);
1020
- }
1021
- ```
1022
-
1023
- **Color classes (Tailwind):**
1024
- | Purpose | Class |
1025
- |---------|-------|
1026
- | Muted text | `text-muted-color` |
1027
- | Success | `text-green-500` |
1028
- | Danger | `text-red-500` |
1029
- | Warning | `text-orange-500` |
1030
- | Primary | `text-primary` |
1031
-
1032
- ---
1033
62
 
1034
- ## Best Practices
1035
-
1036
- ### Permission Loading
1037
-
1038
- Load permissions in a guard to block navigation until ready:
1039
-
1040
- ```typescript
1041
- // appInitGuard
1042
- if (!permissionState.isLoaded()) {
1043
- try {
1044
- await firstValueFrom(permissionState.loadPermissions());
1045
- } catch (error) {
1046
- console.error('Failed to load permissions:', error);
1047
- // Continue with graceful degradation - don't block the app
63
+ isLoaded() {
64
+ return this.permState.isLoaded(); // true when permissions are set in PermissionValidatorService
1048
65
  }
1049
66
  }
1050
- return true;
1051
67
  ```
1052
68
 
1053
- - Cache permissions in memory (PermissionStateService handles this)
1054
- - Clear permissions on logout
1055
- - Never store permissions in localStorage (security risk)
1056
- - Never load permissions in component `ngOnInit` (causes race conditions)
1057
-
1058
- ### Permission Checks
1059
-
1060
- ```typescript
1061
- // Single action
1062
- validator.hasAction('user.create');
69
+ ## 4. Check Permissions in Templates
1063
70
 
1064
- // ALL required
1065
- validator.hasAllActions(['user.create', 'user.update']);
1066
-
1067
- // ANY required
1068
- validator.hasAnyAction(['user.view', 'user.list']);
1069
- ```
1070
-
1071
- Cache check results in computed signals rather than calling in templates or tight loops.
1072
-
1073
- ### Menu Filtering
1074
-
1075
- ```typescript
1076
- // Set menu with requiredAction or logicNode
1077
- this.layoutService.setMenu(menuItems);
1078
-
1079
- // Set permission checker (layout handles filtering automatically)
1080
- this.layoutService.setPermissionChecker((actionCode) =>
1081
- validator.hasAction(actionCode)
1082
- );
1083
- ```
1084
-
1085
- Do not filter menus manually - let layout handle it.
1086
-
1087
- ### Action Naming
1088
-
1089
- Format: `module.operation` or `module.entity.operation`
1090
-
1091
- ```typescript
1092
- // Good
1093
- 'user.create'
1094
- 'user.update'
1095
- 'report.export'
1096
- 'admin.system.config'
71
+ Once `provideIamProviders()` is registered and `loadPermissions()` has been called, permission directives from `ng-shared` work automatically:
1097
72
 
1098
- // Bad
1099
- 'create' // Too generic
1100
- 'userCreate' // Not dot notation
1101
- 'users' // Entity name, not action
1102
- ```
1103
-
1104
- ### LogicNode Pattern
1105
-
1106
- Complex permission rules using AND/OR operators:
1107
-
1108
- ```typescript
1109
- // User needs 'user.create' AND ('user.update' OR 'user.delete')
1110
- const logic: ILogicNode = {
1111
- type: 'AND',
1112
- children: [
1113
- { type: 'ACTION', value: 'user.create' },
1114
- {
1115
- type: 'OR',
1116
- children: [
1117
- { type: 'ACTION', value: 'user.update' },
1118
- { type: 'ACTION', value: 'user.delete' },
1119
- ],
1120
- },
1121
- ],
1122
- };
1123
- ```
1124
-
1125
- ---
1126
-
1127
- ## Common Issues
1128
-
1129
- | Problem | Cause | Solution |
1130
- |---------|-------|----------|
1131
- | `permissionState.isLoaded()` returns false | IAM not enabled or endpoint failing | Check `services.iam.enabled: true` in environment, verify `POST /iam/permissions/my-permissions` works |
1132
- | Empty menu after login | Permissions not loaded or no matching actions | Check `permissionState.isLoaded()`, verify user has frontend actions, check `requiredAction` matches action codes |
1133
- | Menu items not filtered | Permission checker not set | Call `layoutService.setPermissionChecker()` after loading permissions |
1134
- | 401 on IAM endpoints with external auth | Auth token not forwarded | Ensure your auth interceptor adds `Authorization` header to IAM requests |
1135
- | Provider not configured error | Missing DI registration | Add required providers to `app.config.ts` (USER_PROVIDER, LAYOUT_AUTH_STATE) |
1136
- | Empty user/company dropdowns | API response format mismatch | Ensure adapters return `IListResponse<T>` format with `data` array |
1137
- | TreeTable has excessive whitespace | Using `scrollHeight="flex"` without fixed-height parent | Remove `scrollHeight="flex"` and `[scrollable]="true"`, use `overflow-x-auto` container instead |
1138
- | Table not horizontally scrollable on mobile | Missing responsive wrapper | Add `<div class="overflow-x-auto -mx-4 sm:mx-0">` around table |
1139
- | Columns visible on mobile causing overflow | Missing responsive column classes | Add `hidden sm:table-cell` or `hidden md:table-cell` to non-essential columns |
1140
-
1141
- ---
1142
-
1143
- ## Configuration
1144
-
1145
- ### Frontend Environment
1146
-
1147
- ```typescript
1148
- // environment.ts
1149
- services: {
1150
- iam: { baseUrl: 'http://localhost:2002/iam', enabled: true },
1151
- }
1152
- ```
1153
-
1154
- ### Backend Environment
1155
-
1156
- ```bash
1157
- # .env
1158
- IAM_PERMISSION_MODE=FULL # DIRECT, RBAC, or FULL
1159
- ```
1160
-
1161
- | Scenario | services.iam.enabled | Result |
1162
- |----------|---------------------|--------|
1163
- | Full IAM | `true` | Permission-based menu |
1164
- | IAM Disabled | `false` | Static menu (all items visible) |
1165
- | Service Down | `true` (API fails) | Graceful fallback to static menu |
1166
-
1167
- ---
1168
-
1169
- ## Provider Functions
1170
-
1171
- ### provideIamProviders()
1172
-
1173
- Registers provider adapters that enable other packages (ng-auth) to access IAM functionality without direct dependencies.
1174
-
1175
- ```typescript
1176
- import { provideIamProviders } from '@flusys/ng-iam';
1177
-
1178
- // app.config.ts
1179
- export const appConfig: ApplicationConfig = {
1180
- providers: [
1181
- ...provideIamProviders(), // Register IAM providers
1182
- ],
1183
- };
1184
- ```
1185
-
1186
- **Registers:**
1187
-
1188
- | Token | Adapter | Purpose |
1189
- |-------|---------|---------|
1190
- | `PROFILE_PERMISSION_PROVIDER` | `ProfilePermissionProviderAdapter` | User roles/actions for profile page |
1191
-
1192
- ### ProfilePermissionProviderAdapter
1193
-
1194
- Implements `IProfilePermissionProvider` from ng-shared. Enables ng-auth's profile page to display user permissions.
73
+ ```html
74
+ <!-- show element if user has the action code -->
75
+ <button *hasPermission="'product.update'">Edit</button>
1195
76
 
1196
- ```typescript
1197
- interface IProfilePermissionProvider {
1198
- getUserRoles(userId: string, branchId?: string): Observable<ISingleResponse<IProfileRoleInfo[]>>;
1199
- getUserActions(userId: string, branchId?: string): Observable<ISingleResponse<IProfileActionInfo[]>>;
1200
- }
77
+ <!-- show element if user has any of these action codes -->
78
+ <div *hasPermission="['admin.manage', 'product.delete']">Admin area</div>
1201
79
  ```
1202
80
 
1203
- **Usage in ng-auth profile page:**
81
+ Use `permissionGuard` from `ng-shared` to protect routes:
1204
82
 
1205
83
  ```typescript
1206
- // Optional injection - graceful degradation when IAM disabled
1207
- private readonly permissionProvider = inject(PROFILE_PERMISSION_PROVIDER, { optional: true });
84
+ import { permissionGuard } from '@flusys/ng-shared';
1208
85
 
1209
- // Load user permissions
1210
- if (this.permissionProvider) {
1211
- this.permissionProvider.getUserRoles(userId, branchId).subscribe(response => {
1212
- this.roles.set(response.data ?? []);
1213
- });
86
+ {
87
+ path: 'admin',
88
+ canActivate: [permissionGuard('admin.manage')],
89
+ loadComponent: () => import('./admin.component'),
1214
90
  }
1215
91
  ```
1216
92
 
1217
- ---
1218
-
1219
- ## See Also
1220
-
1221
- - **[CORE-GUIDE.md](./CORE-GUIDE.md)** - Configuration system (APP_CONFIG)
1222
- - **[AUTH-GUIDE.md](./AUTH-GUIDE.md)** - Authentication and provider adapters
1223
- - **[SHARED-GUIDE.md](./SHARED-GUIDE.md)** - Provider interface definitions
1224
- - **[LAYOUT-GUIDE.md](./LAYOUT-GUIDE.md)** - Menu system and filtering
1225
-
1226
- ---
93
+ ## License
1227
94
 
1228
- **Last Updated:** 2026-02-23
1229
- **Version:** 1.1.0
1230
- **Angular Version:** 21
95
+ MIT © FLUSYS