@flusys/ng-iam 1.0.0-rc → 1.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 +1 -1
- package/fesm2022/{flusys-ng-iam-action-form-page.component-C_BRrrWW.mjs → flusys-ng-iam-action-form-page.component-CVN8sV-c.mjs} +11 -61
- package/fesm2022/flusys-ng-iam-action-form-page.component-CVN8sV-c.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-action-list-page.component-Daf93zpS.mjs → flusys-ng-iam-action-list-page.component-CQ6RazN0.mjs} +19 -46
- package/fesm2022/flusys-ng-iam-action-list-page.component-CQ6RazN0.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-flusys-ng-iam-BPIpfrjN.mjs → flusys-ng-iam-flusys-ng-iam-DrGHlTiz.mjs} +251 -813
- package/fesm2022/flusys-ng-iam-flusys-ng-iam-DrGHlTiz.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-iam-container.component-Bn4kQtxW.mjs → flusys-ng-iam-iam-container.component-BToYxEej.mjs} +2 -2
- package/fesm2022/flusys-ng-iam-iam-container.component-BToYxEej.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-permission-page.component-CmxOBJPu.mjs → flusys-ng-iam-permission-page.component-BS7xXmsn.mjs} +8 -43
- package/fesm2022/flusys-ng-iam-permission-page.component-BS7xXmsn.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-role-form-page.component-ByNueI1a.mjs → flusys-ng-iam-role-form-page.component-BjPwXkip.mjs} +2 -16
- package/fesm2022/flusys-ng-iam-role-form-page.component-BjPwXkip.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-role-list-page.component-CFly5KnH.mjs → flusys-ng-iam-role-list-page.component-Cz-jk-R_.mjs} +3 -20
- package/fesm2022/flusys-ng-iam-role-list-page.component-Cz-jk-R_.mjs.map +1 -0
- package/fesm2022/flusys-ng-iam.mjs +1 -1
- package/package.json +11 -11
- package/types/flusys-ng-iam.d.ts +46 -444
- package/fesm2022/flusys-ng-iam-action-form-page.component-C_BRrrWW.mjs.map +0 -1
- package/fesm2022/flusys-ng-iam-action-list-page.component-Daf93zpS.mjs.map +0 -1
- package/fesm2022/flusys-ng-iam-flusys-ng-iam-BPIpfrjN.mjs.map +0 -1
- package/fesm2022/flusys-ng-iam-iam-container.component-Bn4kQtxW.mjs.map +0 -1
- package/fesm2022/flusys-ng-iam-permission-page.component-CmxOBJPu.mjs.map +0 -1
- package/fesm2022/flusys-ng-iam-role-form-page.component-ByNueI1a.mjs.map +0 -1
- package/fesm2022/flusys-ng-iam-role-list-page.component-CFly5KnH.mjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Injectable, signal, input, output, computed, effect, ChangeDetectionStrategy, Component, DestroyRef } from '@angular/core';
|
|
3
1
|
import { HttpClient } from '@angular/common/http';
|
|
4
|
-
import
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { inject, Injectable, signal, input, output, effect, ChangeDetectionStrategy, Component, DestroyRef, computed } from '@angular/core';
|
|
4
|
+
import { ApiResourceService, PermissionValidatorService, AngularModule, PrimeModule, ROLE_ACTION_PERMISSIONS, HasPermissionDirective, COMPANY_ACTION_PERMISSIONS, COMPANY_API_PROVIDER, USER_ROLE_PERMISSIONS, USER_PERMISSION_PROVIDER, UserSelectComponent, USER_ACTION_PERMISSIONS, PROFILE_PERMISSION_PROVIDER, permissionGuard, ACTION_PERMISSIONS, ROLE_PERMISSIONS, anyPermissionGuard } from '@flusys/ng-shared';
|
|
5
5
|
import { APP_CONFIG, BaseApiService, isCompanyFeatureEnabled } from '@flusys/ng-core';
|
|
6
6
|
import { ConfirmationService, MessageService } from 'primeng/api';
|
|
7
7
|
import { of, firstValueFrom, map as map$1 } from 'rxjs';
|
|
@@ -19,11 +19,7 @@ import * as i7 from 'primeng/treetable';
|
|
|
19
19
|
import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
|
|
20
20
|
import * as i5$1 from 'primeng/table';
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Action Type - determines how action is used
|
|
25
|
-
* CRITICAL: Must match backend ActionType enum exactly
|
|
26
|
-
*/
|
|
22
|
+
/** Must match backend ActionType enum */
|
|
27
23
|
var ActionType;
|
|
28
24
|
(function (ActionType) {
|
|
29
25
|
ActionType["BACKEND"] = "backend";
|
|
@@ -33,81 +29,40 @@ var ActionType;
|
|
|
33
29
|
|
|
34
30
|
// Company interfaces
|
|
35
31
|
|
|
36
|
-
/**
|
|
37
|
-
* Pagination Constants
|
|
38
|
-
*
|
|
39
|
-
* Standard pagination limits for IAM components.
|
|
40
|
-
* Prevents excessive data loading and potential DoS.
|
|
41
|
-
*/
|
|
42
|
-
/**
|
|
43
|
-
* Maximum items to fetch for dropdown lists
|
|
44
|
-
* Used for: companies, roles, users, branches
|
|
45
|
-
*
|
|
46
|
-
* Security: Prevents memory exhaustion from loading excessive records
|
|
47
|
-
*/
|
|
32
|
+
/** Maximum items for dropdown lists (companies, roles, users, branches) */
|
|
48
33
|
const MAX_DROPDOWN_ITEMS = 100;
|
|
49
34
|
|
|
50
|
-
/**
|
|
51
|
-
* Role API Service
|
|
52
|
-
* Handles role CRUD operations
|
|
53
|
-
* Endpoint: POST /iam/roles/*
|
|
54
|
-
* Conditional: Only available in RBAC/FULL mode
|
|
55
|
-
*/
|
|
56
35
|
class RoleApiService extends ApiResourceService {
|
|
57
36
|
constructor() {
|
|
58
|
-
|
|
59
|
-
super('iam/roles', http);
|
|
37
|
+
super('iam/roles', inject(HttpClient));
|
|
60
38
|
}
|
|
61
39
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoleApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
62
40
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoleApiService, providedIn: 'root' });
|
|
63
41
|
}
|
|
64
42
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoleApiService, decorators: [{
|
|
65
43
|
type: Injectable,
|
|
66
|
-
args: [{
|
|
67
|
-
providedIn: 'root',
|
|
68
|
-
}]
|
|
44
|
+
args: [{ providedIn: 'root' }]
|
|
69
45
|
}], ctorParameters: () => [] });
|
|
70
46
|
|
|
71
|
-
/**
|
|
72
|
-
* Action API Service
|
|
73
|
-
* Handles action CRUD operations
|
|
74
|
-
* Endpoint: POST /iam/actions/*
|
|
75
|
-
*/
|
|
76
47
|
class ActionApiService extends ApiResourceService {
|
|
77
48
|
appConfig = inject(APP_CONFIG);
|
|
78
49
|
constructor() {
|
|
79
50
|
const http = inject(HttpClient);
|
|
80
51
|
super('iam/actions', http);
|
|
81
52
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Get actions for permission assignment
|
|
84
|
-
* POST /iam/actions/tree-for-permission
|
|
85
|
-
* Returns actions filtered by company whitelist if enabled
|
|
86
|
-
*/
|
|
53
|
+
/** Get actions filtered by company whitelist for permission assignment */
|
|
87
54
|
getActionsForPermission() {
|
|
88
55
|
return this.http.post(`${this.appConfig.apiBaseUrl}/iam/actions/tree-for-permission`, {});
|
|
89
56
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Get actions in hierarchical tree structure
|
|
92
|
-
* POST /iam/actions/tree
|
|
93
|
-
* Returns all actions organized in parent-child tree
|
|
94
|
-
*
|
|
95
|
-
* @param search - Optional search term (name or code)
|
|
96
|
-
* @param isActive - Optional filter by active status
|
|
97
|
-
* @param withDeleted - Include deleted actions (default: false)
|
|
98
|
-
* @returns Observable of action tree response
|
|
99
|
-
*/
|
|
57
|
+
/** Get actions in hierarchical tree structure */
|
|
100
58
|
getTree(search, isActive, withDeleted) {
|
|
101
59
|
const body = {};
|
|
102
|
-
if (search?.trim())
|
|
60
|
+
if (search?.trim())
|
|
103
61
|
body.search = search.trim();
|
|
104
|
-
|
|
105
|
-
if (isActive !== undefined) {
|
|
62
|
+
if (isActive !== undefined)
|
|
106
63
|
body.isActive = isActive;
|
|
107
|
-
|
|
108
|
-
if (withDeleted !== undefined) {
|
|
64
|
+
if (withDeleted !== undefined)
|
|
109
65
|
body.withDeleted = withDeleted;
|
|
110
|
-
}
|
|
111
66
|
return this.http.post(`${this.appConfig.apiBaseUrl}/iam/actions/tree`, body);
|
|
112
67
|
}
|
|
113
68
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ActionApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
@@ -115,48 +70,24 @@ class ActionApiService extends ApiResourceService {
|
|
|
115
70
|
}
|
|
116
71
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ActionApiService, decorators: [{
|
|
117
72
|
type: Injectable,
|
|
118
|
-
args: [{
|
|
119
|
-
providedIn: 'root',
|
|
120
|
-
}]
|
|
73
|
+
args: [{ providedIn: 'root' }]
|
|
121
74
|
}], ctorParameters: () => [] });
|
|
122
75
|
|
|
123
|
-
/**
|
|
124
|
-
* Extract all required action IDs from a permission logic tree
|
|
125
|
-
* This is useful for prerequisite validation
|
|
126
|
-
*
|
|
127
|
-
* @param logic - Permission logic to analyze
|
|
128
|
-
* @returns Set of action IDs that are required
|
|
129
|
-
*/
|
|
130
76
|
function extractRequiredActionIds(logic) {
|
|
131
77
|
const actionIds = new Set();
|
|
132
|
-
if (
|
|
133
|
-
|
|
78
|
+
if (logic) {
|
|
79
|
+
collectActionIds(logic, actionIds);
|
|
134
80
|
}
|
|
135
|
-
collectActionIds(logic, actionIds);
|
|
136
81
|
return actionIds;
|
|
137
82
|
}
|
|
138
|
-
/**
|
|
139
|
-
* Recursively collect action IDs from logic tree
|
|
140
|
-
*/
|
|
141
83
|
function collectActionIds(node, actionIds) {
|
|
142
84
|
if (node.type === 'action' && node.actionId) {
|
|
143
85
|
actionIds.add(node.actionId);
|
|
144
86
|
}
|
|
145
87
|
else if (node.type === 'group' && node.children) {
|
|
146
|
-
|
|
147
|
-
collectActionIds(child, actionIds);
|
|
148
|
-
}
|
|
88
|
+
node.children.forEach((child) => collectActionIds(child, actionIds));
|
|
149
89
|
}
|
|
150
90
|
}
|
|
151
|
-
/**
|
|
152
|
-
* Validate if an action's prerequisites are satisfied
|
|
153
|
-
* Respects AND/OR logic operators in permission tree
|
|
154
|
-
*
|
|
155
|
-
* @param action - Action to validate
|
|
156
|
-
* @param selectedActionIds - Set of currently selected action IDs
|
|
157
|
-
* @param allActions - All available actions (to look up missing ones)
|
|
158
|
-
* @returns Validation result with missing actions
|
|
159
|
-
*/
|
|
160
91
|
function validateActionPrerequisites(action, selectedActionIds, allActions) {
|
|
161
92
|
if (!action.permissionLogic) {
|
|
162
93
|
return { valid: true, missingActions: [] };
|
|
@@ -168,20 +99,12 @@ function validateActionPrerequisites(action, selectedActionIds, allActions) {
|
|
|
168
99
|
const missingActions = allActions.filter((a) => a.id && result.missingActionIds.has(a.id));
|
|
169
100
|
return { valid: false, missingActions };
|
|
170
101
|
}
|
|
171
|
-
/**
|
|
172
|
-
* Recursively evaluate logic node respecting AND/OR operators
|
|
173
|
-
* Returns validation result with missing action IDs (for prerequisite dialogs)
|
|
174
|
-
* Note: This differs from evaluateLogicNode in ng-shared which returns boolean only
|
|
175
|
-
*/
|
|
176
102
|
function evaluateLogicNodeWithMissing(node, selectedActionIds) {
|
|
177
103
|
if (node.type === 'action' && node.actionId) {
|
|
178
104
|
const valid = selectedActionIds.has(node.actionId);
|
|
179
|
-
return {
|
|
180
|
-
valid,
|
|
181
|
-
missingActionIds: valid ? new Set() : new Set([node.actionId]),
|
|
182
|
-
};
|
|
105
|
+
return { valid, missingActionIds: valid ? new Set() : new Set([node.actionId]) };
|
|
183
106
|
}
|
|
184
|
-
if (node.type === 'group' && node.children
|
|
107
|
+
if (node.type === 'group' && node.children?.length) {
|
|
185
108
|
const operator = node.operator || 'AND';
|
|
186
109
|
if (operator === 'AND') {
|
|
187
110
|
const missingIds = new Set();
|
|
@@ -195,109 +118,28 @@ function evaluateLogicNodeWithMissing(node, selectedActionIds) {
|
|
|
195
118
|
}
|
|
196
119
|
return { valid: allValid, missingActionIds: missingIds };
|
|
197
120
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
121
|
+
// OR operator
|
|
122
|
+
for (const child of node.children) {
|
|
123
|
+
const childResult = evaluateLogicNodeWithMissing(child, selectedActionIds);
|
|
124
|
+
if (childResult.valid) {
|
|
125
|
+
return { valid: true, missingActionIds: new Set() };
|
|
204
126
|
}
|
|
205
|
-
const firstChildResult = evaluateLogicNodeWithMissing(node.children[0], selectedActionIds);
|
|
206
|
-
return {
|
|
207
|
-
valid: false,
|
|
208
|
-
missingActionIds: firstChildResult.missingActionIds,
|
|
209
|
-
};
|
|
210
127
|
}
|
|
128
|
+
const firstChildResult = evaluateLogicNodeWithMissing(node.children[0], selectedActionIds);
|
|
129
|
+
return { valid: false, missingActionIds: firstChildResult.missingActionIds };
|
|
211
130
|
}
|
|
212
131
|
return { valid: true, missingActionIds: new Set() };
|
|
213
132
|
}
|
|
214
|
-
/**
|
|
215
|
-
* Get human-readable prerequisite description
|
|
216
|
-
*
|
|
217
|
-
* @param logic - Permission logic to describe
|
|
218
|
-
* @param allActions - All available actions for name lookup
|
|
219
|
-
* @returns Human-readable string describing prerequisites
|
|
220
|
-
*/
|
|
221
|
-
function describePrerequisites(logic, allActions) {
|
|
222
|
-
if (!logic) {
|
|
223
|
-
return 'None';
|
|
224
|
-
}
|
|
225
|
-
const requiredIds = extractRequiredActionIds(logic);
|
|
226
|
-
if (requiredIds.size === 0) {
|
|
227
|
-
return 'None';
|
|
228
|
-
}
|
|
229
|
-
const actionMap = new Map(allActions.map((a) => [a.id, a]));
|
|
230
|
-
const names = Array.from(requiredIds)
|
|
231
|
-
.map((id) => actionMap.get(id)?.name || id)
|
|
232
|
-
.filter(Boolean);
|
|
233
|
-
if (names.length === 0) {
|
|
234
|
-
return 'Unknown prerequisites';
|
|
235
|
-
}
|
|
236
|
-
if (names.length === 1) {
|
|
237
|
-
return names[0];
|
|
238
|
-
}
|
|
239
|
-
return names.join(', ');
|
|
240
|
-
}
|
|
241
133
|
|
|
242
|
-
/**
|
|
243
|
-
* Action Permission Logic Service
|
|
244
|
-
*
|
|
245
|
-
* Shared service for handling smart dependency management across all action selectors:
|
|
246
|
-
* - Company-Action Selector
|
|
247
|
-
* - Role-Action Selector
|
|
248
|
-
* - User-Action Selector
|
|
249
|
-
*
|
|
250
|
-
* **Core Features:**
|
|
251
|
-
* - Smart auto-selection (AND/OR optimization)
|
|
252
|
-
* - Dependency detection and management
|
|
253
|
-
* - Alternative suggestion for OR logic
|
|
254
|
-
* - Visual formatting of permission logic trees
|
|
255
|
-
* - Prerequisite validation
|
|
256
|
-
*
|
|
257
|
-
* @example
|
|
258
|
-
* constructor() {
|
|
259
|
-
* this.permissionLogic = inject(ActionPermissionLogicService);
|
|
260
|
-
* }
|
|
261
|
-
*
|
|
262
|
-
* onActionToggle(action: IAction, newValue: boolean) {
|
|
263
|
-
* if (!newValue) {
|
|
264
|
-
* this.permissionLogic.handleUncheck(
|
|
265
|
-
* action,
|
|
266
|
-
* this.selectionMap(),
|
|
267
|
-
* this.actions(),
|
|
268
|
-
* (newMap) => this.selectionMap.set(newMap)
|
|
269
|
-
* );
|
|
270
|
-
* } else {
|
|
271
|
-
* this.permissionLogic.handleCheck(
|
|
272
|
-
* action,
|
|
273
|
-
* this.selectionMap(),
|
|
274
|
-
* this.actions(),
|
|
275
|
-
* (newMap) => this.selectionMap.set(newMap),
|
|
276
|
-
* (previousState) => this.selectionMap.set(previousState)
|
|
277
|
-
* );
|
|
278
|
-
* }
|
|
279
|
-
* }
|
|
280
|
-
*/
|
|
134
|
+
/** Shared service for smart dependency management across action selectors */
|
|
281
135
|
class ActionPermissionLogicService {
|
|
282
136
|
confirmationService = inject(ConfirmationService);
|
|
283
137
|
messageService = inject(MessageService);
|
|
284
|
-
/**
|
|
285
|
-
* Handle checking an action with prerequisite validation
|
|
286
|
-
*
|
|
287
|
-
* Uses recursive deep scan to find ALL missing prerequisites at all levels,
|
|
288
|
-
* not just direct dependencies. This ensures cascading dependencies are
|
|
289
|
-
* resolved in a single step.
|
|
290
|
-
*
|
|
291
|
-
* @param action - Action being checked
|
|
292
|
-
* @param currentSelection - Current selection map
|
|
293
|
-
* @param allActions - All available actions
|
|
294
|
-
* @param onUpdate - Callback to update selection
|
|
295
|
-
* @param onCancel - Callback when user cancels
|
|
296
|
-
*/
|
|
138
|
+
/** Handle checking an action with prerequisite validation (recursive deep scan) */
|
|
297
139
|
handleCheck(action, currentSelection, allActions, onUpdate, onCancel) {
|
|
298
140
|
// Validate prerequisites with RECURSIVE DEEP SCAN
|
|
299
141
|
if (action.permissionLogic) {
|
|
300
|
-
const selectedActionIds =
|
|
142
|
+
const selectedActionIds = this.getSelectedIds(currentSelection);
|
|
301
143
|
const validationResult = validateActionPrerequisites(action, selectedActionIds, allActions);
|
|
302
144
|
if (!validationResult.valid) {
|
|
303
145
|
// Store previous state
|
|
@@ -318,14 +160,7 @@ class ActionPermissionLogicService {
|
|
|
318
160
|
selMap[action.id] = true;
|
|
319
161
|
onUpdate(selMap);
|
|
320
162
|
}
|
|
321
|
-
/**
|
|
322
|
-
* Handle unchecking an action with dependency detection
|
|
323
|
-
*
|
|
324
|
-
* @param action - Action being unchecked
|
|
325
|
-
* @param currentSelection - Current selection map
|
|
326
|
-
* @param allActions - All available actions
|
|
327
|
-
* @param onUpdate - Callback to update selection
|
|
328
|
-
*/
|
|
163
|
+
/** Handle unchecking an action with dependency detection */
|
|
329
164
|
handleUncheck(action, currentSelection, allActions, onUpdate) {
|
|
330
165
|
const affectedActions = this.findActionsDependingOn(action.id, currentSelection, allActions);
|
|
331
166
|
if (affectedActions.length > 0) {
|
|
@@ -337,46 +172,28 @@ class ActionPermissionLogicService {
|
|
|
337
172
|
selMap[action.id] = false;
|
|
338
173
|
onUpdate(selMap);
|
|
339
174
|
}
|
|
340
|
-
/**
|
|
341
|
-
* Check if an action has unmet prerequisites
|
|
342
|
-
*
|
|
343
|
-
* @param action - Action to check
|
|
344
|
-
* @param currentSelection - Current selection map
|
|
345
|
-
* @param allActions - All available actions
|
|
346
|
-
* @returns True if action has unmet prerequisites
|
|
347
|
-
*/
|
|
175
|
+
/** Check if an action has unmet prerequisites */
|
|
348
176
|
hasUnmetPrerequisites(action, currentSelection, allActions) {
|
|
349
177
|
const isSelected = currentSelection[action.id];
|
|
350
178
|
if (!isSelected || !action.permissionLogic) {
|
|
351
179
|
return false;
|
|
352
180
|
}
|
|
353
|
-
const selectedActionIds =
|
|
181
|
+
const selectedActionIds = this.getSelectedIds(currentSelection);
|
|
182
|
+
selectedActionIds.delete(action.id);
|
|
354
183
|
const validationResult = validateActionPrerequisites(action, selectedActionIds, allActions);
|
|
355
184
|
return !validationResult.valid;
|
|
356
185
|
}
|
|
357
|
-
/**
|
|
358
|
-
* Get all selected actions that have unmet prerequisites
|
|
359
|
-
*
|
|
360
|
-
* @param currentSelection - Current selection map
|
|
361
|
-
* @param allActions - All available actions
|
|
362
|
-
* @returns Array of actions with unmet prerequisites
|
|
363
|
-
*/
|
|
186
|
+
/** Get all selected actions that have unmet prerequisites */
|
|
364
187
|
getActionsWithUnmetPrerequisites(currentSelection, allActions) {
|
|
365
188
|
const selectedActions = allActions.filter((a) => currentSelection[a.id]);
|
|
366
189
|
return selectedActions.filter((action) => this.hasUnmetPrerequisites(action, currentSelection, allActions));
|
|
367
190
|
}
|
|
368
|
-
/**
|
|
369
|
-
* Show validation error dialog with auto-fix options
|
|
370
|
-
*
|
|
371
|
-
* @param invalidActions - Actions with unmet prerequisites
|
|
372
|
-
* @param currentSelection - Current selection map
|
|
373
|
-
* @param allActions - All available actions
|
|
374
|
-
* @param onUpdate - Callback to update selection
|
|
375
|
-
*/
|
|
191
|
+
/** Show validation error dialog with auto-fix options */
|
|
376
192
|
showValidationErrorDialog(invalidActions, currentSelection, allActions, onUpdate) {
|
|
377
193
|
const errorList = invalidActions
|
|
378
194
|
.map((action) => {
|
|
379
|
-
const selectedActionIds =
|
|
195
|
+
const selectedActionIds = this.getSelectedIds(currentSelection);
|
|
196
|
+
selectedActionIds.delete(action.id);
|
|
380
197
|
const validationResult = validateActionPrerequisites(action, selectedActionIds, allActions);
|
|
381
198
|
const sanitizedActionName = this.sanitizeHtml(action.name ?? 'Unknown');
|
|
382
199
|
const missingNames = validationResult.missingActions
|
|
@@ -418,19 +235,12 @@ class ActionPermissionLogicService {
|
|
|
418
235
|
},
|
|
419
236
|
});
|
|
420
237
|
}
|
|
421
|
-
/**
|
|
422
|
-
* Get prerequisite description for tooltip display
|
|
423
|
-
*
|
|
424
|
-
* @param action - Action to get prerequisites for
|
|
425
|
-
* @param currentSelection - Current selection map
|
|
426
|
-
* @param allActions - All available actions
|
|
427
|
-
* @returns Plain text prerequisite description
|
|
428
|
-
*/
|
|
238
|
+
/** Get prerequisite description for tooltip display */
|
|
429
239
|
getPrerequisiteTooltip(action, currentSelection, allActions) {
|
|
430
240
|
if (!action.permissionLogic) {
|
|
431
241
|
return '';
|
|
432
242
|
}
|
|
433
|
-
const selectedActionIds =
|
|
243
|
+
const selectedActionIds = this.getSelectedIds(currentSelection);
|
|
434
244
|
const validationResult = validateActionPrerequisites(action, selectedActionIds, allActions);
|
|
435
245
|
if (validationResult.valid) {
|
|
436
246
|
return '[OK] Prerequisites Satisfied\n\nAll required actions are already selected.\nYou can safely add this action.';
|
|
@@ -442,23 +252,16 @@ class ActionPermissionLogicService {
|
|
|
442
252
|
const hint = '\n\n💡 Click to auto-select required actions';
|
|
443
253
|
return `${header}\n\n${logicTree}${hint}`;
|
|
444
254
|
}
|
|
445
|
-
/**
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
* @param logic - Permission logic tree
|
|
449
|
-
* @param missingActions - Actions that are missing
|
|
450
|
-
* @param allActions - All available actions
|
|
451
|
-
* @param currentSelection - Current selection map for accurate status
|
|
452
|
-
* @returns HTML formatted logic tree
|
|
453
|
-
*/
|
|
454
|
-
buildLogicMessage(logic, missingActions, allActions, currentSelection) {
|
|
455
|
-
const selectedIds = currentSelection
|
|
456
|
-
? new Set(Object.keys(currentSelection).filter((id) => currentSelection[id]))
|
|
457
|
-
: new Set();
|
|
255
|
+
/** Build dynamic logic tree message with AND/OR operators and nesting */
|
|
256
|
+
buildLogicMessage(logic, _missingActions, allActions, currentSelection) {
|
|
257
|
+
const selectedIds = currentSelection ? this.getSelectedIds(currentSelection) : new Set();
|
|
458
258
|
const actionMap = new Map(allActions.map((a) => [a.id, a]));
|
|
459
259
|
return this.formatLogicNode(logic, selectedIds, actionMap, 0);
|
|
460
260
|
}
|
|
461
|
-
|
|
261
|
+
/** Extract selected action IDs from selection map */
|
|
262
|
+
getSelectedIds(currentSelection) {
|
|
263
|
+
return new Set(Object.keys(currentSelection).filter((id) => currentSelection[id]));
|
|
264
|
+
}
|
|
462
265
|
sanitizeHtml(text) {
|
|
463
266
|
return text
|
|
464
267
|
.replace(/&/g, '&')
|
|
@@ -467,24 +270,7 @@ class ActionPermissionLogicService {
|
|
|
467
270
|
.replace(/"/g, '"')
|
|
468
271
|
.replace(/'/g, ''');
|
|
469
272
|
}
|
|
470
|
-
/**
|
|
471
|
-
* Recursively collect ALL missing prerequisites at all dependency levels
|
|
472
|
-
*
|
|
473
|
-
* This prevents cascading prerequisite dialogs by finding the complete
|
|
474
|
-
* dependency chain upfront.
|
|
475
|
-
*
|
|
476
|
-
* **Example:**
|
|
477
|
-
* - Action 4 requires Action 3
|
|
478
|
-
* - Action 3 requires Action 2
|
|
479
|
-
* - Action 2 requires Action 1
|
|
480
|
-
*
|
|
481
|
-
* Instead of showing 3 separate dialogs, this returns: [Action 3, Action 2, Action 1]
|
|
482
|
-
*
|
|
483
|
-
* @param action - Starting action to check
|
|
484
|
-
* @param currentSelection - Current selection map
|
|
485
|
-
* @param allActions - All available actions
|
|
486
|
-
* @returns Complete set of missing prerequisites across all levels
|
|
487
|
-
*/
|
|
273
|
+
/** Recursively collect ALL missing prerequisites at all dependency levels */
|
|
488
274
|
getAllMissingPrerequisitesRecursive(action, currentSelection, allActions) {
|
|
489
275
|
if (!action.id)
|
|
490
276
|
return [];
|
|
@@ -504,7 +290,7 @@ class ActionPermissionLogicService {
|
|
|
504
290
|
}
|
|
505
291
|
visited.add(targetAction.id);
|
|
506
292
|
stack.add(targetAction.id);
|
|
507
|
-
const selectedActionIds =
|
|
293
|
+
const selectedActionIds = this.getSelectedIds(currentSelection);
|
|
508
294
|
const result = validateActionPrerequisites(targetAction, selectedActionIds, allActions);
|
|
509
295
|
if (!result.valid) {
|
|
510
296
|
result.missingActions.forEach((missingAction) => {
|
|
@@ -565,7 +351,7 @@ class ActionPermissionLogicService {
|
|
|
565
351
|
},
|
|
566
352
|
});
|
|
567
353
|
}
|
|
568
|
-
showDependencyDialog(action, affectedActions, currentSelection,
|
|
354
|
+
showDependencyDialog(action, affectedActions, currentSelection, _allActions, onUpdate) {
|
|
569
355
|
if (!action.id)
|
|
570
356
|
return;
|
|
571
357
|
const alternatives = this.findAlternatives(action.id, affectedActions, currentSelection);
|
|
@@ -688,7 +474,7 @@ class ActionPermissionLogicService {
|
|
|
688
474
|
calculateSmartSelection(logic, missingActions, currentSelection, allActions) {
|
|
689
475
|
const missingIds = new Set(missingActions.map((a) => a.id));
|
|
690
476
|
const actionMap = new Map(allActions.map((a) => [a.id, a]));
|
|
691
|
-
const selectedIds =
|
|
477
|
+
const selectedIds = this.getSelectedIds(currentSelection);
|
|
692
478
|
const requiredIds = this.findRequiredActionIds(logic, missingIds, selectedIds);
|
|
693
479
|
return Array.from(requiredIds)
|
|
694
480
|
.map((id) => actionMap.get(id))
|
|
@@ -761,12 +547,10 @@ class ActionPermissionLogicService {
|
|
|
761
547
|
}
|
|
762
548
|
return `${indent}<em style="color: #9ca3af;">Invalid logic node</em>`;
|
|
763
549
|
}
|
|
764
|
-
/**
|
|
765
|
-
* Build clean text-based logic tree for tooltips
|
|
766
|
-
*/
|
|
550
|
+
/** Build clean text-based logic tree for tooltips */
|
|
767
551
|
buildTooltipLogicTree(node, currentSelection, allActions, depth) {
|
|
768
552
|
const indent = ' '.repeat(depth);
|
|
769
|
-
const selectedIds =
|
|
553
|
+
const selectedIds = this.getSelectedIds(currentSelection);
|
|
770
554
|
const actionMap = new Map(allActions.map((a) => [a.id, a]));
|
|
771
555
|
if (node.type === 'action' && node.actionId) {
|
|
772
556
|
const action = actionMap.get(node.actionId);
|
|
@@ -801,91 +585,34 @@ class ActionPermissionLogicService {
|
|
|
801
585
|
}
|
|
802
586
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ActionPermissionLogicService, decorators: [{
|
|
803
587
|
type: Injectable,
|
|
804
|
-
args: [{
|
|
805
|
-
providedIn: 'root',
|
|
806
|
-
}]
|
|
588
|
+
args: [{ providedIn: 'root' }]
|
|
807
589
|
}] });
|
|
808
590
|
|
|
809
|
-
/**
|
|
810
|
-
* Consolidated Permission API Service
|
|
811
|
-
* Handles all permission-related operations in one service
|
|
812
|
-
* Supports:
|
|
813
|
-
* - User → Action (direct permissions)
|
|
814
|
-
* - User → Role (role assignments)
|
|
815
|
-
* - Role → Action (role permissions)
|
|
816
|
-
* - Company → Action (company whitelisting)
|
|
817
|
-
*
|
|
818
|
-
* Endpoint: POST /permissions/*
|
|
819
|
-
*/
|
|
820
591
|
class PermissionApiService extends BaseApiService {
|
|
821
592
|
constructor() {
|
|
822
593
|
super('iam');
|
|
823
594
|
}
|
|
824
|
-
// =============================================================================
|
|
825
|
-
// User → Action (Direct Permissions)
|
|
826
|
-
// =============================================================================
|
|
827
|
-
/**
|
|
828
|
-
* Assign/remove actions directly to/from user
|
|
829
|
-
* POST /permissions/user-actions/assign
|
|
830
|
-
*/
|
|
831
595
|
assignUserActions(data) {
|
|
832
596
|
return this.http.post(this.getUrl('permissions/user-actions/assign'), data);
|
|
833
597
|
}
|
|
834
|
-
/**
|
|
835
|
-
* Get user's direct action permissions
|
|
836
|
-
* POST /iam/permissions/get-user-actions
|
|
837
|
-
*/
|
|
838
598
|
getUserActions(userId, query) {
|
|
839
599
|
return this.http.post(this.getUrl('permissions/get-user-actions'), { userId, branchId: query?.branchId });
|
|
840
600
|
}
|
|
841
|
-
// =============================================================================
|
|
842
|
-
// User → Role (Role Assignments)
|
|
843
|
-
// =============================================================================
|
|
844
|
-
/**
|
|
845
|
-
* Assign/remove roles to/from user
|
|
846
|
-
* POST /permissions/user-roles/assign
|
|
847
|
-
*/
|
|
848
601
|
assignUserRoles(data) {
|
|
849
602
|
return this.http.post(this.getUrl('permissions/user-roles/assign'), data);
|
|
850
603
|
}
|
|
851
|
-
/**
|
|
852
|
-
* Get user's role assignments
|
|
853
|
-
* POST /iam/permissions/get-user-roles
|
|
854
|
-
*/
|
|
855
604
|
getUserRoles(userId, query) {
|
|
856
605
|
return this.http.post(this.getUrl('permissions/get-user-roles'), { userId, branchId: query?.branchId });
|
|
857
606
|
}
|
|
858
|
-
// =============================================================================
|
|
859
|
-
// Role → Action (Role Permissions)
|
|
860
|
-
// =============================================================================
|
|
861
|
-
/**
|
|
862
|
-
* Assign/remove actions to/from role
|
|
863
|
-
* POST /permissions/role-actions/assign
|
|
864
|
-
*/
|
|
865
607
|
assignRoleActions(data) {
|
|
866
608
|
return this.http.post(this.getUrl('permissions/role-actions/assign'), data);
|
|
867
609
|
}
|
|
868
|
-
|
|
869
|
-
* Get role's action permissions
|
|
870
|
-
* POST /iam/permissions/get-role-actions
|
|
871
|
-
*/
|
|
872
|
-
getRoleActions(roleId, query) {
|
|
610
|
+
getRoleActions(roleId) {
|
|
873
611
|
return this.http.post(this.getUrl('permissions/get-role-actions'), { roleId });
|
|
874
612
|
}
|
|
875
|
-
// =============================================================================
|
|
876
|
-
// Company → Action (Company Whitelisting)
|
|
877
|
-
// =============================================================================
|
|
878
|
-
/**
|
|
879
|
-
* Assign/remove actions to/from company (whitelisting)
|
|
880
|
-
* POST /permissions/company-actions/assign
|
|
881
|
-
*/
|
|
882
613
|
assignCompanyActions(data) {
|
|
883
614
|
return this.http.post(this.getUrl('permissions/company-actions/assign'), data);
|
|
884
615
|
}
|
|
885
|
-
/**
|
|
886
|
-
* Get company's whitelisted actions
|
|
887
|
-
* POST /iam/permissions/get-company-actions
|
|
888
|
-
*/
|
|
889
616
|
getCompanyActions(companyId) {
|
|
890
617
|
return this.http.post(this.getUrl('permissions/get-company-actions'), { companyId });
|
|
891
618
|
}
|
|
@@ -894,9 +621,7 @@ class PermissionApiService extends BaseApiService {
|
|
|
894
621
|
}
|
|
895
622
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PermissionApiService, decorators: [{
|
|
896
623
|
type: Injectable,
|
|
897
|
-
args: [{
|
|
898
|
-
providedIn: 'root',
|
|
899
|
-
}]
|
|
624
|
+
args: [{ providedIn: 'root' }]
|
|
900
625
|
}], ctorParameters: () => [] });
|
|
901
626
|
|
|
902
627
|
/**
|
|
@@ -931,47 +656,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
931
656
|
}]
|
|
932
657
|
}], ctorParameters: () => [] });
|
|
933
658
|
|
|
934
|
-
/**
|
|
935
|
-
* Permission State Service
|
|
936
|
-
* Manages user permissions state and provides permission checking methods
|
|
937
|
-
*
|
|
938
|
-
* Uses shared PermissionValidatorService for centralized permission checking.
|
|
939
|
-
*
|
|
940
|
-
* @example
|
|
941
|
-
* ```typescript
|
|
942
|
-
* // In component
|
|
943
|
-
* readonly permissionState = inject(PermissionStateService);
|
|
944
|
-
*
|
|
945
|
-
* ngOnInit() {
|
|
946
|
-
* this.permissionState.loadPermissions();
|
|
947
|
-
* }
|
|
948
|
-
*
|
|
949
|
-
* // Check permission
|
|
950
|
-
* if (this.permissionState.hasAction('user.create')) {
|
|
951
|
-
* // Show create button
|
|
952
|
-
* }
|
|
953
|
-
* ```
|
|
954
|
-
*/
|
|
955
659
|
class PermissionStateService {
|
|
956
660
|
permissionApi = inject(MyPermissionsApiService);
|
|
957
661
|
permissionValidator = inject(PermissionValidatorService);
|
|
958
|
-
// Permission state
|
|
959
662
|
_permissions = signal(null, ...(ngDevMode ? [{ debugName: "_permissions" }] : []));
|
|
960
663
|
permissions = this._permissions.asReadonly();
|
|
961
|
-
// Loading state
|
|
962
664
|
_isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : []));
|
|
963
665
|
isLoading = this._isLoading.asReadonly();
|
|
964
|
-
/**
|
|
965
|
-
* Load current user's permissions from API
|
|
966
|
-
* Call this on app initialization or after login
|
|
967
|
-
* Returns Observable for reactive composition
|
|
968
|
-
*/
|
|
969
666
|
loadPermissions(dto) {
|
|
970
667
|
this._isLoading.set(true);
|
|
971
668
|
return this.permissionApi.getMyPermissions(dto).pipe(tap((response) => {
|
|
972
669
|
this._permissions.set(response.data ?? null);
|
|
973
670
|
this._isLoading.set(false);
|
|
974
|
-
// Update shared permission validator
|
|
975
671
|
const actionCodes = response.data?.frontendActions.map((a) => a.code) ?? [];
|
|
976
672
|
this.permissionValidator.setPermissions(actionCodes);
|
|
977
673
|
}), catchError(() => {
|
|
@@ -981,14 +677,8 @@ class PermissionStateService {
|
|
|
981
677
|
return of(void 0);
|
|
982
678
|
}), map(() => void 0));
|
|
983
679
|
}
|
|
984
|
-
/**
|
|
985
|
-
* Check if permissions are loaded
|
|
986
|
-
*
|
|
987
|
-
* @returns true if permissions are loaded
|
|
988
|
-
*/
|
|
989
680
|
isLoaded() {
|
|
990
|
-
return
|
|
991
|
-
this.permissionValidator.isPermissionsLoaded());
|
|
681
|
+
return this._permissions() !== null && this.permissionValidator.isPermissionsLoaded();
|
|
992
682
|
}
|
|
993
683
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PermissionStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
994
684
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PermissionStateService, providedIn: 'root' });
|
|
@@ -1012,26 +702,28 @@ function toLogicNode(node) {
|
|
|
1012
702
|
}
|
|
1013
703
|
function toBuilderNode(node) {
|
|
1014
704
|
if (node.type === 'action') {
|
|
1015
|
-
return
|
|
705
|
+
return createActionNode(node.actionId);
|
|
1016
706
|
}
|
|
1017
707
|
return {
|
|
1018
|
-
|
|
1019
|
-
type: 'group',
|
|
1020
|
-
operator: node.operator,
|
|
708
|
+
...createGroupNode(node.operator),
|
|
1021
709
|
children: node.children?.map(toBuilderNode) ?? [],
|
|
1022
710
|
};
|
|
1023
711
|
}
|
|
712
|
+
function createGroupNode(operator = 'AND') {
|
|
713
|
+
return { id: crypto.randomUUID(), type: 'group', operator, children: [] };
|
|
714
|
+
}
|
|
715
|
+
function createActionNode(actionId = '') {
|
|
716
|
+
return { id: crypto.randomUUID(), type: 'action', actionId };
|
|
717
|
+
}
|
|
1024
718
|
/** Visual builder for AND/OR permission logic trees */
|
|
1025
719
|
class LogicBuilderComponent {
|
|
1026
720
|
logic = input(null, ...(ngDevMode ? [{ debugName: "logic" }] : []));
|
|
1027
721
|
actions = input([], ...(ngDevMode ? [{ debugName: "actions" }] : []));
|
|
1028
722
|
logicChange = output();
|
|
1029
|
-
availableActions = computed(() => this.actions(), ...(ngDevMode ? [{ debugName: "availableActions" }] : []));
|
|
1030
723
|
/** Internal builder tree state (private writable + public readonly pattern) */
|
|
1031
724
|
_builderTree = signal(null, ...(ngDevMode ? [{ debugName: "_builderTree" }] : []));
|
|
1032
725
|
builderLogic = this._builderTree.asReadonly();
|
|
1033
726
|
constructor() {
|
|
1034
|
-
// Sync builderTree with logic input changes
|
|
1035
727
|
effect(() => {
|
|
1036
728
|
const logic = this.logic();
|
|
1037
729
|
if (!logic) {
|
|
@@ -1043,40 +735,24 @@ class LogicBuilderComponent {
|
|
|
1043
735
|
}, { allowSignalWrites: true });
|
|
1044
736
|
}
|
|
1045
737
|
initializeLogic() {
|
|
1046
|
-
this.
|
|
1047
|
-
id: crypto.randomUUID(),
|
|
1048
|
-
type: 'group',
|
|
1049
|
-
operator: 'AND',
|
|
1050
|
-
children: [],
|
|
1051
|
-
});
|
|
1052
|
-
this.emitChange();
|
|
738
|
+
this.updateTreeAndEmit(createGroupNode());
|
|
1053
739
|
}
|
|
1054
740
|
clearLogic() {
|
|
1055
741
|
this._builderTree.set(null);
|
|
1056
742
|
this.logicChange.emit(null);
|
|
1057
743
|
}
|
|
1058
744
|
toggleOperator(nodeId) {
|
|
1059
|
-
|
|
1060
|
-
if (!tree)
|
|
1061
|
-
return;
|
|
1062
|
-
this._builderTree.set(this.updateNodeInTree(tree, nodeId, (node) => ({
|
|
745
|
+
this.updateNode(nodeId, (node) => ({
|
|
1063
746
|
...node,
|
|
1064
747
|
operator: node.operator === 'AND' ? 'OR' : 'AND',
|
|
1065
|
-
}))
|
|
1066
|
-
this.emitChange();
|
|
748
|
+
}));
|
|
1067
749
|
}
|
|
1068
750
|
addChildNode(parentId, type) {
|
|
1069
|
-
const
|
|
1070
|
-
|
|
1071
|
-
return;
|
|
1072
|
-
const newNode = type === 'group'
|
|
1073
|
-
? { id: crypto.randomUUID(), type: 'group', operator: 'AND', children: [] }
|
|
1074
|
-
: { id: crypto.randomUUID(), type: 'action', actionId: '' };
|
|
1075
|
-
this._builderTree.set(this.updateNodeInTree(tree, parentId, (node) => ({
|
|
751
|
+
const newNode = type === 'group' ? createGroupNode() : createActionNode();
|
|
752
|
+
this.updateNode(parentId, (node) => ({
|
|
1076
753
|
...node,
|
|
1077
754
|
children: [...(node.children || []), newNode],
|
|
1078
|
-
}))
|
|
1079
|
-
this.emitChange();
|
|
755
|
+
}));
|
|
1080
756
|
}
|
|
1081
757
|
removeNode(nodeId) {
|
|
1082
758
|
const tree = this._builderTree();
|
|
@@ -1086,48 +762,42 @@ class LogicBuilderComponent {
|
|
|
1086
762
|
this.clearLogic();
|
|
1087
763
|
return;
|
|
1088
764
|
}
|
|
1089
|
-
this.
|
|
1090
|
-
this.emitChange();
|
|
765
|
+
this.updateTreeAndEmit(this.removeNodeFromTree(tree, nodeId));
|
|
1091
766
|
}
|
|
1092
767
|
updateActionId(nodeId, actionId) {
|
|
768
|
+
this.updateNode(nodeId, (node) => ({ ...node, actionId }));
|
|
769
|
+
}
|
|
770
|
+
/** Updates a node in the tree and emits the change */
|
|
771
|
+
updateNode(nodeId, updater) {
|
|
1093
772
|
const tree = this._builderTree();
|
|
1094
773
|
if (!tree)
|
|
1095
774
|
return;
|
|
1096
|
-
this.
|
|
1097
|
-
...node,
|
|
1098
|
-
actionId,
|
|
1099
|
-
})));
|
|
1100
|
-
this.emitChange();
|
|
775
|
+
this.updateTreeAndEmit(this.updateNodeInTree(tree, nodeId, updater));
|
|
1101
776
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
this.logicChange.emit(null);
|
|
1106
|
-
return;
|
|
1107
|
-
}
|
|
777
|
+
/** Sets the tree and emits the change */
|
|
778
|
+
updateTreeAndEmit(tree) {
|
|
779
|
+
this._builderTree.set(tree);
|
|
1108
780
|
this.logicChange.emit(toLogicNode(tree));
|
|
1109
781
|
}
|
|
1110
782
|
updateNodeInTree(node, targetId, updater) {
|
|
1111
783
|
if (node.id === targetId)
|
|
1112
784
|
return updater(node);
|
|
1113
|
-
if (node.children)
|
|
1114
|
-
return
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
}
|
|
1119
|
-
return node;
|
|
785
|
+
if (!node.children)
|
|
786
|
+
return node;
|
|
787
|
+
return {
|
|
788
|
+
...node,
|
|
789
|
+
children: node.children.map((child) => this.updateNodeInTree(child, targetId, updater)),
|
|
790
|
+
};
|
|
1120
791
|
}
|
|
1121
792
|
removeNodeFromTree(node, targetId) {
|
|
1122
|
-
if (node.children)
|
|
1123
|
-
return
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
}
|
|
1130
|
-
return node;
|
|
793
|
+
if (!node.children)
|
|
794
|
+
return node;
|
|
795
|
+
return {
|
|
796
|
+
...node,
|
|
797
|
+
children: node.children
|
|
798
|
+
.filter((child) => child.id !== targetId)
|
|
799
|
+
.map((child) => this.removeNodeFromTree(child, targetId)),
|
|
800
|
+
};
|
|
1131
801
|
}
|
|
1132
802
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LogicBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1133
803
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: LogicBuilderComponent, isStandalone: true, selector: "lib-logic-builder", inputs: { logic: { classPropertyName: "logic", publicName: "logic", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { logicChange: "logicChange" }, ngImport: i0, template: `
|
|
@@ -1190,8 +860,8 @@ class LogicBuilderComponent {
|
|
|
1190
860
|
class="action-select flex-1 p-2 border border-surface rounded text-sm"
|
|
1191
861
|
[ngModel]="node.actionId"
|
|
1192
862
|
(ngModelChange)="updateActionId(node.id, $event)">
|
|
1193
|
-
<option [value]="null">Select Action... ({{
|
|
1194
|
-
@for (action of
|
|
863
|
+
<option [value]="null">Select Action... ({{ actions().length }} available)</option>
|
|
864
|
+
@for (action of actions(); track action.id) {
|
|
1195
865
|
<option [ngValue]="action.id">{{ action.name }}</option>
|
|
1196
866
|
}
|
|
1197
867
|
</select>
|
|
@@ -1245,7 +915,7 @@ class LogicBuilderComponent {
|
|
|
1245
915
|
}
|
|
1246
916
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LogicBuilderComponent, decorators: [{
|
|
1247
917
|
type: Component,
|
|
1248
|
-
args: [{ selector: 'lib-logic-builder',
|
|
918
|
+
args: [{ selector: 'lib-logic-builder', imports: [AngularModule, PrimeModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1249
919
|
<div class="logic-builder">
|
|
1250
920
|
<div class="flex justify-between items-center mb-3">
|
|
1251
921
|
<h4 class="text-sm font-semibold m-0">Permission Logic</h4>
|
|
@@ -1305,8 +975,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
1305
975
|
class="action-select flex-1 p-2 border border-surface rounded text-sm"
|
|
1306
976
|
[ngModel]="node.actionId"
|
|
1307
977
|
(ngModelChange)="updateActionId(node.id, $event)">
|
|
1308
|
-
<option [value]="null">Select Action... ({{
|
|
1309
|
-
@for (action of
|
|
978
|
+
<option [value]="null">Select Action... ({{ actions().length }} available)</option>
|
|
979
|
+
@for (action of actions(); track action.id) {
|
|
1310
980
|
<option [ngValue]="action.id">{{ action.name }}</option>
|
|
1311
981
|
}
|
|
1312
982
|
</select>
|
|
@@ -1359,102 +1029,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
1359
1029
|
`, styles: [":host{display:block}.node-type{display:inline-flex;align-items:center;padding:.25rem .5rem;border-radius:.25rem;font-weight:600;font-size:.75rem;text-transform:uppercase}.node-type.group{background-color:var(--p-blue-100, #dbeafe);color:var(--p-blue-700, #1e40af)}.node-type.action{background-color:var(--p-green-100, #d1fae5);color:var(--p-green-700, #065f46)}:host-context(.p-dark) .node-type.group{background-color:#3b82f633;color:var(--p-blue-400, #60a5fa)}:host-context(.p-dark) .node-type.action{background-color:#22c55e33;color:var(--p-green-400, #4ade80)}.operator-badge{display:inline-flex;align-items:center;padding:.125rem .5rem;border-radius:9999px;font-weight:600;font-size:.75rem;cursor:pointer;transition:opacity .2s}.operator-badge.and{background-color:var(--p-yellow-100, #fef3c7);color:var(--p-yellow-700, #92400e)}.operator-badge.or{background-color:var(--p-indigo-100, #e0e7ff);color:var(--p-indigo-700, #3730a3)}:host-context(.p-dark) .operator-badge.and{background-color:#eab30833;color:var(--p-yellow-400, #facc15)}:host-context(.p-dark) .operator-badge.or{background-color:#6366f133;color:var(--p-indigo-400, #818cf8)}.operator-badge:hover{opacity:.8}.action-select{background-color:var(--p-surface-0, #ffffff);color:var(--p-text-color, #1f2937)}:host-context(.p-dark) .action-select{background-color:var(--p-surface-900, #1f2937);color:var(--p-text-color, #f9fafb)}.action-select:focus{outline:none;border-color:var(--p-primary-color, #3b82f6)}\n"] }]
|
|
1360
1030
|
}], ctorParameters: () => [], propDecorators: { logic: [{ type: i0.Input, args: [{ isSignal: true, alias: "logic", required: false }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], logicChange: [{ type: i0.Output, args: ["logicChange"] }] } });
|
|
1361
1031
|
|
|
1362
|
-
/**
|
|
1363
|
-
* Tree Utility Functions
|
|
1364
|
-
* Shared utilities for working with hierarchical tree structures
|
|
1365
|
-
*/
|
|
1366
|
-
/**
|
|
1367
|
-
* Flattens a hierarchical tree structure into a flat array
|
|
1368
|
-
*
|
|
1369
|
-
* @template T - Type of tree node (must have optional children array)
|
|
1370
|
-
* @param tree - Array of tree nodes to flatten
|
|
1371
|
-
* @returns Flat array containing all nodes from the tree
|
|
1372
|
-
*
|
|
1373
|
-
* @example
|
|
1374
|
-
* ```typescript
|
|
1375
|
-
* const tree = [
|
|
1376
|
-
* { id: '1', name: 'Parent', children: [
|
|
1377
|
-
* { id: '2', name: 'Child 1' },
|
|
1378
|
-
* { id: '3', name: 'Child 2' }
|
|
1379
|
-
* ]}
|
|
1380
|
-
* ];
|
|
1381
|
-
* const flat = flattenTree(tree);
|
|
1382
|
-
* // Returns: [{ id: '1', ... }, { id: '2', ... }, { id: '3', ... }]
|
|
1383
|
-
* ```
|
|
1384
|
-
*/
|
|
1385
1032
|
function flattenTree(tree) {
|
|
1386
|
-
if (!tree?.length)
|
|
1033
|
+
if (!tree?.length)
|
|
1387
1034
|
return [];
|
|
1388
|
-
}
|
|
1389
1035
|
const result = [];
|
|
1390
1036
|
const flatten = (nodes) => {
|
|
1391
1037
|
for (const node of nodes) {
|
|
1392
1038
|
result.push(node);
|
|
1393
|
-
if (node.children?.length)
|
|
1039
|
+
if (node.children?.length)
|
|
1394
1040
|
flatten(node.children);
|
|
1395
|
-
}
|
|
1396
1041
|
}
|
|
1397
1042
|
};
|
|
1398
1043
|
flatten(tree);
|
|
1399
1044
|
return result;
|
|
1400
1045
|
}
|
|
1401
|
-
/**
|
|
1402
|
-
* Builds a map of items by their ID for quick lookup
|
|
1403
|
-
*
|
|
1404
|
-
* @template T - Type of item (must have id property)
|
|
1405
|
-
* @param items - Array of items to map
|
|
1406
|
-
* @returns Map with item IDs as keys and items as values
|
|
1407
|
-
*
|
|
1408
|
-
* @example
|
|
1409
|
-
* ```typescript
|
|
1410
|
-
* const actions = [{ id: '1', name: 'Action 1' }, { id: '2', name: 'Action 2' }];
|
|
1411
|
-
* const map = buildItemMap(actions);
|
|
1412
|
-
* const action = map.get('1'); // { id: '1', name: 'Action 1' }
|
|
1413
|
-
* ```
|
|
1414
|
-
*/
|
|
1415
|
-
function buildItemMap(items) {
|
|
1416
|
-
if (!items?.length) {
|
|
1417
|
-
return new Map();
|
|
1418
|
-
}
|
|
1419
|
-
return new Map(items
|
|
1420
|
-
.filter((item) => item?.id !== undefined && item?.id !== null)
|
|
1421
|
-
.map((item) => [String(item.id), item]));
|
|
1422
|
-
}
|
|
1423
|
-
/**
|
|
1424
|
-
* Build tree structure from flat list using parentId
|
|
1425
|
-
*
|
|
1426
|
-
* Converts flat array with parentId references into hierarchical tree structure.
|
|
1427
|
-
* Used when backend returns flat list but tree structure is needed for display.
|
|
1428
|
-
*
|
|
1429
|
-
* @template T - Type of node (must have id and optional parentId)
|
|
1430
|
-
* @param flatList - Flat array of nodes with parentId references
|
|
1431
|
-
* @returns Hierarchical tree with children arrays
|
|
1432
|
-
*
|
|
1433
|
-
* @example
|
|
1434
|
-
* ```typescript
|
|
1435
|
-
* const flat = [
|
|
1436
|
-
* { id: '1', name: 'Parent', parentId: null },
|
|
1437
|
-
* { id: '2', name: 'Child 1', parentId: '1' },
|
|
1438
|
-
* { id: '3', name: 'Child 2', parentId: '1' }
|
|
1439
|
-
* ];
|
|
1440
|
-
* const tree = buildTreeFromFlat(flat);
|
|
1441
|
-
* // Returns: [{ id: '1', name: 'Parent', children: [child1, child2] }]
|
|
1442
|
-
* ```
|
|
1443
|
-
*/
|
|
1444
1046
|
function buildTreeFromFlat(flatList) {
|
|
1445
|
-
if (!flatList?.length)
|
|
1047
|
+
if (!flatList?.length)
|
|
1446
1048
|
return [];
|
|
1447
|
-
}
|
|
1448
|
-
// Create map for O(1) lookup
|
|
1449
1049
|
const itemMap = new Map();
|
|
1450
1050
|
const rootItems = [];
|
|
1451
|
-
// First pass: Create all nodes with empty children arrays
|
|
1452
1051
|
flatList.forEach((item) => {
|
|
1453
|
-
if (item.id)
|
|
1052
|
+
if (item.id)
|
|
1454
1053
|
itemMap.set(item.id, { ...item, children: [] });
|
|
1455
|
-
}
|
|
1456
1054
|
});
|
|
1457
|
-
// Second pass: Build parent-child relationships
|
|
1458
1055
|
flatList.forEach((item) => {
|
|
1459
1056
|
if (!item.id)
|
|
1460
1057
|
return;
|
|
@@ -1462,51 +1059,23 @@ function buildTreeFromFlat(flatList) {
|
|
|
1462
1059
|
if (!node)
|
|
1463
1060
|
return;
|
|
1464
1061
|
if (!item.parentId) {
|
|
1465
|
-
// Root level item
|
|
1466
1062
|
rootItems.push(node);
|
|
1467
1063
|
}
|
|
1468
1064
|
else {
|
|
1469
|
-
// Child item - add to parent's children
|
|
1470
1065
|
const parent = itemMap.get(item.parentId);
|
|
1471
1066
|
if (parent) {
|
|
1472
1067
|
parent.children.push(node);
|
|
1473
1068
|
}
|
|
1474
1069
|
else {
|
|
1475
|
-
// Parent not found - treat as root
|
|
1476
1070
|
rootItems.push(node);
|
|
1477
1071
|
}
|
|
1478
1072
|
}
|
|
1479
1073
|
});
|
|
1480
1074
|
return rootItems;
|
|
1481
1075
|
}
|
|
1482
|
-
/**
|
|
1483
|
-
* Convert ActionTreeDto to PrimeNG TreeNode format
|
|
1484
|
-
*
|
|
1485
|
-
* Transforms hierarchical action data from backend into PrimeNG TreeTable format.
|
|
1486
|
-
* Recursively processes children and sets leaf/expanded properties.
|
|
1487
|
-
*
|
|
1488
|
-
* @param actions - Array of action tree DTOs from backend
|
|
1489
|
-
* @returns Array of TreeNodes for PrimeNG TreeTable
|
|
1490
|
-
*
|
|
1491
|
-
* @example
|
|
1492
|
-
* ```typescript
|
|
1493
|
-
* const actionsTree = [
|
|
1494
|
-
* {
|
|
1495
|
-
* id: '1',
|
|
1496
|
-
* name: 'User Management',
|
|
1497
|
-
* children: [
|
|
1498
|
-
* { id: '2', name: 'Create User', children: [] }
|
|
1499
|
-
* ]
|
|
1500
|
-
* }
|
|
1501
|
-
* ];
|
|
1502
|
-
* const treeNodes = convertActionToTreeNode(actionsTree);
|
|
1503
|
-
* // Use with: <p-treeTable [value]="treeNodes">
|
|
1504
|
-
* ```
|
|
1505
|
-
*/
|
|
1506
1076
|
function convertActionToTreeNode(actions) {
|
|
1507
|
-
if (!actions?.length)
|
|
1077
|
+
if (!actions?.length)
|
|
1508
1078
|
return [];
|
|
1509
|
-
}
|
|
1510
1079
|
const convert = (action) => {
|
|
1511
1080
|
const { children, ...actionData } = action;
|
|
1512
1081
|
return {
|
|
@@ -1606,17 +1175,14 @@ class RoleActionSelectorComponent {
|
|
|
1606
1175
|
const selMap = this.selectionMap();
|
|
1607
1176
|
const allActions = this.actions();
|
|
1608
1177
|
const unmetSet = new Set();
|
|
1609
|
-
|
|
1610
|
-
if (action.id &&
|
|
1611
|
-
this.permissionLogic.hasUnmetPrerequisites(action, selMap, allActions)) {
|
|
1178
|
+
for (const action of allActions) {
|
|
1179
|
+
if (action.id && this.permissionLogic.hasUnmetPrerequisites(action, selMap, allActions)) {
|
|
1612
1180
|
unmetSet.add(action.id);
|
|
1613
1181
|
}
|
|
1614
|
-
}
|
|
1182
|
+
}
|
|
1615
1183
|
return unmetSet;
|
|
1616
1184
|
}, ...(ngDevMode ? [{ debugName: "actionsWithUnmetPrerequisites" }] : []));
|
|
1617
|
-
invalidActionsCount = computed(() => {
|
|
1618
|
-
return this.actionsWithUnmetPrerequisites().size;
|
|
1619
|
-
}, ...(ngDevMode ? [{ debugName: "invalidActionsCount" }] : []));
|
|
1185
|
+
invalidActionsCount = computed(() => this.actionsWithUnmetPrerequisites().size, ...(ngDevMode ? [{ debugName: "invalidActionsCount" }] : []));
|
|
1620
1186
|
// Computed - Pending Changes
|
|
1621
1187
|
pendingAdd = computed(() => {
|
|
1622
1188
|
const current = this.selectionMap();
|
|
@@ -1701,13 +1267,12 @@ class RoleActionSelectorComponent {
|
|
|
1701
1267
|
const assignedIds = new Set((assignedResponse?.data || []).map((a) => a.actionId));
|
|
1702
1268
|
// Build selection map
|
|
1703
1269
|
const selMap = {};
|
|
1704
|
-
|
|
1270
|
+
for (const action of flatActions) {
|
|
1705
1271
|
if (action.id) {
|
|
1706
1272
|
selMap[action.id] = assignedIds.has(action.id);
|
|
1707
1273
|
}
|
|
1708
|
-
}
|
|
1709
|
-
this.
|
|
1710
|
-
this._initialSelection.set({ ...selMap });
|
|
1274
|
+
}
|
|
1275
|
+
this.applySelection(selMap);
|
|
1711
1276
|
}
|
|
1712
1277
|
finally {
|
|
1713
1278
|
if (!signal.aborted) {
|
|
@@ -1756,35 +1321,20 @@ class RoleActionSelectorComponent {
|
|
|
1756
1321
|
this.permissionLogic.handleCheck(action, this.selectionMap(), this.actions(), (newMap) => this._selectionMap.set(newMap), (previousState) => this._selectionMap.set(previousState));
|
|
1757
1322
|
}
|
|
1758
1323
|
}
|
|
1759
|
-
/**
|
|
1760
|
-
* Toggle all actions
|
|
1761
|
-
*/
|
|
1762
1324
|
toggleAll() {
|
|
1763
|
-
|
|
1764
|
-
const selMap = {};
|
|
1765
|
-
this.actions().forEach((action) => {
|
|
1766
|
-
selMap[action.id] = newValue;
|
|
1767
|
-
});
|
|
1768
|
-
this._selectionMap.set(selMap);
|
|
1325
|
+
this.setAllSelection(!this.allSelected());
|
|
1769
1326
|
}
|
|
1770
|
-
/**
|
|
1771
|
-
* Select all actions
|
|
1772
|
-
*/
|
|
1773
1327
|
selectAll() {
|
|
1774
|
-
|
|
1775
|
-
this.actions().forEach((action) => {
|
|
1776
|
-
selMap[action.id] = true;
|
|
1777
|
-
});
|
|
1778
|
-
this._selectionMap.set(selMap);
|
|
1328
|
+
this.setAllSelection(true);
|
|
1779
1329
|
}
|
|
1780
|
-
/**
|
|
1781
|
-
* Deselect all actions
|
|
1782
|
-
*/
|
|
1783
1330
|
deselectAll() {
|
|
1331
|
+
this.setAllSelection(false);
|
|
1332
|
+
}
|
|
1333
|
+
setAllSelection(value) {
|
|
1784
1334
|
const selMap = {};
|
|
1785
|
-
this.actions()
|
|
1786
|
-
selMap[action.id] =
|
|
1787
|
-
}
|
|
1335
|
+
for (const action of this.actions()) {
|
|
1336
|
+
selMap[action.id] = value;
|
|
1337
|
+
}
|
|
1788
1338
|
this._selectionMap.set(selMap);
|
|
1789
1339
|
}
|
|
1790
1340
|
/**
|
|
@@ -1801,19 +1351,7 @@ class RoleActionSelectorComponent {
|
|
|
1801
1351
|
return;
|
|
1802
1352
|
}
|
|
1803
1353
|
// Build payload
|
|
1804
|
-
const items =
|
|
1805
|
-
this.pendingAdd().forEach((action) => {
|
|
1806
|
-
items.push({
|
|
1807
|
-
id: action.id,
|
|
1808
|
-
action: 'add',
|
|
1809
|
-
});
|
|
1810
|
-
});
|
|
1811
|
-
this.pendingRemove().forEach((action) => {
|
|
1812
|
-
items.push({
|
|
1813
|
-
id: action.id,
|
|
1814
|
-
action: 'remove',
|
|
1815
|
-
});
|
|
1816
|
-
});
|
|
1354
|
+
const items = this.buildPayloadItems();
|
|
1817
1355
|
if (items.length === 0)
|
|
1818
1356
|
return;
|
|
1819
1357
|
this.saving.set(true);
|
|
@@ -1837,9 +1375,20 @@ class RoleActionSelectorComponent {
|
|
|
1837
1375
|
this.saving.set(false);
|
|
1838
1376
|
}
|
|
1839
1377
|
}
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1378
|
+
applySelection(selMap) {
|
|
1379
|
+
this._selectionMap.set(selMap);
|
|
1380
|
+
this._initialSelection.set({ ...selMap });
|
|
1381
|
+
}
|
|
1382
|
+
buildPayloadItems() {
|
|
1383
|
+
const items = [];
|
|
1384
|
+
for (const action of this.pendingAdd()) {
|
|
1385
|
+
items.push({ id: action.id, action: 'add' });
|
|
1386
|
+
}
|
|
1387
|
+
for (const action of this.pendingRemove()) {
|
|
1388
|
+
items.push({ id: action.id, action: 'remove' });
|
|
1389
|
+
}
|
|
1390
|
+
return items;
|
|
1391
|
+
}
|
|
1843
1392
|
resetState() {
|
|
1844
1393
|
this._actionsTree.set([]);
|
|
1845
1394
|
this._actions.set([]);
|
|
@@ -2072,7 +1621,7 @@ class RoleActionSelectorComponent {
|
|
|
2072
1621
|
}
|
|
2073
1622
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoleActionSelectorComponent, decorators: [{
|
|
2074
1623
|
type: Component,
|
|
2075
|
-
args: [{ selector: 'flusys-role-action-selector',
|
|
1624
|
+
args: [{ selector: 'flusys-role-action-selector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective], template: `
|
|
2076
1625
|
<div class="role-action-selector">
|
|
2077
1626
|
<!-- Role Selector -->
|
|
2078
1627
|
<div class="mb-4">
|
|
@@ -2384,17 +1933,14 @@ class CompanyActionSelectorComponent {
|
|
|
2384
1933
|
const selMap = this.selectionMap();
|
|
2385
1934
|
const allActions = this.actions();
|
|
2386
1935
|
const unmetSet = new Set();
|
|
2387
|
-
|
|
2388
|
-
if (action.id &&
|
|
2389
|
-
this.permissionLogic.hasUnmetPrerequisites(action, selMap, allActions)) {
|
|
1936
|
+
for (const action of allActions) {
|
|
1937
|
+
if (action.id && this.permissionLogic.hasUnmetPrerequisites(action, selMap, allActions)) {
|
|
2390
1938
|
unmetSet.add(action.id);
|
|
2391
1939
|
}
|
|
2392
|
-
}
|
|
1940
|
+
}
|
|
2393
1941
|
return unmetSet;
|
|
2394
1942
|
}, ...(ngDevMode ? [{ debugName: "actionsWithUnmetPrerequisites" }] : []));
|
|
2395
|
-
invalidActionsCount = computed(() => {
|
|
2396
|
-
return this.actionsWithUnmetPrerequisites().size;
|
|
2397
|
-
}, ...(ngDevMode ? [{ debugName: "invalidActionsCount" }] : []));
|
|
1943
|
+
invalidActionsCount = computed(() => this.actionsWithUnmetPrerequisites().size, ...(ngDevMode ? [{ debugName: "invalidActionsCount" }] : []));
|
|
2398
1944
|
// Computed - Pending Changes
|
|
2399
1945
|
pendingAdd = computed(() => {
|
|
2400
1946
|
const current = this.selectionMap();
|
|
@@ -2478,13 +2024,7 @@ class CompanyActionSelectorComponent {
|
|
|
2478
2024
|
if (signal.aborted)
|
|
2479
2025
|
return;
|
|
2480
2026
|
const assignedIds = new Set((assignedResponse?.data || []).map((a) => a.actionId));
|
|
2481
|
-
|
|
2482
|
-
const selMap = {};
|
|
2483
|
-
actionsList.forEach((action) => {
|
|
2484
|
-
if (action.id) {
|
|
2485
|
-
selMap[action.id] = assignedIds.has(action.id);
|
|
2486
|
-
}
|
|
2487
|
-
});
|
|
2027
|
+
const selMap = this.buildSelectionMap(actionsList, (action) => assignedIds.has(action.id));
|
|
2488
2028
|
this._selectionMap.set(selMap);
|
|
2489
2029
|
this._initialSelection.set({ ...selMap });
|
|
2490
2030
|
}
|
|
@@ -2535,36 +2075,17 @@ class CompanyActionSelectorComponent {
|
|
|
2535
2075
|
this.permissionLogic.handleCheck(action, this.selectionMap(), this.actions(), (newMap) => this._selectionMap.set(newMap), (previousState) => this._selectionMap.set(previousState));
|
|
2536
2076
|
}
|
|
2537
2077
|
}
|
|
2538
|
-
/**
|
|
2539
|
-
* Toggle all actions
|
|
2540
|
-
*/
|
|
2541
2078
|
toggleAll() {
|
|
2542
|
-
|
|
2543
|
-
const selMap = {};
|
|
2544
|
-
this.actions().forEach((action) => {
|
|
2545
|
-
selMap[action.id] = newValue;
|
|
2546
|
-
});
|
|
2547
|
-
this._selectionMap.set(selMap);
|
|
2079
|
+
this.setAllSelection(!this.allSelected());
|
|
2548
2080
|
}
|
|
2549
|
-
/**
|
|
2550
|
-
* Select all actions
|
|
2551
|
-
*/
|
|
2552
2081
|
selectAll() {
|
|
2553
|
-
|
|
2554
|
-
this.actions().forEach((action) => {
|
|
2555
|
-
selMap[action.id] = true;
|
|
2556
|
-
});
|
|
2557
|
-
this._selectionMap.set(selMap);
|
|
2082
|
+
this.setAllSelection(true);
|
|
2558
2083
|
}
|
|
2559
|
-
/**
|
|
2560
|
-
* Deselect all actions
|
|
2561
|
-
*/
|
|
2562
2084
|
deselectAll() {
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
this._selectionMap.set(selMap);
|
|
2085
|
+
this.setAllSelection(false);
|
|
2086
|
+
}
|
|
2087
|
+
setAllSelection(value) {
|
|
2088
|
+
this._selectionMap.set(this.buildSelectionMap(this.actions(), () => value));
|
|
2568
2089
|
}
|
|
2569
2090
|
/**
|
|
2570
2091
|
* Save changes to backend
|
|
@@ -2579,20 +2100,7 @@ class CompanyActionSelectorComponent {
|
|
|
2579
2100
|
this.permissionLogic.showValidationErrorDialog(invalidActions, this.selectionMap(), this.actions(), (newMap) => this._selectionMap.set(newMap));
|
|
2580
2101
|
return;
|
|
2581
2102
|
}
|
|
2582
|
-
|
|
2583
|
-
const items = [];
|
|
2584
|
-
this.pendingAdd().forEach((action) => {
|
|
2585
|
-
items.push({
|
|
2586
|
-
id: action.id,
|
|
2587
|
-
action: 'add',
|
|
2588
|
-
});
|
|
2589
|
-
});
|
|
2590
|
-
this.pendingRemove().forEach((action) => {
|
|
2591
|
-
items.push({
|
|
2592
|
-
id: action.id,
|
|
2593
|
-
action: 'remove',
|
|
2594
|
-
});
|
|
2595
|
-
});
|
|
2103
|
+
const items = this.buildPayloadItems();
|
|
2596
2104
|
if (items.length === 0)
|
|
2597
2105
|
return;
|
|
2598
2106
|
this.saving.set(true);
|
|
@@ -2670,15 +2178,29 @@ class CompanyActionSelectorComponent {
|
|
|
2670
2178
|
},
|
|
2671
2179
|
});
|
|
2672
2180
|
}
|
|
2673
|
-
/**
|
|
2674
|
-
* Reset component state
|
|
2675
|
-
*/
|
|
2676
2181
|
resetState() {
|
|
2677
2182
|
this._actionsTree.set([]);
|
|
2678
2183
|
this._actions.set([]);
|
|
2679
2184
|
this._selectionMap.set({});
|
|
2680
2185
|
this._initialSelection.set({});
|
|
2681
2186
|
}
|
|
2187
|
+
buildSelectionMap(actions, predicate) {
|
|
2188
|
+
const selMap = {};
|
|
2189
|
+
for (const action of actions) {
|
|
2190
|
+
selMap[action.id] = predicate(action);
|
|
2191
|
+
}
|
|
2192
|
+
return selMap;
|
|
2193
|
+
}
|
|
2194
|
+
buildPayloadItems() {
|
|
2195
|
+
const items = [];
|
|
2196
|
+
for (const action of this.pendingAdd()) {
|
|
2197
|
+
items.push({ id: action.id, action: 'add' });
|
|
2198
|
+
}
|
|
2199
|
+
for (const action of this.pendingRemove()) {
|
|
2200
|
+
items.push({ id: action.id, action: 'remove' });
|
|
2201
|
+
}
|
|
2202
|
+
return items;
|
|
2203
|
+
}
|
|
2682
2204
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CompanyActionSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2683
2205
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: CompanyActionSelectorComponent, isStandalone: true, selector: "flusys-company-action-selector", ngImport: i0, template: `
|
|
2684
2206
|
<div class="company-action-selector">
|
|
@@ -2899,7 +2421,7 @@ class CompanyActionSelectorComponent {
|
|
|
2899
2421
|
}
|
|
2900
2422
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CompanyActionSelectorComponent, decorators: [{
|
|
2901
2423
|
type: Component,
|
|
2902
|
-
args: [{ selector: 'flusys-company-action-selector',
|
|
2424
|
+
args: [{ selector: 'flusys-company-action-selector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective], template: `
|
|
2903
2425
|
<div class="company-action-selector">
|
|
2904
2426
|
<!-- Company Selector -->
|
|
2905
2427
|
<div class="mb-4">
|
|
@@ -3157,9 +2679,6 @@ class UserRoleSelectorComponent {
|
|
|
3157
2679
|
roleApi = inject(RoleApiService);
|
|
3158
2680
|
permissionApi = inject(PermissionApiService);
|
|
3159
2681
|
messageService = inject(MessageService);
|
|
3160
|
-
destroyRef = inject(DestroyRef);
|
|
3161
|
-
// AbortController for data loading
|
|
3162
|
-
loadDataAbortController = null;
|
|
3163
2682
|
// State - User/Branch Selection
|
|
3164
2683
|
selectedUserId = signal(null, ...(ngDevMode ? [{ debugName: "selectedUserId" }] : []));
|
|
3165
2684
|
selectedBranchId = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedBranchId" }] : []));
|
|
@@ -3220,10 +2739,6 @@ class UserRoleSelectorComponent {
|
|
|
3220
2739
|
return this.hasChanges() && !this.saving();
|
|
3221
2740
|
}, ...(ngDevMode ? [{ debugName: "canSave" }] : []));
|
|
3222
2741
|
constructor() {
|
|
3223
|
-
// Cleanup on destroy
|
|
3224
|
-
this.destroyRef.onDestroy(() => {
|
|
3225
|
-
this.loadDataAbortController?.abort();
|
|
3226
|
-
});
|
|
3227
2742
|
// Effect: Load user branches and data when user changes
|
|
3228
2743
|
effect(() => {
|
|
3229
2744
|
const userId = this.selectedUserId();
|
|
@@ -3260,9 +2775,7 @@ class UserRoleSelectorComponent {
|
|
|
3260
2775
|
this.branches.set([]);
|
|
3261
2776
|
return;
|
|
3262
2777
|
}
|
|
3263
|
-
const response = await this.userPermissionProvider
|
|
3264
|
-
.getUserBranchPermissions(userId)
|
|
3265
|
-
.toPromise();
|
|
2778
|
+
const response = await firstValueFrom(this.userPermissionProvider.getUserBranchPermissions(userId));
|
|
3266
2779
|
const userBranches = response?.data?.map((ub) => ({
|
|
3267
2780
|
id: ub.branchId,
|
|
3268
2781
|
name: ub.branchName,
|
|
@@ -3285,20 +2798,16 @@ class UserRoleSelectorComponent {
|
|
|
3285
2798
|
this.loading.set(true);
|
|
3286
2799
|
try {
|
|
3287
2800
|
// Load all roles
|
|
3288
|
-
const rolesResponse = await this.roleApi
|
|
3289
|
-
.getAll('', {
|
|
2801
|
+
const rolesResponse = await firstValueFrom(this.roleApi.getAll('', {
|
|
3290
2802
|
pagination: { currentPage: 0, pageSize: MAX_DROPDOWN_ITEMS },
|
|
3291
|
-
})
|
|
3292
|
-
.toPromise();
|
|
2803
|
+
}));
|
|
3293
2804
|
const rolesList = rolesResponse?.data ?? [];
|
|
3294
2805
|
this._roles.set(rolesList);
|
|
3295
2806
|
// Load existing user-role assignments
|
|
3296
2807
|
const query = isCompanyFeatureEnabled(this.appConfig) && branchId
|
|
3297
2808
|
? { branchId }
|
|
3298
2809
|
: undefined;
|
|
3299
|
-
const assignedResponse = await this.permissionApi
|
|
3300
|
-
.getUserRoles(userId, query)
|
|
3301
|
-
.toPromise();
|
|
2810
|
+
const assignedResponse = await firstValueFrom(this.permissionApi.getUserRoles(userId, query));
|
|
3302
2811
|
const assignedIds = new Set((assignedResponse?.data || []).map((r) => r.roleId));
|
|
3303
2812
|
// Build selection map
|
|
3304
2813
|
const selMap = {};
|
|
@@ -3338,30 +2847,27 @@ class UserRoleSelectorComponent {
|
|
|
3338
2847
|
* Toggle all roles
|
|
3339
2848
|
*/
|
|
3340
2849
|
toggleAll() {
|
|
3341
|
-
|
|
3342
|
-
const selMap = {};
|
|
3343
|
-
this.roles().forEach((role) => {
|
|
3344
|
-
selMap[role.id] = newValue;
|
|
3345
|
-
});
|
|
3346
|
-
this._selectionMap.set(selMap);
|
|
2850
|
+
this.setAllSelections(!this.allSelected());
|
|
3347
2851
|
}
|
|
3348
2852
|
/**
|
|
3349
2853
|
* Select all roles
|
|
3350
2854
|
*/
|
|
3351
2855
|
selectAll() {
|
|
3352
|
-
|
|
3353
|
-
this.roles().forEach((role) => {
|
|
3354
|
-
selMap[role.id] = true;
|
|
3355
|
-
});
|
|
3356
|
-
this._selectionMap.set(selMap);
|
|
2856
|
+
this.setAllSelections(true);
|
|
3357
2857
|
}
|
|
3358
2858
|
/**
|
|
3359
2859
|
* Deselect all roles
|
|
3360
2860
|
*/
|
|
3361
2861
|
deselectAll() {
|
|
2862
|
+
this.setAllSelections(false);
|
|
2863
|
+
}
|
|
2864
|
+
/**
|
|
2865
|
+
* Set all role selections to a given value
|
|
2866
|
+
*/
|
|
2867
|
+
setAllSelections(value) {
|
|
3362
2868
|
const selMap = {};
|
|
3363
2869
|
this.roles().forEach((role) => {
|
|
3364
|
-
selMap[role.id] =
|
|
2870
|
+
selMap[role.id] = value;
|
|
3365
2871
|
});
|
|
3366
2872
|
this._selectionMap.set(selMap);
|
|
3367
2873
|
}
|
|
@@ -3404,9 +2910,7 @@ class UserRoleSelectorComponent {
|
|
|
3404
2910
|
payload.branchId = branchId;
|
|
3405
2911
|
}
|
|
3406
2912
|
}
|
|
3407
|
-
const response = await this.permissionApi
|
|
3408
|
-
.assignUserRoles(payload)
|
|
3409
|
-
.toPromise();
|
|
2913
|
+
const response = await firstValueFrom(this.permissionApi.assignUserRoles(payload));
|
|
3410
2914
|
this.messageService.add({
|
|
3411
2915
|
severity: 'success',
|
|
3412
2916
|
summary: 'Success',
|
|
@@ -3654,11 +3158,11 @@ class UserRoleSelectorComponent {
|
|
|
3654
3158
|
}
|
|
3655
3159
|
}
|
|
3656
3160
|
</div>
|
|
3657
|
-
`, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i5$1.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "component", type: UserSelectComponent, selector: "lib-user-select", inputs: ["
|
|
3161
|
+
`, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i5$1.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "component", type: UserSelectComponent, selector: "lib-user-select", inputs: ["value"], outputs: ["valueChange", "userSelected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3658
3162
|
}
|
|
3659
3163
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserRoleSelectorComponent, decorators: [{
|
|
3660
3164
|
type: Component,
|
|
3661
|
-
args: [{ selector: 'flusys-user-role-selector',
|
|
3165
|
+
args: [{ selector: 'flusys-user-role-selector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective, UserSelectComponent], template: `
|
|
3662
3166
|
<div class="user-role-selector">
|
|
3663
3167
|
<!-- User and Branch Selectors -->
|
|
3664
3168
|
<div class="surface-card p-4 rounded-border mb-4 shadow-sm">
|
|
@@ -3925,9 +3429,6 @@ class UserActionSelectorComponent {
|
|
|
3925
3429
|
permissionApi = inject(PermissionApiService);
|
|
3926
3430
|
permissionLogic = inject(ActionPermissionLogicService);
|
|
3927
3431
|
messageService = inject(MessageService);
|
|
3928
|
-
destroyRef = inject(DestroyRef);
|
|
3929
|
-
// AbortController for data loading
|
|
3930
|
-
loadDataAbortController = null;
|
|
3931
3432
|
// State - User/Branch Selection
|
|
3932
3433
|
selectedUserId = signal(null, ...(ngDevMode ? [{ debugName: "selectedUserId" }] : []));
|
|
3933
3434
|
selectedBranchId = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedBranchId" }] : []));
|
|
@@ -3946,8 +3447,10 @@ class UserActionSelectorComponent {
|
|
|
3946
3447
|
selectionMap = this._selectionMap.asReadonly();
|
|
3947
3448
|
_initialSelection = signal({}, ...(ngDevMode ? [{ debugName: "_initialSelection" }] : []));
|
|
3948
3449
|
initialSelection = this._initialSelection.asReadonly();
|
|
3450
|
+
// Computed - Company Feature Flag (cached)
|
|
3451
|
+
isCompanyFeatureActive = computed(() => isCompanyFeatureEnabled(this.appConfig) === true, ...(ngDevMode ? [{ debugName: "isCompanyFeatureActive" }] : []));
|
|
3949
3452
|
// Computed - UI Flags
|
|
3950
|
-
showBranchSelector =
|
|
3453
|
+
showBranchSelector = this.isCompanyFeatureActive;
|
|
3951
3454
|
filteredBranches = computed(() => {
|
|
3952
3455
|
const currentCompanyId = this.companyContext.currentCompanyInfo()?.id || undefined;
|
|
3953
3456
|
if (!currentCompanyId)
|
|
@@ -4009,15 +3512,11 @@ class UserActionSelectorComponent {
|
|
|
4009
3512
|
return (this.hasChanges() && !this.saving() && this.invalidActionsCount() === 0);
|
|
4010
3513
|
}, ...(ngDevMode ? [{ debugName: "canSave" }] : []));
|
|
4011
3514
|
constructor() {
|
|
4012
|
-
// Cleanup on destroy
|
|
4013
|
-
this.destroyRef.onDestroy(() => {
|
|
4014
|
-
this.loadDataAbortController?.abort();
|
|
4015
|
-
});
|
|
4016
3515
|
// Effect: Load user branches and data when user changes
|
|
4017
3516
|
effect(() => {
|
|
4018
3517
|
const userId = this.selectedUserId();
|
|
4019
3518
|
if (userId) {
|
|
4020
|
-
if (
|
|
3519
|
+
if (this.isCompanyFeatureActive()) {
|
|
4021
3520
|
this.loadUserBranches(userId);
|
|
4022
3521
|
}
|
|
4023
3522
|
this.loadData();
|
|
@@ -4032,9 +3531,7 @@ class UserActionSelectorComponent {
|
|
|
4032
3531
|
effect(() => {
|
|
4033
3532
|
const userId = this.selectedUserId();
|
|
4034
3533
|
const branchId = this.selectedBranchId();
|
|
4035
|
-
if (
|
|
4036
|
-
userId &&
|
|
4037
|
-
branchId !== undefined) {
|
|
3534
|
+
if (this.isCompanyFeatureActive() && userId && branchId !== undefined) {
|
|
4038
3535
|
this.loadData();
|
|
4039
3536
|
}
|
|
4040
3537
|
});
|
|
@@ -4043,15 +3540,13 @@ class UserActionSelectorComponent {
|
|
|
4043
3540
|
* Load user's permitted branches
|
|
4044
3541
|
*/
|
|
4045
3542
|
async loadUserBranches(userId) {
|
|
3543
|
+
const companyId = this.companyContext.currentCompanyInfo()?.id;
|
|
3544
|
+
if (!companyId) {
|
|
3545
|
+
this.branches.set([]);
|
|
3546
|
+
return;
|
|
3547
|
+
}
|
|
4046
3548
|
try {
|
|
4047
|
-
const
|
|
4048
|
-
if (!companyId) {
|
|
4049
|
-
this.branches.set([]);
|
|
4050
|
-
return;
|
|
4051
|
-
}
|
|
4052
|
-
const response = await this.userPermissionProvider
|
|
4053
|
-
.getUserBranchPermissions(userId)
|
|
4054
|
-
.toPromise();
|
|
3549
|
+
const response = await firstValueFrom(this.userPermissionProvider.getUserBranchPermissions(userId));
|
|
4055
3550
|
const userBranches = response?.data?.map((ub) => ({
|
|
4056
3551
|
id: ub.branchId,
|
|
4057
3552
|
name: ub.branchName,
|
|
@@ -4073,30 +3568,15 @@ class UserActionSelectorComponent {
|
|
|
4073
3568
|
const branchId = this.selectedBranchId();
|
|
4074
3569
|
this.loading.set(true);
|
|
4075
3570
|
try {
|
|
4076
|
-
|
|
4077
|
-
const actionsResponse = await this.actionApi
|
|
4078
|
-
.getActionsForPermission()
|
|
4079
|
-
.toPromise();
|
|
4080
|
-
// Get flat list (filtered by company if enabled)
|
|
3571
|
+
const actionsResponse = await firstValueFrom(this.actionApi.getActionsForPermission());
|
|
4081
3572
|
const flatActions = actionsResponse?.data ?? [];
|
|
4082
|
-
// Build tree structure from flat list with parentId
|
|
4083
3573
|
const actionsTree = buildTreeFromFlat(flatActions);
|
|
4084
|
-
// Store both representations
|
|
4085
3574
|
this._actionsTree.set(actionsTree);
|
|
4086
3575
|
this._actions.set(flatActions);
|
|
4087
|
-
|
|
4088
|
-
const
|
|
4089
|
-
? { branchId }
|
|
4090
|
-
: undefined;
|
|
4091
|
-
const assignedResponse = await this.permissionApi
|
|
4092
|
-
.getUserActions(userId, query)
|
|
4093
|
-
.toPromise();
|
|
3576
|
+
const query = this.isCompanyFeatureActive() && branchId ? { branchId } : undefined;
|
|
3577
|
+
const assignedResponse = await firstValueFrom(this.permissionApi.getUserActions(userId, query));
|
|
4094
3578
|
const assignedIds = new Set((assignedResponse?.data || []).map((a) => a.actionId));
|
|
4095
|
-
|
|
4096
|
-
const selMap = {};
|
|
4097
|
-
flatActions.forEach((action) => {
|
|
4098
|
-
selMap[action.id] = assignedIds.has(action.id);
|
|
4099
|
-
});
|
|
3579
|
+
const selMap = this.buildSelectionMap(flatActions, (action) => assignedIds.has(action.id));
|
|
4100
3580
|
this._selectionMap.set(selMap);
|
|
4101
3581
|
this._initialSelection.set({ ...selMap });
|
|
4102
3582
|
}
|
|
@@ -4148,48 +3628,25 @@ class UserActionSelectorComponent {
|
|
|
4148
3628
|
this.permissionLogic.handleCheck(action, this.selectionMap(), this.actions(), (newMap) => this._selectionMap.set(newMap), (previousState) => this._selectionMap.set(previousState));
|
|
4149
3629
|
}
|
|
4150
3630
|
}
|
|
4151
|
-
/**
|
|
4152
|
-
* Toggle all actions
|
|
4153
|
-
*/
|
|
4154
3631
|
toggleAll() {
|
|
4155
|
-
|
|
4156
|
-
const selMap = {};
|
|
4157
|
-
this.actions().forEach((action) => {
|
|
4158
|
-
selMap[action.id] = newValue;
|
|
4159
|
-
});
|
|
4160
|
-
this._selectionMap.set(selMap);
|
|
3632
|
+
this.setAllActions(!this.allSelected());
|
|
4161
3633
|
}
|
|
4162
|
-
/**
|
|
4163
|
-
* Select all actions
|
|
4164
|
-
*/
|
|
4165
3634
|
selectAll() {
|
|
4166
|
-
|
|
4167
|
-
this.actions().forEach((action) => {
|
|
4168
|
-
selMap[action.id] = true;
|
|
4169
|
-
});
|
|
4170
|
-
this._selectionMap.set(selMap);
|
|
3635
|
+
this.setAllActions(true);
|
|
4171
3636
|
}
|
|
4172
|
-
/**
|
|
4173
|
-
* Deselect all actions
|
|
4174
|
-
*/
|
|
4175
3637
|
deselectAll() {
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
this._selectionMap.set(selMap);
|
|
3638
|
+
this.setAllActions(false);
|
|
3639
|
+
}
|
|
3640
|
+
setAllActions(value) {
|
|
3641
|
+
this._selectionMap.set(this.buildSelectionMap(this.actions(), () => value));
|
|
4181
3642
|
}
|
|
4182
|
-
/**
|
|
4183
|
-
* Save changes to backend
|
|
4184
|
-
*/
|
|
4185
3643
|
async saveChanges() {
|
|
4186
3644
|
const userId = this.selectedUserId();
|
|
4187
3645
|
if (!userId)
|
|
4188
3646
|
return;
|
|
4189
3647
|
const branchId = this.selectedBranchId();
|
|
4190
|
-
const companyId = this.companyContext.currentCompanyInfo()?.id
|
|
4191
|
-
|
|
4192
|
-
if (isCompanyFeatureEnabled(this.appConfig) && !companyId) {
|
|
3648
|
+
const companyId = this.companyContext.currentCompanyInfo()?.id;
|
|
3649
|
+
if (this.isCompanyFeatureActive() && !companyId) {
|
|
4193
3650
|
this.messageService.add({
|
|
4194
3651
|
severity: 'warn',
|
|
4195
3652
|
summary: 'Company Required',
|
|
@@ -4197,49 +3654,30 @@ class UserActionSelectorComponent {
|
|
|
4197
3654
|
});
|
|
4198
3655
|
return;
|
|
4199
3656
|
}
|
|
4200
|
-
// Pre-save validation
|
|
4201
3657
|
const invalidActions = this.permissionLogic.getActionsWithUnmetPrerequisites(this.selectionMap(), this.actions());
|
|
4202
3658
|
if (invalidActions.length > 0) {
|
|
4203
3659
|
this.permissionLogic.showValidationErrorDialog(invalidActions, this.selectionMap(), this.actions(), (newMap) => this._selectionMap.set(newMap));
|
|
4204
3660
|
return;
|
|
4205
3661
|
}
|
|
4206
|
-
|
|
4207
|
-
const items = [];
|
|
4208
|
-
this.pendingAdd().forEach((action) => {
|
|
4209
|
-
items.push({
|
|
4210
|
-
id: action.id,
|
|
4211
|
-
action: 'add',
|
|
4212
|
-
});
|
|
4213
|
-
});
|
|
4214
|
-
this.pendingRemove().forEach((action) => {
|
|
4215
|
-
items.push({
|
|
4216
|
-
id: action.id,
|
|
4217
|
-
action: 'remove',
|
|
4218
|
-
});
|
|
4219
|
-
});
|
|
3662
|
+
const items = this.buildPermissionItems();
|
|
4220
3663
|
if (items.length === 0)
|
|
4221
3664
|
return;
|
|
4222
3665
|
this.saving.set(true);
|
|
4223
3666
|
try {
|
|
4224
3667
|
const payload = { userId, items };
|
|
4225
|
-
if (
|
|
4226
|
-
if (companyId)
|
|
3668
|
+
if (this.isCompanyFeatureActive()) {
|
|
3669
|
+
if (companyId)
|
|
4227
3670
|
payload.companyId = companyId;
|
|
4228
|
-
|
|
4229
|
-
if (branchId) {
|
|
3671
|
+
if (branchId)
|
|
4230
3672
|
payload.branchId = branchId;
|
|
4231
|
-
}
|
|
4232
3673
|
}
|
|
4233
|
-
const response = await this.permissionApi
|
|
4234
|
-
.assignUserActions(payload)
|
|
4235
|
-
.toPromise();
|
|
3674
|
+
const response = await firstValueFrom(this.permissionApi.assignUserActions(payload));
|
|
4236
3675
|
this.messageService.add({
|
|
4237
3676
|
severity: 'success',
|
|
4238
3677
|
summary: 'Success',
|
|
4239
3678
|
detail: response?.data?.message ||
|
|
4240
3679
|
'User action permissions updated successfully',
|
|
4241
3680
|
});
|
|
4242
|
-
// Update baseline
|
|
4243
3681
|
this._initialSelection.set({ ...this.selectionMap() });
|
|
4244
3682
|
}
|
|
4245
3683
|
catch {
|
|
@@ -4249,15 +3687,29 @@ class UserActionSelectorComponent {
|
|
|
4249
3687
|
this.saving.set(false);
|
|
4250
3688
|
}
|
|
4251
3689
|
}
|
|
4252
|
-
/**
|
|
4253
|
-
* Reset component state
|
|
4254
|
-
*/
|
|
4255
3690
|
resetState() {
|
|
4256
3691
|
this._actionsTree.set([]);
|
|
4257
3692
|
this._actions.set([]);
|
|
4258
3693
|
this._selectionMap.set({});
|
|
4259
3694
|
this._initialSelection.set({});
|
|
4260
3695
|
}
|
|
3696
|
+
buildSelectionMap(actions, predicate) {
|
|
3697
|
+
const selMap = {};
|
|
3698
|
+
actions.forEach((action) => {
|
|
3699
|
+
selMap[action.id] = predicate(action);
|
|
3700
|
+
});
|
|
3701
|
+
return selMap;
|
|
3702
|
+
}
|
|
3703
|
+
buildPermissionItems() {
|
|
3704
|
+
const items = [];
|
|
3705
|
+
this.pendingAdd().forEach((action) => {
|
|
3706
|
+
items.push({ id: action.id, action: 'add' });
|
|
3707
|
+
});
|
|
3708
|
+
this.pendingRemove().forEach((action) => {
|
|
3709
|
+
items.push({ id: action.id, action: 'remove' });
|
|
3710
|
+
});
|
|
3711
|
+
return items;
|
|
3712
|
+
}
|
|
4261
3713
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserActionSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4262
3714
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: UserActionSelectorComponent, isStandalone: true, selector: "flusys-user-action-selector", ngImport: i0, template: `
|
|
4263
3715
|
<div class="user-action-selector">
|
|
@@ -4513,11 +3965,11 @@ class UserActionSelectorComponent {
|
|
|
4513
3965
|
}
|
|
4514
3966
|
}
|
|
4515
3967
|
</div>
|
|
4516
|
-
`, isInline: true, styles: [":host{display:block}.validation-warning{background-color:var(--p-yellow-50, #fefce8);border-left:4px solid var(--p-yellow-500, #eab308);color:var(--p-yellow-700, #a16207)}:host-context(.p-dark) .validation-warning{background-color:#eab3081a;color:var(--p-yellow-400, #facc15)}:host ::ng-deep tr.highlight-warning{background-color:var(--p-red-50, rgba(239, 68, 68, .1))!important}:host-context(.p-dark) ::ng-deep tr.highlight-warning{background-color:#ef444426!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i5.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: i7.TreeTable, selector: "p-treeTable, p-treetable, p-tree-table", inputs: ["columns", "styleClass", "tableStyle", "tableStyleClass", "autoLayout", "lazy", "lazyLoadOnInit", "paginator", "rows", "first", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "customSort", "selectionMode", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "compareSelectionBy", "rowHover", "loading", "loadingIcon", "showLoader", "scrollable", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "frozenColumns", "resizableColumns", "columnResizeMode", "reorderableColumns", "contextMenu", "rowTrackBy", "filters", "globalFilterFields", "filterDelay", "filterMode", "filterLocale", "paginatorLocale", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "value", "virtualRowHeight", "selectionKeys", "showGridlines"], outputs: ["selectionChange", "contextMenuSelectionChange", "onFilter", "onNodeExpand", "onNodeCollapse", "onPage", "onSort", "onLazyLoad", "sortFunction", "onColResize", "onColReorder", "onNodeSelect", "onNodeUnselect", "onContextMenuSelect", "onHeaderCheckboxToggle", "onEditInit", "onEditComplete", "onEditCancel", "selectionKeysChange"] }, { kind: "component", type: i7.TreeTableToggler, selector: "p-treeTableToggler, p-treetabletoggler, p-treetable-toggler", inputs: ["rowNode"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "component", type: UserSelectComponent, selector: "lib-user-select", inputs: ["
|
|
3968
|
+
`, isInline: true, styles: [":host{display:block}.validation-warning{background-color:var(--p-yellow-50, #fefce8);border-left:4px solid var(--p-yellow-500, #eab308);color:var(--p-yellow-700, #a16207)}:host-context(.p-dark) .validation-warning{background-color:#eab3081a;color:var(--p-yellow-400, #facc15)}:host ::ng-deep tr.highlight-warning{background-color:var(--p-red-50, rgba(239, 68, 68, .1))!important}:host-context(.p-dark) ::ng-deep tr.highlight-warning{background-color:#ef444426!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i5.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: i7.TreeTable, selector: "p-treeTable, p-treetable, p-tree-table", inputs: ["columns", "styleClass", "tableStyle", "tableStyleClass", "autoLayout", "lazy", "lazyLoadOnInit", "paginator", "rows", "first", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "customSort", "selectionMode", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "compareSelectionBy", "rowHover", "loading", "loadingIcon", "showLoader", "scrollable", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "frozenColumns", "resizableColumns", "columnResizeMode", "reorderableColumns", "contextMenu", "rowTrackBy", "filters", "globalFilterFields", "filterDelay", "filterMode", "filterLocale", "paginatorLocale", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "value", "virtualRowHeight", "selectionKeys", "showGridlines"], outputs: ["selectionChange", "contextMenuSelectionChange", "onFilter", "onNodeExpand", "onNodeCollapse", "onPage", "onSort", "onLazyLoad", "sortFunction", "onColResize", "onColReorder", "onNodeSelect", "onNodeUnselect", "onContextMenuSelect", "onHeaderCheckboxToggle", "onEditInit", "onEditComplete", "onEditCancel", "selectionKeysChange"] }, { kind: "component", type: i7.TreeTableToggler, selector: "p-treeTableToggler, p-treetabletoggler, p-treetable-toggler", inputs: ["rowNode"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "component", type: UserSelectComponent, selector: "lib-user-select", inputs: ["value"], outputs: ["valueChange", "userSelected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4517
3969
|
}
|
|
4518
3970
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserActionSelectorComponent, decorators: [{
|
|
4519
3971
|
type: Component,
|
|
4520
|
-
args: [{ selector: 'flusys-user-action-selector',
|
|
3972
|
+
args: [{ selector: 'flusys-user-action-selector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective, UserSelectComponent], template: `
|
|
4521
3973
|
<div class="user-action-selector">
|
|
4522
3974
|
<!-- User and Branch Selectors -->
|
|
4523
3975
|
<div class="surface-card p-4 rounded-border mb-4 shadow-sm">
|
|
@@ -4828,90 +4280,76 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4828
4280
|
type: Injectable
|
|
4829
4281
|
}] });
|
|
4830
4282
|
|
|
4831
|
-
/**
|
|
4832
|
-
* Provide IAM Provider Adapters
|
|
4833
|
-
*
|
|
4834
|
-
* Registers IAM implementations for provider interfaces from ng-shared.
|
|
4835
|
-
* This allows ng-auth profile page to display permissions without direct dependencies.
|
|
4836
|
-
*
|
|
4837
|
-
* @example
|
|
4838
|
-
* // In app.config.ts
|
|
4839
|
-
* import { provideIamProviders } from '@flusys/ng-iam';
|
|
4840
|
-
*
|
|
4841
|
-
* export const appConfig: ApplicationConfig = {
|
|
4842
|
-
* providers: [
|
|
4843
|
-
* ...provideIamProviders(),
|
|
4844
|
-
* // ... other providers
|
|
4845
|
-
* ]
|
|
4846
|
-
* };
|
|
4847
|
-
*
|
|
4848
|
-
* @returns Array of Angular providers
|
|
4849
|
-
*/
|
|
4283
|
+
/** Registers IAM provider adapters for ng-shared interfaces */
|
|
4850
4284
|
function provideIamProviders() {
|
|
4851
4285
|
return [
|
|
4852
|
-
|
|
4853
|
-
{
|
|
4854
|
-
provide: PROFILE_PERMISSION_PROVIDER,
|
|
4855
|
-
useClass: ProfilePermissionProviderAdapter,
|
|
4856
|
-
},
|
|
4286
|
+
{ provide: PROFILE_PERMISSION_PROVIDER, useClass: ProfilePermissionProviderAdapter },
|
|
4857
4287
|
];
|
|
4858
4288
|
}
|
|
4859
4289
|
|
|
4860
4290
|
/**
|
|
4861
4291
|
* IAM Routes Configuration
|
|
4862
4292
|
*
|
|
4863
|
-
* Identity and Access Management routing
|
|
4864
|
-
* - Actions: Permission actions
|
|
4293
|
+
* Identity and Access Management routing with permission guards.
|
|
4294
|
+
* - Actions: Permission actions management
|
|
4865
4295
|
* - Roles: Role management (conditional on RBAC/FULL mode)
|
|
4866
|
-
* - Permissions: User permission assignments
|
|
4867
|
-
*
|
|
4868
|
-
* All routes are protected by permission guards to prevent direct URL access.
|
|
4296
|
+
* - Permissions: User permission assignments
|
|
4869
4297
|
*/
|
|
4870
4298
|
const IAM_ROUTES = [
|
|
4871
4299
|
{
|
|
4872
4300
|
path: '',
|
|
4873
|
-
loadComponent: () => import('./flusys-ng-iam-iam-container.component-
|
|
4301
|
+
loadComponent: () => import('./flusys-ng-iam-iam-container.component-BToYxEej.mjs').then((m) => m.IamContainerComponent),
|
|
4874
4302
|
children: [
|
|
4875
4303
|
// Actions Management
|
|
4876
4304
|
{
|
|
4877
4305
|
path: 'actions',
|
|
4306
|
+
canActivate: [permissionGuard(ACTION_PERMISSIONS.READ)],
|
|
4878
4307
|
children: [
|
|
4879
4308
|
{
|
|
4880
4309
|
path: '',
|
|
4881
|
-
loadComponent: () => import('./flusys-ng-iam-action-list-page.component-
|
|
4310
|
+
loadComponent: () => import('./flusys-ng-iam-action-list-page.component-CQ6RazN0.mjs').then((m) => m.ActionListPageComponent),
|
|
4882
4311
|
},
|
|
4883
4312
|
{
|
|
4884
4313
|
path: 'new',
|
|
4885
|
-
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-
|
|
4314
|
+
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-CVN8sV-c.mjs').then((m) => m.ActionFormPageComponent),
|
|
4886
4315
|
},
|
|
4887
4316
|
{
|
|
4888
4317
|
path: ':id',
|
|
4889
|
-
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-
|
|
4318
|
+
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-CVN8sV-c.mjs').then((m) => m.ActionFormPageComponent),
|
|
4890
4319
|
},
|
|
4891
4320
|
],
|
|
4892
4321
|
},
|
|
4893
4322
|
// Roles Management
|
|
4894
4323
|
{
|
|
4895
4324
|
path: 'roles',
|
|
4325
|
+
canActivate: [permissionGuard(ROLE_PERMISSIONS.READ)],
|
|
4896
4326
|
children: [
|
|
4897
4327
|
{
|
|
4898
4328
|
path: '',
|
|
4899
|
-
loadComponent: () => import('./flusys-ng-iam-role-list-page.component-
|
|
4329
|
+
loadComponent: () => import('./flusys-ng-iam-role-list-page.component-Cz-jk-R_.mjs').then((m) => m.RoleListPageComponent),
|
|
4900
4330
|
},
|
|
4901
4331
|
{
|
|
4902
4332
|
path: 'new',
|
|
4903
|
-
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-
|
|
4333
|
+
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-BjPwXkip.mjs').then((m) => m.RoleFormPageComponent),
|
|
4904
4334
|
},
|
|
4905
4335
|
{
|
|
4906
4336
|
path: ':id',
|
|
4907
|
-
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-
|
|
4337
|
+
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-BjPwXkip.mjs').then((m) => m.RoleFormPageComponent),
|
|
4908
4338
|
},
|
|
4909
4339
|
],
|
|
4910
4340
|
},
|
|
4911
|
-
// Permissions Management (
|
|
4341
|
+
// Permissions Management (requires any permission tab access)
|
|
4912
4342
|
{
|
|
4913
4343
|
path: 'permissions',
|
|
4914
|
-
|
|
4344
|
+
canActivate: [
|
|
4345
|
+
anyPermissionGuard([
|
|
4346
|
+
ROLE_ACTION_PERMISSIONS.READ,
|
|
4347
|
+
USER_ROLE_PERMISSIONS.READ,
|
|
4348
|
+
USER_ACTION_PERMISSIONS.READ,
|
|
4349
|
+
COMPANY_ACTION_PERMISSIONS.READ,
|
|
4350
|
+
]),
|
|
4351
|
+
],
|
|
4352
|
+
loadComponent: () => import('./flusys-ng-iam-permission-page.component-BS7xXmsn.mjs').then((m) => m.PermissionPageComponent),
|
|
4915
4353
|
},
|
|
4916
4354
|
// Default redirect to actions
|
|
4917
4355
|
{
|
|
@@ -4930,4 +4368,4 @@ const IAM_ROUTES = [
|
|
|
4930
4368
|
*/
|
|
4931
4369
|
|
|
4932
4370
|
export { ActionApiService as A, CompanyActionSelectorComponent as C, IAM_ROUTES as I, LogicBuilderComponent as L, MAX_DROPDOWN_ITEMS as M, PermissionApiService as P, RoleApiService as R, UserRoleSelectorComponent as U, ActionType as a, RoleActionSelectorComponent as b, convertActionToTreeNode as c, UserActionSelectorComponent as d, ActionPermissionLogicService as e, MyPermissionsApiService as f, PermissionStateService as g, ProfilePermissionProviderAdapter as h, provideIamProviders as p };
|
|
4933
|
-
//# sourceMappingURL=flusys-ng-iam-flusys-ng-iam-
|
|
4371
|
+
//# sourceMappingURL=flusys-ng-iam-flusys-ng-iam-DrGHlTiz.mjs.map
|