@flusys/ng-iam 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 +232 -853
- package/fesm2022/{flusys-ng-iam-action-form-page.component-CVN8sV-c.mjs → flusys-ng-iam-action-form-page.component-eXpZNJ_H.mjs} +5 -5
- package/fesm2022/{flusys-ng-iam-action-form-page.component-CVN8sV-c.mjs.map → flusys-ng-iam-action-form-page.component-eXpZNJ_H.mjs.map} +1 -1
- package/fesm2022/{flusys-ng-iam-action-list-page.component-CQ6RazN0.mjs → flusys-ng-iam-action-list-page.component-BtJlGcTj.mjs} +5 -5
- package/fesm2022/{flusys-ng-iam-action-list-page.component-CQ6RazN0.mjs.map → flusys-ng-iam-action-list-page.component-BtJlGcTj.mjs.map} +1 -1
- package/fesm2022/{flusys-ng-iam-flusys-ng-iam-DrGHlTiz.mjs → flusys-ng-iam-flusys-ng-iam-CJAQT60K.mjs} +54 -56
- package/fesm2022/flusys-ng-iam-flusys-ng-iam-CJAQT60K.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-iam-container.component-BToYxEej.mjs → flusys-ng-iam-iam-container.component-UYJjqYV9.mjs} +4 -4
- package/fesm2022/{flusys-ng-iam-iam-container.component-BToYxEej.mjs.map → flusys-ng-iam-iam-container.component-UYJjqYV9.mjs.map} +1 -1
- package/fesm2022/{flusys-ng-iam-permission-page.component-BS7xXmsn.mjs → flusys-ng-iam-permission-page.component-DcgT7L3_.mjs} +5 -5
- package/fesm2022/{flusys-ng-iam-permission-page.component-BS7xXmsn.mjs.map → flusys-ng-iam-permission-page.component-DcgT7L3_.mjs.map} +1 -1
- package/fesm2022/{flusys-ng-iam-role-form-page.component-BjPwXkip.mjs → flusys-ng-iam-role-form-page.component-D_AAEay2.mjs} +5 -5
- package/fesm2022/{flusys-ng-iam-role-form-page.component-BjPwXkip.mjs.map → flusys-ng-iam-role-form-page.component-D_AAEay2.mjs.map} +1 -1
- package/fesm2022/{flusys-ng-iam-role-list-page.component-Cz-jk-R_.mjs → flusys-ng-iam-role-list-page.component-D4J1by6Q.mjs} +5 -5
- package/fesm2022/{flusys-ng-iam-role-list-page.component-Cz-jk-R_.mjs.map → flusys-ng-iam-role-list-page.component-D4J1by6Q.mjs.map} +1 -1
- package/fesm2022/flusys-ng-iam.mjs +1 -1
- package/package.json +11 -11
- package/types/flusys-ng-iam.d.ts +0 -1
- package/fesm2022/flusys-ng-iam-flusys-ng-iam-DrGHlTiz.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -4,37 +4,15 @@
|
|
|
4
4
|
|
|
5
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
6
|
|
|
7
|
-
**Key Principle:** ng-iam is **100% independent** from ng-auth through the **Provider Interface Pattern**.
|
|
8
|
-
|
|
9
|
-
## Package Information
|
|
7
|
+
**Key Principle:** ng-iam is **100% independent** from ng-auth through the **Provider Interface Pattern**.
|
|
10
8
|
|
|
11
9
|
| Item | Value |
|
|
12
10
|
|------|-------|
|
|
13
11
|
| Package | `@flusys/ng-iam` |
|
|
14
|
-
| Version |
|
|
12
|
+
| Version | 3.0.0 |
|
|
15
13
|
| Dependencies | ng-core, ng-shared, ng-layout (IMenuItem only) |
|
|
16
|
-
| NO dependency on | ng-auth (fully independent via Provider Pattern) |
|
|
17
14
|
| CSS Framework | Tailwind CSS (not PrimeFlex) |
|
|
18
15
|
| 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
16
|
|
|
39
17
|
---
|
|
40
18
|
|
|
@@ -42,131 +20,74 @@
|
|
|
42
20
|
|
|
43
21
|
```
|
|
44
22
|
ng-shared (defines interfaces + tokens)
|
|
45
|
-
|
|
23
|
+
↓
|
|
46
24
|
Your Auth System (implements interfaces with adapters)
|
|
47
|
-
|
|
25
|
+
↓
|
|
48
26
|
ng-iam (consumes interfaces via DI)
|
|
49
27
|
```
|
|
50
28
|
|
|
51
|
-
ng-iam resolves all auth-related data at runtime via DI tokens defined in ng-shared.
|
|
29
|
+
ng-iam resolves all auth-related data at runtime via DI tokens defined in ng-shared.
|
|
52
30
|
|
|
53
31
|
---
|
|
54
32
|
|
|
55
33
|
## Provider Interfaces
|
|
56
34
|
|
|
57
|
-
ng-iam requires these provider interfaces:
|
|
58
|
-
|
|
59
35
|
| Token | Source | Purpose | Required? |
|
|
60
36
|
|-------|--------|---------|-----------|
|
|
61
|
-
| `USER_PROVIDER` |
|
|
62
|
-
| `LAYOUT_AUTH_STATE` |
|
|
63
|
-
| `COMPANY_API_PROVIDER` |
|
|
64
|
-
| `USER_PERMISSION_PROVIDER` |
|
|
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`:
|
|
37
|
+
| `USER_PROVIDER` | ng-shared | User list for role/action assignment | Yes |
|
|
38
|
+
| `LAYOUT_AUTH_STATE` | ng-layout | Current company/branch context | Yes (if company enabled) |
|
|
39
|
+
| `COMPANY_API_PROVIDER` | ng-shared | Company list for whitelisting | Optional |
|
|
40
|
+
| `USER_PERMISSION_PROVIDER` | ng-shared | User branch permissions | Optional |
|
|
89
41
|
|
|
90
42
|
```typescript
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const response = await
|
|
94
|
-
.getUserBranchPermissions(userId)
|
|
95
|
-
.toPromise();
|
|
43
|
+
// Provider usage
|
|
44
|
+
const companyId = inject(LAYOUT_AUTH_STATE).currentCompanyInfo()?.id;
|
|
45
|
+
const response = await inject(USER_PERMISSION_PROVIDER).getUserBranchPermissions(userId).toPromise();
|
|
96
46
|
```
|
|
97
47
|
|
|
98
48
|
---
|
|
99
49
|
|
|
100
50
|
## Integration Options
|
|
101
51
|
|
|
102
|
-
###
|
|
103
|
-
|
|
104
|
-
ng-auth provides pre-built adapters for all required interfaces.
|
|
52
|
+
### With FLUSYS ng-auth (Recommended)
|
|
105
53
|
|
|
106
54
|
```typescript
|
|
107
|
-
// app.config.ts
|
|
108
55
|
import { provideAuthProviders } from '@flusys/ng-auth';
|
|
109
|
-
|
|
110
56
|
export const appConfig: ApplicationConfig = {
|
|
111
|
-
providers: [
|
|
112
|
-
provideAuthProviders(), // Registers all adapters
|
|
113
|
-
],
|
|
57
|
+
providers: [provideAuthProviders()], // Registers all adapters
|
|
114
58
|
};
|
|
115
59
|
```
|
|
116
60
|
|
|
117
|
-
|
|
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.
|
|
61
|
+
### With Custom Auth System
|
|
129
62
|
|
|
130
63
|
```typescript
|
|
131
|
-
// Minimum configuration (basic IAM)
|
|
132
64
|
providers: [
|
|
133
65
|
{ provide: USER_PROVIDER, useClass: YourUserProvider },
|
|
134
66
|
{ 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 },
|
|
67
|
+
{ provide: COMPANY_API_PROVIDER, useClass: YourCompanyApiProvider }, // Optional
|
|
68
|
+
{ provide: USER_PERMISSION_PROVIDER, useClass: YourUserPermissionProvider }, // Optional
|
|
143
69
|
]
|
|
144
70
|
```
|
|
145
71
|
|
|
146
|
-
**Feature matrix:**
|
|
147
|
-
|
|
148
72
|
| Feature | Providers Needed |
|
|
149
73
|
|---------|-----------------|
|
|
150
74
|
| Basic role assignments | USER_PROVIDER only |
|
|
151
75
|
| Company-scoped permissions | + LAYOUT_AUTH_STATE, COMPANY_API_PROVIDER |
|
|
152
76
|
| 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
77
|
|
|
157
78
|
---
|
|
158
79
|
|
|
159
80
|
## Permission Modes
|
|
160
81
|
|
|
161
|
-
|
|
82
|
+
Configured via `APP_CONFIG.iam.permissionMode`:
|
|
162
83
|
|
|
163
84
|
| Mode | Structure | Tabs Available | Use Case |
|
|
164
85
|
|------|-----------|---------------|----------|
|
|
165
|
-
| **DIRECT** | User -> Actions | User-Actions | Small apps
|
|
166
|
-
| **RBAC** | User -> Role -> Actions | Role-Actions, User-Roles | Standard apps
|
|
167
|
-
| **FULL** | Both combined |
|
|
86
|
+
| **DIRECT** | User -> Actions | User-Actions | Small apps |
|
|
87
|
+
| **RBAC** | User -> Role -> Actions | Role-Actions, User-Roles | Standard apps |
|
|
88
|
+
| **FULL** | Both combined | All tabs | Maximum flexibility |
|
|
168
89
|
|
|
169
|
-
When company feature is enabled, a **Company-Actions** tab is added for whitelisting
|
|
90
|
+
When company feature is enabled, a **Company-Actions** tab is added for whitelisting.
|
|
170
91
|
|
|
171
92
|
---
|
|
172
93
|
|
|
@@ -175,26 +96,21 @@ When company feature is enabled, a **Company-Actions** tab is added for whitelis
|
|
|
175
96
|
**Export:** `IAM_ROUTES` from `@flusys/ng-iam`
|
|
176
97
|
|
|
177
98
|
```
|
|
178
|
-
/iam
|
|
179
|
-
/actions
|
|
180
|
-
/actions/new
|
|
181
|
-
/actions/:id
|
|
182
|
-
/roles
|
|
183
|
-
/roles/new
|
|
184
|
-
/roles/:id
|
|
185
|
-
/permissions
|
|
186
|
-
(default redirect -> /actions)
|
|
99
|
+
/iam → IamContainerComponent (tabbed navigation)
|
|
100
|
+
/actions → ActionListPageComponent (hierarchical tree)
|
|
101
|
+
/actions/new → ActionFormPageComponent (create)
|
|
102
|
+
/actions/:id → ActionFormPageComponent (edit)
|
|
103
|
+
/roles → RoleListPageComponent (paginated table)
|
|
104
|
+
/roles/new → RoleFormPageComponent (create)
|
|
105
|
+
/roles/:id → RoleFormPageComponent (edit)
|
|
106
|
+
/permissions → PermissionPageComponent (tabbed assignment UI)
|
|
187
107
|
```
|
|
188
108
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
export const routes: Routes = [
|
|
195
|
-
{ path: 'iam', children: IAM_ROUTES },
|
|
196
|
-
];
|
|
197
|
-
```
|
|
109
|
+
| Route | Guard | Permission |
|
|
110
|
+
|-------|-------|------------|
|
|
111
|
+
| `/actions` | permissionGuard | `ACTION_PERMISSIONS.READ` |
|
|
112
|
+
| `/roles` | permissionGuard | `ROLE_PERMISSIONS.READ` |
|
|
113
|
+
| `/permissions` | anyPermissionGuard | Multiple READ permissions |
|
|
198
114
|
|
|
199
115
|
---
|
|
200
116
|
|
|
@@ -202,139 +118,84 @@ export const routes: Routes = [
|
|
|
202
118
|
|
|
203
119
|
### PermissionStateService
|
|
204
120
|
|
|
205
|
-
|
|
121
|
+
**File:** `services/permission-state.service.ts` | Provided at root level
|
|
206
122
|
|
|
207
|
-
|
|
123
|
+
Manages current user's permissions with signal-based state.
|
|
208
124
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
| Signal | Type | Description |
|
|
212
|
-
|--------|------|-------------|
|
|
125
|
+
| API | Type | Description |
|
|
126
|
+
|-----|------|-------------|
|
|
213
127
|
| `permissions` | `Signal<IMyPermissionsResponseDto \| null>` | Current user permissions (readonly) |
|
|
214
128
|
| `isLoading` | `Signal<boolean>` | Loading state (readonly) |
|
|
129
|
+
| `loadPermissions(dto?)` | `Observable<void>` | Load from `/iam/permissions/my-permissions` |
|
|
130
|
+
| `isLoaded()` | `boolean` | Check if permissions loaded |
|
|
215
131
|
|
|
216
|
-
|
|
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).
|
|
132
|
+
Permission checking delegated to `PermissionValidatorService` from ng-shared.
|
|
224
133
|
|
|
225
134
|
```typescript
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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']);
|
|
135
|
+
await firstValueFrom(permissionState.loadPermissions()); // Load in guard
|
|
136
|
+
validator.hasAction('user.create'); // Check permission
|
|
137
|
+
validator.hasAllActions(['user.create', 'user.update']); // Check multiple
|
|
236
138
|
```
|
|
237
139
|
|
|
238
140
|
---
|
|
239
141
|
|
|
240
142
|
### ActionApiService
|
|
241
143
|
|
|
242
|
-
Extends `ApiResourceService<IUpdateActionDto, IAction>`
|
|
144
|
+
**File:** `services/action-api.service.ts` | Extends `ApiResourceService<IUpdateActionDto, IAction>`
|
|
243
145
|
|
|
244
146
|
**Resource:** `iam/actions`
|
|
245
147
|
|
|
246
|
-
**Additional Methods:**
|
|
247
|
-
|
|
248
148
|
| Method | Endpoint | Description |
|
|
249
149
|
|--------|----------|-------------|
|
|
250
|
-
| `getTree(search?, isActive?, withDeleted?)` | `POST /iam/actions/tree` |
|
|
251
|
-
| `getActionsForPermission()` | `POST /iam/actions/tree-for-permission` |
|
|
150
|
+
| `getTree(search?, isActive?, withDeleted?)` | `POST /iam/actions/tree` | Hierarchical action tree |
|
|
151
|
+
| `getActionsForPermission()` | `POST /iam/actions/tree-for-permission` | Actions filtered by company whitelist |
|
|
252
152
|
|
|
253
|
-
Inherits standard CRUD
|
|
153
|
+
Inherits standard CRUD: `getAll`, `getById`, `insert`, `update`, `delete`.
|
|
254
154
|
|
|
255
155
|
---
|
|
256
156
|
|
|
257
157
|
### RoleApiService
|
|
258
158
|
|
|
259
|
-
Extends `ApiResourceService<IUpdateRoleDto, IRole>`
|
|
260
|
-
|
|
261
|
-
**Resource:** `iam/roles`
|
|
159
|
+
**File:** `services/role-api.service.ts` | Extends `ApiResourceService<IUpdateRoleDto, IRole>`
|
|
262
160
|
|
|
263
|
-
|
|
161
|
+
**Resource:** `iam/roles` | Only available in RBAC/FULL mode.
|
|
264
162
|
|
|
265
163
|
---
|
|
266
164
|
|
|
267
165
|
### PermissionApiService
|
|
268
166
|
|
|
269
|
-
|
|
167
|
+
**File:** `services/permission-api.service.ts` | Extends `BaseApiService`
|
|
270
168
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
**User -> Action (Direct Permissions):**
|
|
169
|
+
All endpoints use POST (POST-only RPC convention).
|
|
274
170
|
|
|
275
171
|
| Method | Endpoint | Description |
|
|
276
172
|
|--------|----------|-------------|
|
|
277
173
|
| `assignUserActions(data)` | `POST /iam/permissions/user-actions/assign` | Assign/remove actions for user |
|
|
278
|
-
| `getUserActions(userId, query?)` | `
|
|
279
|
-
|
|
280
|
-
**User -> Role:**
|
|
281
|
-
|
|
282
|
-
| Method | Endpoint | Description |
|
|
283
|
-
|--------|----------|-------------|
|
|
174
|
+
| `getUserActions(userId, query?)` | `POST /iam/permissions/get-user-actions` | Get user's direct actions |
|
|
284
175
|
| `assignUserRoles(data)` | `POST /iam/permissions/user-roles/assign` | Assign/remove roles for user |
|
|
285
|
-
| `getUserRoles(userId, query?)` | `
|
|
286
|
-
|
|
287
|
-
**Role -> Action:**
|
|
288
|
-
|
|
289
|
-
| Method | Endpoint | Description |
|
|
290
|
-
|--------|----------|-------------|
|
|
176
|
+
| `getUserRoles(userId, query?)` | `POST /iam/permissions/get-user-roles` | Get user's roles |
|
|
291
177
|
| `assignRoleActions(data)` | `POST /iam/permissions/role-actions/assign` | Assign/remove actions for role |
|
|
292
|
-
| `getRoleActions(roleId, query?)` | `
|
|
293
|
-
|
|
294
|
-
**Company -> Action (Whitelisting):**
|
|
295
|
-
|
|
296
|
-
| Method | Endpoint | Description |
|
|
297
|
-
|--------|----------|-------------|
|
|
178
|
+
| `getRoleActions(roleId, query?)` | `POST /iam/permissions/get-role-actions` | Get role's actions |
|
|
298
179
|
| `assignCompanyActions(data)` | `POST /iam/permissions/company-actions/assign` | Assign/remove company whitelist |
|
|
299
|
-
| `getCompanyActions(companyId)` | `
|
|
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 |
|
|
180
|
+
| `getCompanyActions(companyId)` | `POST /iam/permissions/get-company-actions` | Get company's whitelisted actions |
|
|
314
181
|
|
|
315
182
|
---
|
|
316
183
|
|
|
317
184
|
### ActionPermissionLogicService
|
|
318
185
|
|
|
319
|
-
|
|
186
|
+
**File:** `services/action-permission-logic.service.ts`
|
|
320
187
|
|
|
321
|
-
|
|
188
|
+
Manages smart prerequisite validation using AND/OR logic trees.
|
|
322
189
|
|
|
323
190
|
| Method | Description |
|
|
324
191
|
|--------|-------------|
|
|
325
|
-
| `handleCheck(action, selection, allActions, onUpdate, onCancel)` | Handle
|
|
192
|
+
| `handleCheck(action, selection, allActions, onUpdate, onCancel)` | Handle selection with prerequisite validation |
|
|
326
193
|
| `handleUncheck(action, selection, allActions, onUpdate)` | Handle deselection with dependency detection |
|
|
327
194
|
| `hasUnmetPrerequisites(action, selection, allActions)` | Check if action has unmet prerequisites |
|
|
328
|
-
| `
|
|
329
|
-
| `showValidationErrorDialog(invalid, selection, allActions, onUpdate)` | Pre-save validation error dialog with auto-fix |
|
|
195
|
+
| `showValidationErrorDialog(invalid, selection, allActions, onUpdate)` | Pre-save validation with auto-fix |
|
|
330
196
|
| `getPrerequisiteTooltip(action, selection, allActions)` | Get tooltip text with prerequisite info |
|
|
331
197
|
|
|
332
|
-
|
|
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
|
|
198
|
+
Features: Recursive deep-scan, smart AND/OR optimization, alternative suggestions, pre-save validation.
|
|
338
199
|
|
|
339
200
|
---
|
|
340
201
|
|
|
@@ -342,257 +203,99 @@ Manages smart prerequisite validation using AND/OR logic trees. Uses PrimeNG `Co
|
|
|
342
203
|
|
|
343
204
|
### LogicBuilderComponent
|
|
344
205
|
|
|
345
|
-
Visual AND/OR permission logic tree editor.
|
|
206
|
+
**Selector:** `lib-logic-builder` | Visual AND/OR permission logic tree editor.
|
|
346
207
|
|
|
347
|
-
|
|
|
348
|
-
|
|
349
|
-
|
|
|
350
|
-
|
|
|
351
|
-
|
|
|
208
|
+
| Input/Output | Type | Description |
|
|
209
|
+
|--------------|------|-------------|
|
|
210
|
+
| `logic` (input) | `ILogicNode \| null` | Current permission logic tree |
|
|
211
|
+
| `actions` (input) | `Array<{id, name}>` | Available actions for dropdowns |
|
|
212
|
+
| `logicChange` (output) | `ILogicNode \| null` | Emitted when logic changes |
|
|
352
213
|
|
|
353
|
-
|
|
214
|
+
---
|
|
354
215
|
|
|
355
|
-
|
|
356
|
-
|-------|------|-------------|
|
|
357
|
-
| `logic` | `ILogicNode \| null` | Current permission logic tree |
|
|
358
|
-
| `actions` | `Array<{id, name}>` | Available actions for dropdowns |
|
|
216
|
+
### Selector Components (Common Pattern)
|
|
359
217
|
|
|
360
|
-
|
|
218
|
+
All selector components share a common pattern with these signals:
|
|
361
219
|
|
|
362
|
-
|
|
|
363
|
-
|
|
364
|
-
| `
|
|
220
|
+
| Signal | Description |
|
|
221
|
+
|--------|-------------|
|
|
222
|
+
| `selectedXxxId` | Selected entity ID (user/role/company) |
|
|
223
|
+
| `treeNodes` / `items` | Available items (TreeNode[] or flat array) |
|
|
224
|
+
| `flatActions` | Flattened action list (for action selectors) |
|
|
225
|
+
| `selectedIds` | Currently selected IDs (Set) |
|
|
226
|
+
| `originalSelectedIds` | Original state for change tracking |
|
|
227
|
+
| `isLoading` / `isSaving` | Loading/saving states |
|
|
228
|
+
| `pendingAdd` / `pendingRemove` | Computed: items to add/remove |
|
|
229
|
+
| `hasChanges` | Computed: whether there are pending changes |
|
|
365
230
|
|
|
366
|
-
|
|
231
|
+
Common features: AbortController for request cancellation, responsive tables with `overflow-x-auto`, change tracking, select/deselect all.
|
|
367
232
|
|
|
368
233
|
---
|
|
369
234
|
|
|
370
235
|
### UserActionSelectorComponent
|
|
371
236
|
|
|
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`
|
|
237
|
+
**Selector:** `flusys-user-action-selector` | Assigns actions directly to users (DIRECT/FULL mode).
|
|
381
238
|
|
|
382
|
-
|
|
383
|
-
-
|
|
384
|
-
- Branch selector (conditional, loaded from USER_PERMISSION_PROVIDER)
|
|
239
|
+
- User dropdown (`lib-user-select` from ng-shared)
|
|
240
|
+
- Branch selector (conditional, from USER_PERMISSION_PROVIDER)
|
|
385
241
|
- 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
242
|
- Prerequisite validation with tooltips
|
|
390
|
-
- Change tracking with pending add/remove summary
|
|
391
|
-
- Select/deselect all
|
|
392
|
-
- Pre-save validation
|
|
393
243
|
|
|
394
244
|
---
|
|
395
245
|
|
|
396
246
|
### UserRoleSelectorComponent
|
|
397
247
|
|
|
398
|
-
Assigns roles to users (RBAC/FULL mode).
|
|
248
|
+
**Selector:** `flusys-user-role-selector` | Assigns roles to users (RBAC/FULL mode).
|
|
399
249
|
|
|
400
|
-
|
|
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
|
|
250
|
+
- User dropdown with branch selector
|
|
251
|
+
- DataTable with pagination
|
|
415
252
|
- No prerequisite validation (roles are simple assignments)
|
|
416
253
|
|
|
417
254
|
---
|
|
418
255
|
|
|
419
256
|
### RoleActionSelectorComponent
|
|
420
257
|
|
|
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 |
|
|
258
|
+
**Selector:** `flusys-role-action-selector` | Assigns actions to roles (RBAC/FULL mode).
|
|
428
259
|
|
|
429
|
-
**Dependencies:** `RoleApiService`, `ActionApiService`, `PermissionApiService`, `MessageService`, `ActionPermissionLogicService`
|
|
430
|
-
|
|
431
|
-
**Features:**
|
|
432
260
|
- Role dropdown with filter
|
|
433
|
-
- Hierarchical TreeTable with checkboxes
|
|
434
|
-
-
|
|
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
|
|
261
|
+
- Hierarchical TreeTable with checkboxes
|
|
262
|
+
- Prerequisite validation with auto-fix dialog
|
|
439
263
|
|
|
440
264
|
---
|
|
441
265
|
|
|
442
266
|
### CompanyActionSelectorComponent
|
|
443
267
|
|
|
444
|
-
|
|
268
|
+
**Selector:** `flusys-company-action-selector` | Manages company action whitelisting.
|
|
445
269
|
|
|
446
|
-
|
|
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
|
|
270
|
+
- Company dropdown (from COMPANY_API_PROVIDER)
|
|
271
|
+
- Full action tree (not filtered)
|
|
460
272
|
- Backend prerequisite error handling with auto-fix
|
|
461
|
-
- Request cancellation with AbortController
|
|
462
|
-
- Change tracking with pending whitelist/remove summary
|
|
463
273
|
|
|
464
274
|
---
|
|
465
275
|
|
|
466
276
|
## Page Components
|
|
467
277
|
|
|
468
|
-
|
|
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 |
|
|
278
|
+
Page components are **internal** (not exported). Accessed via `IAM_ROUTES`.
|
|
535
279
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
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)
|
|
553
|
-
|
|
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`
|
|
280
|
+
| Component | Selector | Features |
|
|
281
|
+
|-----------|----------|----------|
|
|
282
|
+
| `IamContainerComponent` | `lib-iam-container` | Tabbed navigation, responsive tabs |
|
|
283
|
+
| `ActionListPageComponent` | `lib-action-list-page` | TreeTable, CRUD, read-only indicators |
|
|
284
|
+
| `ActionFormPageComponent` | `lib-action-form-page` | Signal forms, LogicBuilder integration |
|
|
285
|
+
| `RoleListPageComponent` | `lib-role-list-page` | Paginated table (10/25/50), CRUD |
|
|
286
|
+
| `RoleFormPageComponent` | `lib-role-form-page` | Signal forms, company auto-assignment |
|
|
287
|
+
| `PermissionPageComponent` | `lib-permission-page` | Dynamic tabs based on permission mode |
|
|
583
288
|
|
|
584
289
|
---
|
|
585
290
|
|
|
586
291
|
## Interfaces
|
|
587
292
|
|
|
588
|
-
###
|
|
293
|
+
### Core Types
|
|
589
294
|
|
|
590
295
|
```typescript
|
|
591
|
-
enum ActionType {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
BOTH = 'both', // Both backend and frontend
|
|
595
|
-
}
|
|
296
|
+
enum ActionType { BACKEND = 'backend', FRONTEND = 'frontend', BOTH = 'both' }
|
|
297
|
+
type PermissionAction = 'add' | 'remove';
|
|
298
|
+
type PermissionMode = 'rbac' | 'direct' | 'full';
|
|
596
299
|
```
|
|
597
300
|
|
|
598
301
|
### IAction (extends IBaseEntity)
|
|
@@ -605,39 +308,12 @@ interface IAction extends IBaseEntity {
|
|
|
605
308
|
code: string | null;
|
|
606
309
|
actionType: ActionType;
|
|
607
310
|
permissionLogic: ILogicNode | null; // AND/OR prerequisite rules
|
|
608
|
-
parentId: string | null;
|
|
609
|
-
serial: number | null;
|
|
311
|
+
parentId: string | null;
|
|
312
|
+
serial: number | null;
|
|
610
313
|
isActive: boolean;
|
|
611
|
-
metadata: Record<string,
|
|
612
|
-
}
|
|
613
|
-
```
|
|
614
|
-
|
|
615
|
-
### IActionTreeDto (extends IAction)
|
|
616
|
-
|
|
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;
|
|
314
|
+
metadata: Record<string, unknown> | null;
|
|
640
315
|
}
|
|
316
|
+
interface IActionTreeDto extends IAction { children: IActionTreeDto[]; }
|
|
641
317
|
```
|
|
642
318
|
|
|
643
319
|
### IRole (extends IBaseEntity)
|
|
@@ -650,159 +326,37 @@ interface IRole extends IBaseEntity {
|
|
|
650
326
|
companyId: string | null;
|
|
651
327
|
isActive: boolean;
|
|
652
328
|
serial: number | null;
|
|
653
|
-
metadata: Record<string,
|
|
329
|
+
metadata: Record<string, unknown> | null;
|
|
654
330
|
}
|
|
655
331
|
```
|
|
656
332
|
|
|
657
|
-
###
|
|
333
|
+
### Assignment DTOs
|
|
658
334
|
|
|
659
335
|
```typescript
|
|
660
|
-
interface
|
|
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
|
-
}
|
|
336
|
+
interface IPermissionItemDto { id: string; action: PermissionAction; }
|
|
689
337
|
|
|
690
|
-
// Assignment DTOs
|
|
691
338
|
interface IAssignUserActionsDto {
|
|
692
|
-
userId: string;
|
|
693
|
-
companyId?: string;
|
|
694
|
-
branchId?: string; // null = company-wide
|
|
695
|
-
items: IPermissionItemDto[];
|
|
339
|
+
userId: string; companyId?: string; branchId?: string; items: IPermissionItemDto[];
|
|
696
340
|
}
|
|
697
|
-
|
|
698
|
-
interface IAssignCompanyActionsDto {
|
|
699
|
-
companyId: string;
|
|
700
|
-
items: IPermissionItemDto[];
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
interface IAssignRoleActionsDto {
|
|
704
|
-
roleId: string;
|
|
705
|
-
items: IPermissionItemDto[];
|
|
706
|
-
}
|
|
707
|
-
|
|
341
|
+
interface IAssignRoleActionsDto { roleId: string; items: IPermissionItemDto[]; }
|
|
342
|
+
interface IAssignCompanyActionsDto { companyId: string; items: IPermissionItemDto[]; }
|
|
708
343
|
interface IAssignUserRolesDto {
|
|
709
|
-
userId: string;
|
|
710
|
-
companyId?: string;
|
|
711
|
-
branchId?: string;
|
|
712
|
-
items: IPermissionItemDto[];
|
|
344
|
+
userId: string; companyId?: string; branchId?: string; items: IPermissionItemDto[];
|
|
713
345
|
}
|
|
714
346
|
```
|
|
715
347
|
|
|
716
348
|
### Response DTOs
|
|
717
349
|
|
|
718
|
-
|
|
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
|
|
350
|
+
All response DTOs include: `id`, entity reference IDs, `actionCode`/`roleCode`, `actionName`/`roleName`, `createdAt`.
|
|
759
351
|
|
|
760
352
|
```typescript
|
|
761
353
|
interface IMyPermissionsResponseDto {
|
|
762
354
|
frontendActions: Array<{ id: string; code: string; name: string; description: string }>;
|
|
763
355
|
cachedEndpoints: number; // Endpoint count for backend PermissionGuard
|
|
764
356
|
}
|
|
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
357
|
|
|
801
358
|
interface IPermissionOperationResultDto {
|
|
802
|
-
success: boolean;
|
|
803
|
-
added: number;
|
|
804
|
-
removed: number;
|
|
805
|
-
message: string;
|
|
359
|
+
success: boolean; added: number; removed: number; message: string;
|
|
806
360
|
prerequisiteErrors?: IPrerequisiteValidationError[];
|
|
807
361
|
}
|
|
808
362
|
```
|
|
@@ -810,351 +364,195 @@ interface IPermissionOperationResultDto {
|
|
|
810
364
|
### Simple Models
|
|
811
365
|
|
|
812
366
|
```typescript
|
|
813
|
-
interface IUser {
|
|
814
|
-
|
|
815
|
-
|
|
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
|
-
}
|
|
367
|
+
interface IUser { id: string; name: string; email: string; }
|
|
368
|
+
interface IBranch { id: string; name: string; companyId: string; }
|
|
369
|
+
interface ICompany { id: string; name: string; slug?: string; }
|
|
848
370
|
```
|
|
849
371
|
|
|
850
372
|
---
|
|
851
373
|
|
|
852
374
|
## Utils & Constants
|
|
853
375
|
|
|
854
|
-
|
|
376
|
+
Utils are **internal** (not exported from public API).
|
|
855
377
|
|
|
856
378
|
### permission-logic.util.ts
|
|
857
379
|
|
|
858
380
|
| Function | Description |
|
|
859
381
|
|----------|-------------|
|
|
860
|
-
| `validateActionPrerequisites(action, selectedIds, allActions)` | Validate prerequisites respecting AND/OR
|
|
861
|
-
| `extractRequiredActionIds(logic, allActions)` | Extract all action IDs from
|
|
862
|
-
| `describePrerequisites(logic, allActions)` | Human-readable prerequisite description |
|
|
382
|
+
| `validateActionPrerequisites(action, selectedIds, allActions)` | Validate prerequisites respecting AND/OR |
|
|
383
|
+
| `extractRequiredActionIds(logic, allActions)` | Extract all action IDs from logic tree |
|
|
863
384
|
|
|
864
385
|
### tree-utils.ts
|
|
865
386
|
|
|
866
387
|
| Function | Description |
|
|
867
388
|
|----------|-------------|
|
|
868
389
|
| `flattenTree<T>(tree)` | Flatten hierarchical tree into flat array |
|
|
869
|
-
| `buildItemMap<T>(items)` | Build ID-to-item `Map` for O(1) lookup |
|
|
870
390
|
| `buildTreeFromFlat<T>(flatList)` | Build tree from flat list using `parentId` |
|
|
871
|
-
| `convertActionToTreeNode(actions)` | Convert
|
|
391
|
+
| `convertActionToTreeNode(actions)` | Convert to PrimeNG `TreeNode<IAction>[]` |
|
|
872
392
|
|
|
873
393
|
### Constants
|
|
874
394
|
|
|
875
395
|
```typescript
|
|
876
|
-
export const MAX_DROPDOWN_ITEMS = 100; // Max items for dropdown lists
|
|
396
|
+
export const MAX_DROPDOWN_ITEMS = 100; // Max items for dropdown lists
|
|
877
397
|
```
|
|
878
398
|
|
|
879
|
-
Exported directly as `MAX_DROPDOWN_ITEMS` from the package.
|
|
880
|
-
|
|
881
399
|
---
|
|
882
400
|
|
|
883
|
-
##
|
|
884
|
-
|
|
885
|
-
### Required Endpoints
|
|
886
|
-
|
|
887
|
-
**Action CRUD:**
|
|
401
|
+
## Responsive Table Patterns
|
|
888
402
|
|
|
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
|
-
```
|
|
403
|
+
All IAM tables use consistent Tailwind CSS patterns:
|
|
898
404
|
|
|
899
|
-
|
|
405
|
+
```html
|
|
406
|
+
<!-- Container: horizontal scroll on mobile, full-width on desktop -->
|
|
407
|
+
<div class="overflow-x-auto -mx-4 sm:mx-0">
|
|
408
|
+
<p-treeTable [tableStyle]="{ 'min-width': '50rem' }">...</p-treeTable>
|
|
409
|
+
</div>
|
|
900
410
|
|
|
411
|
+
<!-- Responsive columns -->
|
|
412
|
+
<th>Name</th> <!-- Always visible -->
|
|
413
|
+
<th class="hidden sm:table-cell">Code</th> <!-- 640px+ -->
|
|
414
|
+
<th class="hidden md:table-cell">Status</th> <!-- 768px+ -->
|
|
415
|
+
<th class="hidden lg:table-cell">Description</th> <!-- 1024px+ -->
|
|
901
416
|
```
|
|
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
417
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
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
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
```
|
|
418
|
+
| Breakpoint | Class | Visibility |
|
|
419
|
+
|------------|-------|------------|
|
|
420
|
+
| Always | (none) | Always visible |
|
|
421
|
+
| sm | `hidden sm:table-cell` | 640px+ |
|
|
422
|
+
| md | `hidden md:table-cell` | 768px+ |
|
|
423
|
+
| lg | `hidden lg:table-cell` | 1024px+ |
|
|
941
424
|
|
|
942
425
|
---
|
|
943
426
|
|
|
944
|
-
##
|
|
427
|
+
## Angular 21 Signal Forms Pattern
|
|
945
428
|
|
|
946
|
-
|
|
429
|
+
```typescript
|
|
430
|
+
import { form, required } from '@angular/forms';
|
|
431
|
+
|
|
432
|
+
readonly formModel = {
|
|
433
|
+
name: '',
|
|
434
|
+
description: '',
|
|
435
|
+
code: '',
|
|
436
|
+
actionType: ActionType.FRONTEND,
|
|
437
|
+
parentId: null as string | null,
|
|
438
|
+
serial: null as number | null,
|
|
439
|
+
isActive: true,
|
|
440
|
+
permissionLogic: null as ILogicNode | null,
|
|
441
|
+
};
|
|
947
442
|
|
|
948
|
-
|
|
443
|
+
readonly actionForm = form(this.formModel, (f) => {
|
|
444
|
+
required(f.name, { message: 'Name is required' });
|
|
445
|
+
});
|
|
949
446
|
|
|
950
|
-
|
|
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>
|
|
447
|
+
readonly canSubmit = computed(() => this.actionForm.valid() && !this.isSaving());
|
|
962
448
|
```
|
|
963
449
|
|
|
964
|
-
|
|
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
|
-
```
|
|
450
|
+
---
|
|
983
451
|
|
|
984
|
-
|
|
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+ |
|
|
452
|
+
## Backend Integration
|
|
991
453
|
|
|
992
|
-
###
|
|
454
|
+
### Required Endpoints
|
|
993
455
|
|
|
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
456
|
```
|
|
457
|
+
# Action CRUD
|
|
458
|
+
POST /iam/actions/tree
|
|
459
|
+
POST /iam/actions/tree-for-permission
|
|
460
|
+
POST /iam/actions/get-all
|
|
461
|
+
POST /iam/actions/get/:id
|
|
462
|
+
POST /iam/actions/insert
|
|
463
|
+
POST /iam/actions/update
|
|
464
|
+
POST /iam/actions/delete
|
|
1005
465
|
|
|
1006
|
-
|
|
466
|
+
# Role CRUD
|
|
467
|
+
POST /iam/roles/get-all
|
|
468
|
+
POST /iam/roles/get/:id
|
|
469
|
+
POST /iam/roles/insert
|
|
470
|
+
POST /iam/roles/update
|
|
471
|
+
POST /iam/roles/delete
|
|
1007
472
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
background-color: rgba(234, 179, 8, 0.1);
|
|
1019
|
-
color: var(--p-yellow-400, #facc15);
|
|
1020
|
-
}
|
|
473
|
+
# Permission Assignments
|
|
474
|
+
POST /iam/permissions/my-permissions
|
|
475
|
+
POST /iam/permissions/user-actions/assign
|
|
476
|
+
POST /iam/permissions/get-user-actions
|
|
477
|
+
POST /iam/permissions/user-roles/assign
|
|
478
|
+
POST /iam/permissions/get-user-roles
|
|
479
|
+
POST /iam/permissions/role-actions/assign
|
|
480
|
+
POST /iam/permissions/get-role-actions
|
|
481
|
+
POST /iam/permissions/company-actions/assign
|
|
482
|
+
POST /iam/permissions/get-company-actions
|
|
1021
483
|
```
|
|
1022
484
|
|
|
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
485
|
---
|
|
1033
486
|
|
|
1034
487
|
## Best Practices
|
|
1035
488
|
|
|
1036
489
|
### Permission Loading
|
|
1037
490
|
|
|
1038
|
-
Load permissions in a guard to block navigation until ready:
|
|
1039
|
-
|
|
1040
491
|
```typescript
|
|
1041
|
-
//
|
|
492
|
+
// In guard - block navigation until ready
|
|
1042
493
|
if (!permissionState.isLoaded()) {
|
|
1043
|
-
|
|
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
|
|
1048
|
-
}
|
|
494
|
+
await firstValueFrom(permissionState.loadPermissions());
|
|
1049
495
|
}
|
|
1050
|
-
return true;
|
|
1051
496
|
```
|
|
1052
497
|
|
|
1053
|
-
- Cache
|
|
1054
|
-
- Clear
|
|
1055
|
-
- Never store
|
|
1056
|
-
- Never load
|
|
498
|
+
- Cache in memory (PermissionStateService handles this)
|
|
499
|
+
- Clear on logout
|
|
500
|
+
- Never store in localStorage (security risk)
|
|
501
|
+
- Never load in component `ngOnInit` (race conditions)
|
|
1057
502
|
|
|
1058
503
|
### Permission Checks
|
|
1059
504
|
|
|
1060
505
|
```typescript
|
|
1061
|
-
// Single
|
|
1062
|
-
validator.
|
|
1063
|
-
|
|
1064
|
-
// ALL required
|
|
1065
|
-
validator.hasAllActions(['user.create', 'user.update']);
|
|
1066
|
-
|
|
1067
|
-
// ANY required
|
|
1068
|
-
validator.hasAnyAction(['user.view', 'user.list']);
|
|
506
|
+
validator.hasAction('user.create'); // Single
|
|
507
|
+
validator.hasAllActions(['user.create', 'user.update']); // ALL required
|
|
508
|
+
validator.hasAnyAction(['user.view', 'user.list']); // ANY required
|
|
1069
509
|
```
|
|
1070
510
|
|
|
1071
|
-
Cache check results in computed signals
|
|
511
|
+
Cache check results in computed signals, not in templates.
|
|
1072
512
|
|
|
1073
513
|
### Menu Filtering
|
|
1074
514
|
|
|
1075
515
|
```typescript
|
|
1076
|
-
// Set menu with requiredAction or logicNode
|
|
1077
516
|
this.layoutService.setMenu(menuItems);
|
|
1078
|
-
|
|
1079
|
-
// Set permission checker (layout handles filtering automatically)
|
|
1080
|
-
this.layoutService.setPermissionChecker((actionCode) =>
|
|
1081
|
-
validator.hasAction(actionCode)
|
|
1082
|
-
);
|
|
517
|
+
this.layoutService.setPermissionChecker((code) => validator.hasAction(code));
|
|
1083
518
|
```
|
|
1084
519
|
|
|
1085
|
-
Do not filter menus manually - let layout handle it.
|
|
1086
|
-
|
|
1087
520
|
### Action Naming
|
|
1088
521
|
|
|
1089
522
|
Format: `module.operation` or `module.entity.operation`
|
|
1090
523
|
|
|
1091
524
|
```typescript
|
|
1092
|
-
// Good
|
|
1093
|
-
'
|
|
1094
|
-
'user.update'
|
|
1095
|
-
'report.export'
|
|
1096
|
-
'admin.system.config'
|
|
1097
|
-
|
|
1098
|
-
// Bad
|
|
1099
|
-
'create' // Too generic
|
|
1100
|
-
'userCreate' // Not dot notation
|
|
1101
|
-
'users' // Entity name, not action
|
|
525
|
+
// Good: 'user.create', 'report.export', 'admin.system.config'
|
|
526
|
+
// Bad: 'create' (generic), 'userCreate' (not dot notation)
|
|
1102
527
|
```
|
|
1103
528
|
|
|
1104
529
|
### LogicNode Pattern
|
|
1105
530
|
|
|
1106
|
-
Complex permission rules using AND/OR operators:
|
|
1107
|
-
|
|
1108
531
|
```typescript
|
|
1109
532
|
// User needs 'user.create' AND ('user.update' OR 'user.delete')
|
|
1110
533
|
const logic: ILogicNode = {
|
|
1111
534
|
type: 'AND',
|
|
1112
535
|
children: [
|
|
1113
536
|
{ type: 'ACTION', value: 'user.create' },
|
|
1114
|
-
{
|
|
1115
|
-
type: '
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
{ type: 'ACTION', value: 'user.delete' },
|
|
1119
|
-
],
|
|
1120
|
-
},
|
|
537
|
+
{ type: 'OR', children: [
|
|
538
|
+
{ type: 'ACTION', value: 'user.update' },
|
|
539
|
+
{ type: 'ACTION', value: 'user.delete' },
|
|
540
|
+
]},
|
|
1121
541
|
],
|
|
1122
542
|
};
|
|
1123
543
|
```
|
|
1124
544
|
|
|
1125
545
|
---
|
|
1126
546
|
|
|
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
547
|
## Configuration
|
|
1144
548
|
|
|
1145
|
-
### Frontend Environment
|
|
1146
|
-
|
|
1147
549
|
```typescript
|
|
1148
|
-
// environment.ts
|
|
1149
|
-
services: {
|
|
1150
|
-
iam: { baseUrl: 'http://localhost:2002/iam', enabled: true },
|
|
1151
|
-
}
|
|
550
|
+
// Frontend: environment.ts
|
|
551
|
+
services: { iam: { baseUrl: 'http://localhost:2002/iam', enabled: true } }
|
|
1152
552
|
```
|
|
1153
553
|
|
|
1154
|
-
### Backend Environment
|
|
1155
|
-
|
|
1156
554
|
```bash
|
|
1157
|
-
# .env
|
|
555
|
+
# Backend: .env
|
|
1158
556
|
IAM_PERMISSION_MODE=FULL # DIRECT, RBAC, or FULL
|
|
1159
557
|
```
|
|
1160
558
|
|
|
@@ -1170,60 +568,41 @@ IAM_PERMISSION_MODE=FULL # DIRECT, RBAC, or FULL
|
|
|
1170
568
|
|
|
1171
569
|
### provideIamProviders()
|
|
1172
570
|
|
|
1173
|
-
Registers
|
|
571
|
+
Registers adapters for cross-package communication (e.g., ng-auth profile page).
|
|
1174
572
|
|
|
1175
573
|
```typescript
|
|
1176
574
|
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
|
-
};
|
|
575
|
+
providers: [...provideIamProviders()]
|
|
1184
576
|
```
|
|
1185
577
|
|
|
1186
|
-
|
|
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.
|
|
578
|
+
Registers `PROFILE_PERMISSION_PROVIDER` with `ProfilePermissionProviderAdapter`.
|
|
1195
579
|
|
|
1196
|
-
|
|
1197
|
-
interface IProfilePermissionProvider {
|
|
1198
|
-
getUserRoles(userId: string, branchId?: string): Observable<ISingleResponse<IProfileRoleInfo[]>>;
|
|
1199
|
-
getUserActions(userId: string, branchId?: string): Observable<ISingleResponse<IProfileActionInfo[]>>;
|
|
1200
|
-
}
|
|
1201
|
-
```
|
|
580
|
+
---
|
|
1202
581
|
|
|
1203
|
-
|
|
582
|
+
## Common Issues
|
|
1204
583
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
```
|
|
584
|
+
| Problem | Solution |
|
|
585
|
+
|---------|----------|
|
|
586
|
+
| `permissionState.isLoaded()` returns false | Check `services.iam.enabled: true`, verify `/iam/permissions/my-permissions` works |
|
|
587
|
+
| Empty menu after login | Verify user has frontend actions, check `requiredAction` matches action codes |
|
|
588
|
+
| Menu items not filtered | Call `layoutService.setPermissionChecker()` after loading permissions |
|
|
589
|
+
| 401 on IAM endpoints | Ensure auth interceptor adds `Authorization` header |
|
|
590
|
+
| Provider not configured error | Add required providers to `app.config.ts` |
|
|
591
|
+
| Empty dropdowns | Ensure adapters return `IListResponse<T>` format |
|
|
592
|
+
| Table not scrollable on mobile | Add `overflow-x-auto -mx-4 sm:mx-0` wrapper |
|
|
593
|
+
| Columns overflow on mobile | Add `hidden sm:table-cell` to non-essential columns |
|
|
1216
594
|
|
|
1217
595
|
---
|
|
1218
596
|
|
|
1219
597
|
## See Also
|
|
1220
598
|
|
|
1221
|
-
-
|
|
1222
|
-
-
|
|
1223
|
-
-
|
|
1224
|
-
-
|
|
599
|
+
- [CORE-GUIDE.md](./CORE-GUIDE.md) - Configuration system (APP_CONFIG)
|
|
600
|
+
- [AUTH-GUIDE.md](./AUTH-GUIDE.md) - Authentication and provider adapters
|
|
601
|
+
- [SHARED-GUIDE.md](./SHARED-GUIDE.md) - Provider interface definitions
|
|
602
|
+
- [LAYOUT-GUIDE.md](./LAYOUT-GUIDE.md) - Menu system and filtering
|
|
1225
603
|
|
|
1226
604
|
---
|
|
1227
605
|
|
|
1228
|
-
**Last Updated:** 2026-02-
|
|
606
|
+
**Last Updated:** 2026-02-25
|
|
607
|
+
**Version:** 3.0.0
|
|
1229
608
|
**Angular Version:** 21
|