@flusys/ng-iam 1.0.0-rc → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/fesm2022/{flusys-ng-iam-action-form-page.component-C_BRrrWW.mjs → flusys-ng-iam-action-form-page.component-eXpZNJ_H.mjs} +14 -64
- package/fesm2022/flusys-ng-iam-action-form-page.component-eXpZNJ_H.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-action-list-page.component-Daf93zpS.mjs → flusys-ng-iam-action-list-page.component-BtJlGcTj.mjs} +22 -49
- package/fesm2022/flusys-ng-iam-action-list-page.component-BtJlGcTj.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-flusys-ng-iam-BPIpfrjN.mjs → flusys-ng-iam-flusys-ng-iam-CJAQT60K.mjs} +295 -859
- package/fesm2022/flusys-ng-iam-flusys-ng-iam-CJAQT60K.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-iam-container.component-Bn4kQtxW.mjs → flusys-ng-iam-iam-container.component-UYJjqYV9.mjs} +5 -5
- package/fesm2022/flusys-ng-iam-iam-container.component-UYJjqYV9.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-permission-page.component-CmxOBJPu.mjs → flusys-ng-iam-permission-page.component-DcgT7L3_.mjs} +11 -46
- package/fesm2022/flusys-ng-iam-permission-page.component-DcgT7L3_.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-role-form-page.component-ByNueI1a.mjs → flusys-ng-iam-role-form-page.component-D_AAEay2.mjs} +5 -19
- package/fesm2022/flusys-ng-iam-role-form-page.component-D_AAEay2.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-role-list-page.component-CFly5KnH.mjs → flusys-ng-iam-role-list-page.component-D4J1by6Q.mjs} +6 -23
- package/fesm2022/flusys-ng-iam-role-list-page.component-D4J1by6Q.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 -445
- 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,8 +1,8 @@
|
|
|
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
|
|
5
|
-
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
|
+
import { APP_CONFIG, getServiceUrl, 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';
|
|
8
8
|
import { tap, catchError, map } from 'rxjs/operators';
|
|
@@ -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,130 +29,66 @@ 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('roles', inject(HttpClient), 'iam');
|
|
60
38
|
}
|
|
61
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
62
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
39
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: RoleApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
40
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: RoleApiService, providedIn: 'root' });
|
|
63
41
|
}
|
|
64
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
42
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", 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
|
-
|
|
80
|
-
super('iam/actions', http);
|
|
50
|
+
super('actions', inject(HttpClient), 'iam');
|
|
81
51
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Get actions for permission assignment
|
|
84
|
-
* POST /iam/actions/tree-for-permission
|
|
85
|
-
* Returns actions filtered by company whitelist if enabled
|
|
86
|
-
*/
|
|
52
|
+
/** Get actions filtered by company whitelist for permission assignment */
|
|
87
53
|
getActionsForPermission() {
|
|
88
|
-
|
|
54
|
+
const iamBaseUrl = getServiceUrl(this.appConfig, 'iam');
|
|
55
|
+
return this.http.post(`${iamBaseUrl}/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
|
-
return this.http.post(`${
|
|
66
|
+
const iamBaseUrl = getServiceUrl(this.appConfig, 'iam');
|
|
67
|
+
return this.http.post(`${iamBaseUrl}/actions/tree`, body);
|
|
112
68
|
}
|
|
113
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
114
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
69
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ActionApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
70
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ActionApiService, providedIn: 'root' });
|
|
115
71
|
}
|
|
116
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
72
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ActionApiService, decorators: [{
|
|
117
73
|
type: Injectable,
|
|
118
|
-
args: [{
|
|
119
|
-
providedIn: 'root',
|
|
120
|
-
}]
|
|
74
|
+
args: [{ providedIn: 'root' }]
|
|
121
75
|
}], ctorParameters: () => [] });
|
|
122
76
|
|
|
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
77
|
function extractRequiredActionIds(logic) {
|
|
131
78
|
const actionIds = new Set();
|
|
132
|
-
if (
|
|
133
|
-
|
|
79
|
+
if (logic) {
|
|
80
|
+
collectActionIds(logic, actionIds);
|
|
134
81
|
}
|
|
135
|
-
collectActionIds(logic, actionIds);
|
|
136
82
|
return actionIds;
|
|
137
83
|
}
|
|
138
|
-
/**
|
|
139
|
-
* Recursively collect action IDs from logic tree
|
|
140
|
-
*/
|
|
141
84
|
function collectActionIds(node, actionIds) {
|
|
142
85
|
if (node.type === 'action' && node.actionId) {
|
|
143
86
|
actionIds.add(node.actionId);
|
|
144
87
|
}
|
|
145
88
|
else if (node.type === 'group' && node.children) {
|
|
146
|
-
|
|
147
|
-
collectActionIds(child, actionIds);
|
|
148
|
-
}
|
|
89
|
+
node.children.forEach((child) => collectActionIds(child, actionIds));
|
|
149
90
|
}
|
|
150
91
|
}
|
|
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
92
|
function validateActionPrerequisites(action, selectedActionIds, allActions) {
|
|
161
93
|
if (!action.permissionLogic) {
|
|
162
94
|
return { valid: true, missingActions: [] };
|
|
@@ -168,20 +100,12 @@ function validateActionPrerequisites(action, selectedActionIds, allActions) {
|
|
|
168
100
|
const missingActions = allActions.filter((a) => a.id && result.missingActionIds.has(a.id));
|
|
169
101
|
return { valid: false, missingActions };
|
|
170
102
|
}
|
|
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
103
|
function evaluateLogicNodeWithMissing(node, selectedActionIds) {
|
|
177
104
|
if (node.type === 'action' && node.actionId) {
|
|
178
105
|
const valid = selectedActionIds.has(node.actionId);
|
|
179
|
-
return {
|
|
180
|
-
valid,
|
|
181
|
-
missingActionIds: valid ? new Set() : new Set([node.actionId]),
|
|
182
|
-
};
|
|
106
|
+
return { valid, missingActionIds: valid ? new Set() : new Set([node.actionId]) };
|
|
183
107
|
}
|
|
184
|
-
if (node.type === 'group' && node.children
|
|
108
|
+
if (node.type === 'group' && node.children?.length) {
|
|
185
109
|
const operator = node.operator || 'AND';
|
|
186
110
|
if (operator === 'AND') {
|
|
187
111
|
const missingIds = new Set();
|
|
@@ -195,109 +119,28 @@ function evaluateLogicNodeWithMissing(node, selectedActionIds) {
|
|
|
195
119
|
}
|
|
196
120
|
return { valid: allValid, missingActionIds: missingIds };
|
|
197
121
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
122
|
+
// OR operator
|
|
123
|
+
for (const child of node.children) {
|
|
124
|
+
const childResult = evaluateLogicNodeWithMissing(child, selectedActionIds);
|
|
125
|
+
if (childResult.valid) {
|
|
126
|
+
return { valid: true, missingActionIds: new Set() };
|
|
204
127
|
}
|
|
205
|
-
const firstChildResult = evaluateLogicNodeWithMissing(node.children[0], selectedActionIds);
|
|
206
|
-
return {
|
|
207
|
-
valid: false,
|
|
208
|
-
missingActionIds: firstChildResult.missingActionIds,
|
|
209
|
-
};
|
|
210
128
|
}
|
|
129
|
+
const firstChildResult = evaluateLogicNodeWithMissing(node.children[0], selectedActionIds);
|
|
130
|
+
return { valid: false, missingActionIds: firstChildResult.missingActionIds };
|
|
211
131
|
}
|
|
212
132
|
return { valid: true, missingActionIds: new Set() };
|
|
213
133
|
}
|
|
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
134
|
|
|
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
|
-
*/
|
|
135
|
+
/** Shared service for smart dependency management across action selectors */
|
|
281
136
|
class ActionPermissionLogicService {
|
|
282
137
|
confirmationService = inject(ConfirmationService);
|
|
283
138
|
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
|
-
*/
|
|
139
|
+
/** Handle checking an action with prerequisite validation (recursive deep scan) */
|
|
297
140
|
handleCheck(action, currentSelection, allActions, onUpdate, onCancel) {
|
|
298
141
|
// Validate prerequisites with RECURSIVE DEEP SCAN
|
|
299
142
|
if (action.permissionLogic) {
|
|
300
|
-
const selectedActionIds =
|
|
143
|
+
const selectedActionIds = this.getSelectedIds(currentSelection);
|
|
301
144
|
const validationResult = validateActionPrerequisites(action, selectedActionIds, allActions);
|
|
302
145
|
if (!validationResult.valid) {
|
|
303
146
|
// Store previous state
|
|
@@ -318,14 +161,7 @@ class ActionPermissionLogicService {
|
|
|
318
161
|
selMap[action.id] = true;
|
|
319
162
|
onUpdate(selMap);
|
|
320
163
|
}
|
|
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
|
-
*/
|
|
164
|
+
/** Handle unchecking an action with dependency detection */
|
|
329
165
|
handleUncheck(action, currentSelection, allActions, onUpdate) {
|
|
330
166
|
const affectedActions = this.findActionsDependingOn(action.id, currentSelection, allActions);
|
|
331
167
|
if (affectedActions.length > 0) {
|
|
@@ -337,46 +173,28 @@ class ActionPermissionLogicService {
|
|
|
337
173
|
selMap[action.id] = false;
|
|
338
174
|
onUpdate(selMap);
|
|
339
175
|
}
|
|
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
|
-
*/
|
|
176
|
+
/** Check if an action has unmet prerequisites */
|
|
348
177
|
hasUnmetPrerequisites(action, currentSelection, allActions) {
|
|
349
178
|
const isSelected = currentSelection[action.id];
|
|
350
179
|
if (!isSelected || !action.permissionLogic) {
|
|
351
180
|
return false;
|
|
352
181
|
}
|
|
353
|
-
const selectedActionIds =
|
|
182
|
+
const selectedActionIds = this.getSelectedIds(currentSelection);
|
|
183
|
+
selectedActionIds.delete(action.id);
|
|
354
184
|
const validationResult = validateActionPrerequisites(action, selectedActionIds, allActions);
|
|
355
185
|
return !validationResult.valid;
|
|
356
186
|
}
|
|
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
|
-
*/
|
|
187
|
+
/** Get all selected actions that have unmet prerequisites */
|
|
364
188
|
getActionsWithUnmetPrerequisites(currentSelection, allActions) {
|
|
365
189
|
const selectedActions = allActions.filter((a) => currentSelection[a.id]);
|
|
366
190
|
return selectedActions.filter((action) => this.hasUnmetPrerequisites(action, currentSelection, allActions));
|
|
367
191
|
}
|
|
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
|
-
*/
|
|
192
|
+
/** Show validation error dialog with auto-fix options */
|
|
376
193
|
showValidationErrorDialog(invalidActions, currentSelection, allActions, onUpdate) {
|
|
377
194
|
const errorList = invalidActions
|
|
378
195
|
.map((action) => {
|
|
379
|
-
const selectedActionIds =
|
|
196
|
+
const selectedActionIds = this.getSelectedIds(currentSelection);
|
|
197
|
+
selectedActionIds.delete(action.id);
|
|
380
198
|
const validationResult = validateActionPrerequisites(action, selectedActionIds, allActions);
|
|
381
199
|
const sanitizedActionName = this.sanitizeHtml(action.name ?? 'Unknown');
|
|
382
200
|
const missingNames = validationResult.missingActions
|
|
@@ -418,19 +236,12 @@ class ActionPermissionLogicService {
|
|
|
418
236
|
},
|
|
419
237
|
});
|
|
420
238
|
}
|
|
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
|
-
*/
|
|
239
|
+
/** Get prerequisite description for tooltip display */
|
|
429
240
|
getPrerequisiteTooltip(action, currentSelection, allActions) {
|
|
430
241
|
if (!action.permissionLogic) {
|
|
431
242
|
return '';
|
|
432
243
|
}
|
|
433
|
-
const selectedActionIds =
|
|
244
|
+
const selectedActionIds = this.getSelectedIds(currentSelection);
|
|
434
245
|
const validationResult = validateActionPrerequisites(action, selectedActionIds, allActions);
|
|
435
246
|
if (validationResult.valid) {
|
|
436
247
|
return '[OK] Prerequisites Satisfied\n\nAll required actions are already selected.\nYou can safely add this action.';
|
|
@@ -442,23 +253,16 @@ class ActionPermissionLogicService {
|
|
|
442
253
|
const hint = '\n\n💡 Click to auto-select required actions';
|
|
443
254
|
return `${header}\n\n${logicTree}${hint}`;
|
|
444
255
|
}
|
|
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();
|
|
256
|
+
/** Build dynamic logic tree message with AND/OR operators and nesting */
|
|
257
|
+
buildLogicMessage(logic, _missingActions, allActions, currentSelection) {
|
|
258
|
+
const selectedIds = currentSelection ? this.getSelectedIds(currentSelection) : new Set();
|
|
458
259
|
const actionMap = new Map(allActions.map((a) => [a.id, a]));
|
|
459
260
|
return this.formatLogicNode(logic, selectedIds, actionMap, 0);
|
|
460
261
|
}
|
|
461
|
-
|
|
262
|
+
/** Extract selected action IDs from selection map */
|
|
263
|
+
getSelectedIds(currentSelection) {
|
|
264
|
+
return new Set(Object.keys(currentSelection).filter((id) => currentSelection[id]));
|
|
265
|
+
}
|
|
462
266
|
sanitizeHtml(text) {
|
|
463
267
|
return text
|
|
464
268
|
.replace(/&/g, '&')
|
|
@@ -467,24 +271,7 @@ class ActionPermissionLogicService {
|
|
|
467
271
|
.replace(/"/g, '"')
|
|
468
272
|
.replace(/'/g, ''');
|
|
469
273
|
}
|
|
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
|
-
*/
|
|
274
|
+
/** Recursively collect ALL missing prerequisites at all dependency levels */
|
|
488
275
|
getAllMissingPrerequisitesRecursive(action, currentSelection, allActions) {
|
|
489
276
|
if (!action.id)
|
|
490
277
|
return [];
|
|
@@ -504,7 +291,7 @@ class ActionPermissionLogicService {
|
|
|
504
291
|
}
|
|
505
292
|
visited.add(targetAction.id);
|
|
506
293
|
stack.add(targetAction.id);
|
|
507
|
-
const selectedActionIds =
|
|
294
|
+
const selectedActionIds = this.getSelectedIds(currentSelection);
|
|
508
295
|
const result = validateActionPrerequisites(targetAction, selectedActionIds, allActions);
|
|
509
296
|
if (!result.valid) {
|
|
510
297
|
result.missingActions.forEach((missingAction) => {
|
|
@@ -565,7 +352,7 @@ class ActionPermissionLogicService {
|
|
|
565
352
|
},
|
|
566
353
|
});
|
|
567
354
|
}
|
|
568
|
-
showDependencyDialog(action, affectedActions, currentSelection,
|
|
355
|
+
showDependencyDialog(action, affectedActions, currentSelection, _allActions, onUpdate) {
|
|
569
356
|
if (!action.id)
|
|
570
357
|
return;
|
|
571
358
|
const alternatives = this.findAlternatives(action.id, affectedActions, currentSelection);
|
|
@@ -688,7 +475,7 @@ class ActionPermissionLogicService {
|
|
|
688
475
|
calculateSmartSelection(logic, missingActions, currentSelection, allActions) {
|
|
689
476
|
const missingIds = new Set(missingActions.map((a) => a.id));
|
|
690
477
|
const actionMap = new Map(allActions.map((a) => [a.id, a]));
|
|
691
|
-
const selectedIds =
|
|
478
|
+
const selectedIds = this.getSelectedIds(currentSelection);
|
|
692
479
|
const requiredIds = this.findRequiredActionIds(logic, missingIds, selectedIds);
|
|
693
480
|
return Array.from(requiredIds)
|
|
694
481
|
.map((id) => actionMap.get(id))
|
|
@@ -761,12 +548,10 @@ class ActionPermissionLogicService {
|
|
|
761
548
|
}
|
|
762
549
|
return `${indent}<em style="color: #9ca3af;">Invalid logic node</em>`;
|
|
763
550
|
}
|
|
764
|
-
/**
|
|
765
|
-
* Build clean text-based logic tree for tooltips
|
|
766
|
-
*/
|
|
551
|
+
/** Build clean text-based logic tree for tooltips */
|
|
767
552
|
buildTooltipLogicTree(node, currentSelection, allActions, depth) {
|
|
768
553
|
const indent = ' '.repeat(depth);
|
|
769
|
-
const selectedIds =
|
|
554
|
+
const selectedIds = this.getSelectedIds(currentSelection);
|
|
770
555
|
const actionMap = new Map(allActions.map((a) => [a.id, a]));
|
|
771
556
|
if (node.type === 'action' && node.actionId) {
|
|
772
557
|
const action = actionMap.get(node.actionId);
|
|
@@ -796,107 +581,48 @@ class ActionPermissionLogicService {
|
|
|
796
581
|
}
|
|
797
582
|
return `${indent}(invalid)`;
|
|
798
583
|
}
|
|
799
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
800
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
584
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ActionPermissionLogicService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
585
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ActionPermissionLogicService, providedIn: 'root' });
|
|
801
586
|
}
|
|
802
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
587
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ActionPermissionLogicService, decorators: [{
|
|
803
588
|
type: Injectable,
|
|
804
|
-
args: [{
|
|
805
|
-
providedIn: 'root',
|
|
806
|
-
}]
|
|
589
|
+
args: [{ providedIn: 'root' }]
|
|
807
590
|
}] });
|
|
808
591
|
|
|
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
592
|
class PermissionApiService extends BaseApiService {
|
|
821
593
|
constructor() {
|
|
822
594
|
super('iam');
|
|
823
595
|
}
|
|
824
|
-
// =============================================================================
|
|
825
|
-
// User → Action (Direct Permissions)
|
|
826
|
-
// =============================================================================
|
|
827
|
-
/**
|
|
828
|
-
* Assign/remove actions directly to/from user
|
|
829
|
-
* POST /permissions/user-actions/assign
|
|
830
|
-
*/
|
|
831
596
|
assignUserActions(data) {
|
|
832
597
|
return this.http.post(this.getUrl('permissions/user-actions/assign'), data);
|
|
833
598
|
}
|
|
834
|
-
/**
|
|
835
|
-
* Get user's direct action permissions
|
|
836
|
-
* POST /iam/permissions/get-user-actions
|
|
837
|
-
*/
|
|
838
599
|
getUserActions(userId, query) {
|
|
839
600
|
return this.http.post(this.getUrl('permissions/get-user-actions'), { userId, branchId: query?.branchId });
|
|
840
601
|
}
|
|
841
|
-
// =============================================================================
|
|
842
|
-
// User → Role (Role Assignments)
|
|
843
|
-
// =============================================================================
|
|
844
|
-
/**
|
|
845
|
-
* Assign/remove roles to/from user
|
|
846
|
-
* POST /permissions/user-roles/assign
|
|
847
|
-
*/
|
|
848
602
|
assignUserRoles(data) {
|
|
849
603
|
return this.http.post(this.getUrl('permissions/user-roles/assign'), data);
|
|
850
604
|
}
|
|
851
|
-
/**
|
|
852
|
-
* Get user's role assignments
|
|
853
|
-
* POST /iam/permissions/get-user-roles
|
|
854
|
-
*/
|
|
855
605
|
getUserRoles(userId, query) {
|
|
856
606
|
return this.http.post(this.getUrl('permissions/get-user-roles'), { userId, branchId: query?.branchId });
|
|
857
607
|
}
|
|
858
|
-
// =============================================================================
|
|
859
|
-
// Role → Action (Role Permissions)
|
|
860
|
-
// =============================================================================
|
|
861
|
-
/**
|
|
862
|
-
* Assign/remove actions to/from role
|
|
863
|
-
* POST /permissions/role-actions/assign
|
|
864
|
-
*/
|
|
865
608
|
assignRoleActions(data) {
|
|
866
609
|
return this.http.post(this.getUrl('permissions/role-actions/assign'), data);
|
|
867
610
|
}
|
|
868
|
-
|
|
869
|
-
* Get role's action permissions
|
|
870
|
-
* POST /iam/permissions/get-role-actions
|
|
871
|
-
*/
|
|
872
|
-
getRoleActions(roleId, query) {
|
|
611
|
+
getRoleActions(roleId) {
|
|
873
612
|
return this.http.post(this.getUrl('permissions/get-role-actions'), { roleId });
|
|
874
613
|
}
|
|
875
|
-
// =============================================================================
|
|
876
|
-
// Company → Action (Company Whitelisting)
|
|
877
|
-
// =============================================================================
|
|
878
|
-
/**
|
|
879
|
-
* Assign/remove actions to/from company (whitelisting)
|
|
880
|
-
* POST /permissions/company-actions/assign
|
|
881
|
-
*/
|
|
882
614
|
assignCompanyActions(data) {
|
|
883
615
|
return this.http.post(this.getUrl('permissions/company-actions/assign'), data);
|
|
884
616
|
}
|
|
885
|
-
/**
|
|
886
|
-
* Get company's whitelisted actions
|
|
887
|
-
* POST /iam/permissions/get-company-actions
|
|
888
|
-
*/
|
|
889
617
|
getCompanyActions(companyId) {
|
|
890
618
|
return this.http.post(this.getUrl('permissions/get-company-actions'), { companyId });
|
|
891
619
|
}
|
|
892
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
893
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
620
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PermissionApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
621
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PermissionApiService, providedIn: 'root' });
|
|
894
622
|
}
|
|
895
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
623
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PermissionApiService, decorators: [{
|
|
896
624
|
type: Injectable,
|
|
897
|
-
args: [{
|
|
898
|
-
providedIn: 'root',
|
|
899
|
-
}]
|
|
625
|
+
args: [{ providedIn: 'root' }]
|
|
900
626
|
}], ctorParameters: () => [] });
|
|
901
627
|
|
|
902
628
|
/**
|
|
@@ -908,10 +634,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
908
634
|
class MyPermissionsApiService {
|
|
909
635
|
http = inject(HttpClient);
|
|
910
636
|
appConfig = inject(APP_CONFIG);
|
|
911
|
-
baseUrl
|
|
912
|
-
constructor() {
|
|
913
|
-
this.baseUrl = `${this.appConfig.apiBaseUrl}/iam/permissions`;
|
|
914
|
-
}
|
|
637
|
+
baseUrl = `${getServiceUrl(this.appConfig, 'iam')}/permissions`;
|
|
915
638
|
/**
|
|
916
639
|
* Get current user's complete permissions
|
|
917
640
|
* POST /iam/permissions/my-permissions
|
|
@@ -921,57 +644,28 @@ class MyPermissionsApiService {
|
|
|
921
644
|
getMyPermissions(dto = {}) {
|
|
922
645
|
return this.http.post(`${this.baseUrl}/my-permissions`, dto);
|
|
923
646
|
}
|
|
924
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
925
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
647
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: MyPermissionsApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
648
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: MyPermissionsApiService, providedIn: 'root' });
|
|
926
649
|
}
|
|
927
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
650
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: MyPermissionsApiService, decorators: [{
|
|
928
651
|
type: Injectable,
|
|
929
652
|
args: [{
|
|
930
653
|
providedIn: 'root',
|
|
931
654
|
}]
|
|
932
|
-
}]
|
|
655
|
+
}] });
|
|
933
656
|
|
|
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
657
|
class PermissionStateService {
|
|
956
658
|
permissionApi = inject(MyPermissionsApiService);
|
|
957
659
|
permissionValidator = inject(PermissionValidatorService);
|
|
958
|
-
// Permission state
|
|
959
660
|
_permissions = signal(null, ...(ngDevMode ? [{ debugName: "_permissions" }] : []));
|
|
960
661
|
permissions = this._permissions.asReadonly();
|
|
961
|
-
// Loading state
|
|
962
662
|
_isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : []));
|
|
963
663
|
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
664
|
loadPermissions(dto) {
|
|
970
665
|
this._isLoading.set(true);
|
|
971
666
|
return this.permissionApi.getMyPermissions(dto).pipe(tap((response) => {
|
|
972
667
|
this._permissions.set(response.data ?? null);
|
|
973
668
|
this._isLoading.set(false);
|
|
974
|
-
// Update shared permission validator
|
|
975
669
|
const actionCodes = response.data?.frontendActions.map((a) => a.code) ?? [];
|
|
976
670
|
this.permissionValidator.setPermissions(actionCodes);
|
|
977
671
|
}), catchError(() => {
|
|
@@ -981,19 +675,13 @@ class PermissionStateService {
|
|
|
981
675
|
return of(void 0);
|
|
982
676
|
}), map(() => void 0));
|
|
983
677
|
}
|
|
984
|
-
/**
|
|
985
|
-
* Check if permissions are loaded
|
|
986
|
-
*
|
|
987
|
-
* @returns true if permissions are loaded
|
|
988
|
-
*/
|
|
989
678
|
isLoaded() {
|
|
990
|
-
return
|
|
991
|
-
this.permissionValidator.isPermissionsLoaded());
|
|
679
|
+
return this._permissions() !== null && this.permissionValidator.isPermissionsLoaded();
|
|
992
680
|
}
|
|
993
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
994
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
681
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PermissionStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
682
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PermissionStateService, providedIn: 'root' });
|
|
995
683
|
}
|
|
996
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
684
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PermissionStateService, decorators: [{
|
|
997
685
|
type: Injectable,
|
|
998
686
|
args: [{ providedIn: 'root' }]
|
|
999
687
|
}] });
|
|
@@ -1012,26 +700,28 @@ function toLogicNode(node) {
|
|
|
1012
700
|
}
|
|
1013
701
|
function toBuilderNode(node) {
|
|
1014
702
|
if (node.type === 'action') {
|
|
1015
|
-
return
|
|
703
|
+
return createActionNode(node.actionId);
|
|
1016
704
|
}
|
|
1017
705
|
return {
|
|
1018
|
-
|
|
1019
|
-
type: 'group',
|
|
1020
|
-
operator: node.operator,
|
|
706
|
+
...createGroupNode(node.operator),
|
|
1021
707
|
children: node.children?.map(toBuilderNode) ?? [],
|
|
1022
708
|
};
|
|
1023
709
|
}
|
|
710
|
+
function createGroupNode(operator = 'AND') {
|
|
711
|
+
return { id: crypto.randomUUID(), type: 'group', operator, children: [] };
|
|
712
|
+
}
|
|
713
|
+
function createActionNode(actionId = '') {
|
|
714
|
+
return { id: crypto.randomUUID(), type: 'action', actionId };
|
|
715
|
+
}
|
|
1024
716
|
/** Visual builder for AND/OR permission logic trees */
|
|
1025
717
|
class LogicBuilderComponent {
|
|
1026
718
|
logic = input(null, ...(ngDevMode ? [{ debugName: "logic" }] : []));
|
|
1027
719
|
actions = input([], ...(ngDevMode ? [{ debugName: "actions" }] : []));
|
|
1028
720
|
logicChange = output();
|
|
1029
|
-
availableActions = computed(() => this.actions(), ...(ngDevMode ? [{ debugName: "availableActions" }] : []));
|
|
1030
721
|
/** Internal builder tree state (private writable + public readonly pattern) */
|
|
1031
722
|
_builderTree = signal(null, ...(ngDevMode ? [{ debugName: "_builderTree" }] : []));
|
|
1032
723
|
builderLogic = this._builderTree.asReadonly();
|
|
1033
724
|
constructor() {
|
|
1034
|
-
// Sync builderTree with logic input changes
|
|
1035
725
|
effect(() => {
|
|
1036
726
|
const logic = this.logic();
|
|
1037
727
|
if (!logic) {
|
|
@@ -1043,40 +733,24 @@ class LogicBuilderComponent {
|
|
|
1043
733
|
}, { allowSignalWrites: true });
|
|
1044
734
|
}
|
|
1045
735
|
initializeLogic() {
|
|
1046
|
-
this.
|
|
1047
|
-
id: crypto.randomUUID(),
|
|
1048
|
-
type: 'group',
|
|
1049
|
-
operator: 'AND',
|
|
1050
|
-
children: [],
|
|
1051
|
-
});
|
|
1052
|
-
this.emitChange();
|
|
736
|
+
this.updateTreeAndEmit(createGroupNode());
|
|
1053
737
|
}
|
|
1054
738
|
clearLogic() {
|
|
1055
739
|
this._builderTree.set(null);
|
|
1056
740
|
this.logicChange.emit(null);
|
|
1057
741
|
}
|
|
1058
742
|
toggleOperator(nodeId) {
|
|
1059
|
-
|
|
1060
|
-
if (!tree)
|
|
1061
|
-
return;
|
|
1062
|
-
this._builderTree.set(this.updateNodeInTree(tree, nodeId, (node) => ({
|
|
743
|
+
this.updateNode(nodeId, (node) => ({
|
|
1063
744
|
...node,
|
|
1064
745
|
operator: node.operator === 'AND' ? 'OR' : 'AND',
|
|
1065
|
-
}))
|
|
1066
|
-
this.emitChange();
|
|
746
|
+
}));
|
|
1067
747
|
}
|
|
1068
748
|
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) => ({
|
|
749
|
+
const newNode = type === 'group' ? createGroupNode() : createActionNode();
|
|
750
|
+
this.updateNode(parentId, (node) => ({
|
|
1076
751
|
...node,
|
|
1077
752
|
children: [...(node.children || []), newNode],
|
|
1078
|
-
}))
|
|
1079
|
-
this.emitChange();
|
|
753
|
+
}));
|
|
1080
754
|
}
|
|
1081
755
|
removeNode(nodeId) {
|
|
1082
756
|
const tree = this._builderTree();
|
|
@@ -1086,51 +760,45 @@ class LogicBuilderComponent {
|
|
|
1086
760
|
this.clearLogic();
|
|
1087
761
|
return;
|
|
1088
762
|
}
|
|
1089
|
-
this.
|
|
1090
|
-
this.emitChange();
|
|
763
|
+
this.updateTreeAndEmit(this.removeNodeFromTree(tree, nodeId));
|
|
1091
764
|
}
|
|
1092
765
|
updateActionId(nodeId, actionId) {
|
|
766
|
+
this.updateNode(nodeId, (node) => ({ ...node, actionId }));
|
|
767
|
+
}
|
|
768
|
+
/** Updates a node in the tree and emits the change */
|
|
769
|
+
updateNode(nodeId, updater) {
|
|
1093
770
|
const tree = this._builderTree();
|
|
1094
771
|
if (!tree)
|
|
1095
772
|
return;
|
|
1096
|
-
this.
|
|
1097
|
-
...node,
|
|
1098
|
-
actionId,
|
|
1099
|
-
})));
|
|
1100
|
-
this.emitChange();
|
|
773
|
+
this.updateTreeAndEmit(this.updateNodeInTree(tree, nodeId, updater));
|
|
1101
774
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
this.logicChange.emit(null);
|
|
1106
|
-
return;
|
|
1107
|
-
}
|
|
775
|
+
/** Sets the tree and emits the change */
|
|
776
|
+
updateTreeAndEmit(tree) {
|
|
777
|
+
this._builderTree.set(tree);
|
|
1108
778
|
this.logicChange.emit(toLogicNode(tree));
|
|
1109
779
|
}
|
|
1110
780
|
updateNodeInTree(node, targetId, updater) {
|
|
1111
781
|
if (node.id === targetId)
|
|
1112
782
|
return updater(node);
|
|
1113
|
-
if (node.children)
|
|
1114
|
-
return
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
}
|
|
1119
|
-
return node;
|
|
783
|
+
if (!node.children)
|
|
784
|
+
return node;
|
|
785
|
+
return {
|
|
786
|
+
...node,
|
|
787
|
+
children: node.children.map((child) => this.updateNodeInTree(child, targetId, updater)),
|
|
788
|
+
};
|
|
1120
789
|
}
|
|
1121
790
|
removeNodeFromTree(node, targetId) {
|
|
1122
|
-
if (node.children)
|
|
1123
|
-
return
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
}
|
|
1130
|
-
return node;
|
|
791
|
+
if (!node.children)
|
|
792
|
+
return node;
|
|
793
|
+
return {
|
|
794
|
+
...node,
|
|
795
|
+
children: node.children
|
|
796
|
+
.filter((child) => child.id !== targetId)
|
|
797
|
+
.map((child) => this.removeNodeFromTree(child, targetId)),
|
|
798
|
+
};
|
|
1131
799
|
}
|
|
1132
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
1133
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
800
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LogicBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
801
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", 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: `
|
|
1134
802
|
<div class="logic-builder">
|
|
1135
803
|
<div class="flex justify-between items-center mb-3">
|
|
1136
804
|
<h4 class="text-sm font-semibold m-0">Permission Logic</h4>
|
|
@@ -1190,8 +858,8 @@ class LogicBuilderComponent {
|
|
|
1190
858
|
class="action-select flex-1 p-2 border border-surface rounded text-sm"
|
|
1191
859
|
[ngModel]="node.actionId"
|
|
1192
860
|
(ngModelChange)="updateActionId(node.id, $event)">
|
|
1193
|
-
<option [value]="null">Select Action... ({{
|
|
1194
|
-
@for (action of
|
|
861
|
+
<option [value]="null">Select Action... ({{ actions().length }} available)</option>
|
|
862
|
+
@for (action of actions(); track action.id) {
|
|
1195
863
|
<option [ngValue]="action.id">{{ action.name }}</option>
|
|
1196
864
|
}
|
|
1197
865
|
</select>
|
|
@@ -1243,9 +911,9 @@ class LogicBuilderComponent {
|
|
|
1243
911
|
</ng-template>
|
|
1244
912
|
`, isInline: true, 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"], dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { 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: "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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1245
913
|
}
|
|
1246
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
914
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LogicBuilderComponent, decorators: [{
|
|
1247
915
|
type: Component,
|
|
1248
|
-
args: [{ selector: 'lib-logic-builder',
|
|
916
|
+
args: [{ selector: 'lib-logic-builder', imports: [AngularModule, PrimeModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1249
917
|
<div class="logic-builder">
|
|
1250
918
|
<div class="flex justify-between items-center mb-3">
|
|
1251
919
|
<h4 class="text-sm font-semibold m-0">Permission Logic</h4>
|
|
@@ -1305,8 +973,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
1305
973
|
class="action-select flex-1 p-2 border border-surface rounded text-sm"
|
|
1306
974
|
[ngModel]="node.actionId"
|
|
1307
975
|
(ngModelChange)="updateActionId(node.id, $event)">
|
|
1308
|
-
<option [value]="null">Select Action... ({{
|
|
1309
|
-
@for (action of
|
|
976
|
+
<option [value]="null">Select Action... ({{ actions().length }} available)</option>
|
|
977
|
+
@for (action of actions(); track action.id) {
|
|
1310
978
|
<option [ngValue]="action.id">{{ action.name }}</option>
|
|
1311
979
|
}
|
|
1312
980
|
</select>
|
|
@@ -1359,102 +1027,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
1359
1027
|
`, 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
1028
|
}], 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
1029
|
|
|
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
1030
|
function flattenTree(tree) {
|
|
1386
|
-
if (!tree?.length)
|
|
1031
|
+
if (!tree?.length)
|
|
1387
1032
|
return [];
|
|
1388
|
-
}
|
|
1389
1033
|
const result = [];
|
|
1390
1034
|
const flatten = (nodes) => {
|
|
1391
1035
|
for (const node of nodes) {
|
|
1392
1036
|
result.push(node);
|
|
1393
|
-
if (node.children?.length)
|
|
1037
|
+
if (node.children?.length)
|
|
1394
1038
|
flatten(node.children);
|
|
1395
|
-
}
|
|
1396
1039
|
}
|
|
1397
1040
|
};
|
|
1398
1041
|
flatten(tree);
|
|
1399
1042
|
return result;
|
|
1400
1043
|
}
|
|
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
1044
|
function buildTreeFromFlat(flatList) {
|
|
1445
|
-
if (!flatList?.length)
|
|
1045
|
+
if (!flatList?.length)
|
|
1446
1046
|
return [];
|
|
1447
|
-
}
|
|
1448
|
-
// Create map for O(1) lookup
|
|
1449
1047
|
const itemMap = new Map();
|
|
1450
1048
|
const rootItems = [];
|
|
1451
|
-
// First pass: Create all nodes with empty children arrays
|
|
1452
1049
|
flatList.forEach((item) => {
|
|
1453
|
-
if (item.id)
|
|
1050
|
+
if (item.id)
|
|
1454
1051
|
itemMap.set(item.id, { ...item, children: [] });
|
|
1455
|
-
}
|
|
1456
1052
|
});
|
|
1457
|
-
// Second pass: Build parent-child relationships
|
|
1458
1053
|
flatList.forEach((item) => {
|
|
1459
1054
|
if (!item.id)
|
|
1460
1055
|
return;
|
|
@@ -1462,51 +1057,23 @@ function buildTreeFromFlat(flatList) {
|
|
|
1462
1057
|
if (!node)
|
|
1463
1058
|
return;
|
|
1464
1059
|
if (!item.parentId) {
|
|
1465
|
-
// Root level item
|
|
1466
1060
|
rootItems.push(node);
|
|
1467
1061
|
}
|
|
1468
1062
|
else {
|
|
1469
|
-
// Child item - add to parent's children
|
|
1470
1063
|
const parent = itemMap.get(item.parentId);
|
|
1471
1064
|
if (parent) {
|
|
1472
1065
|
parent.children.push(node);
|
|
1473
1066
|
}
|
|
1474
1067
|
else {
|
|
1475
|
-
// Parent not found - treat as root
|
|
1476
1068
|
rootItems.push(node);
|
|
1477
1069
|
}
|
|
1478
1070
|
}
|
|
1479
1071
|
});
|
|
1480
1072
|
return rootItems;
|
|
1481
1073
|
}
|
|
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
1074
|
function convertActionToTreeNode(actions) {
|
|
1507
|
-
if (!actions?.length)
|
|
1075
|
+
if (!actions?.length)
|
|
1508
1076
|
return [];
|
|
1509
|
-
}
|
|
1510
1077
|
const convert = (action) => {
|
|
1511
1078
|
const { children, ...actionData } = action;
|
|
1512
1079
|
return {
|
|
@@ -1606,17 +1173,14 @@ class RoleActionSelectorComponent {
|
|
|
1606
1173
|
const selMap = this.selectionMap();
|
|
1607
1174
|
const allActions = this.actions();
|
|
1608
1175
|
const unmetSet = new Set();
|
|
1609
|
-
|
|
1610
|
-
if (action.id &&
|
|
1611
|
-
this.permissionLogic.hasUnmetPrerequisites(action, selMap, allActions)) {
|
|
1176
|
+
for (const action of allActions) {
|
|
1177
|
+
if (action.id && this.permissionLogic.hasUnmetPrerequisites(action, selMap, allActions)) {
|
|
1612
1178
|
unmetSet.add(action.id);
|
|
1613
1179
|
}
|
|
1614
|
-
}
|
|
1180
|
+
}
|
|
1615
1181
|
return unmetSet;
|
|
1616
1182
|
}, ...(ngDevMode ? [{ debugName: "actionsWithUnmetPrerequisites" }] : []));
|
|
1617
|
-
invalidActionsCount = computed(() => {
|
|
1618
|
-
return this.actionsWithUnmetPrerequisites().size;
|
|
1619
|
-
}, ...(ngDevMode ? [{ debugName: "invalidActionsCount" }] : []));
|
|
1183
|
+
invalidActionsCount = computed(() => this.actionsWithUnmetPrerequisites().size, ...(ngDevMode ? [{ debugName: "invalidActionsCount" }] : []));
|
|
1620
1184
|
// Computed - Pending Changes
|
|
1621
1185
|
pendingAdd = computed(() => {
|
|
1622
1186
|
const current = this.selectionMap();
|
|
@@ -1701,13 +1265,12 @@ class RoleActionSelectorComponent {
|
|
|
1701
1265
|
const assignedIds = new Set((assignedResponse?.data || []).map((a) => a.actionId));
|
|
1702
1266
|
// Build selection map
|
|
1703
1267
|
const selMap = {};
|
|
1704
|
-
|
|
1268
|
+
for (const action of flatActions) {
|
|
1705
1269
|
if (action.id) {
|
|
1706
1270
|
selMap[action.id] = assignedIds.has(action.id);
|
|
1707
1271
|
}
|
|
1708
|
-
}
|
|
1709
|
-
this.
|
|
1710
|
-
this._initialSelection.set({ ...selMap });
|
|
1272
|
+
}
|
|
1273
|
+
this.applySelection(selMap);
|
|
1711
1274
|
}
|
|
1712
1275
|
finally {
|
|
1713
1276
|
if (!signal.aborted) {
|
|
@@ -1756,35 +1319,20 @@ class RoleActionSelectorComponent {
|
|
|
1756
1319
|
this.permissionLogic.handleCheck(action, this.selectionMap(), this.actions(), (newMap) => this._selectionMap.set(newMap), (previousState) => this._selectionMap.set(previousState));
|
|
1757
1320
|
}
|
|
1758
1321
|
}
|
|
1759
|
-
/**
|
|
1760
|
-
* Toggle all actions
|
|
1761
|
-
*/
|
|
1762
1322
|
toggleAll() {
|
|
1763
|
-
|
|
1764
|
-
const selMap = {};
|
|
1765
|
-
this.actions().forEach((action) => {
|
|
1766
|
-
selMap[action.id] = newValue;
|
|
1767
|
-
});
|
|
1768
|
-
this._selectionMap.set(selMap);
|
|
1323
|
+
this.setAllSelection(!this.allSelected());
|
|
1769
1324
|
}
|
|
1770
|
-
/**
|
|
1771
|
-
* Select all actions
|
|
1772
|
-
*/
|
|
1773
1325
|
selectAll() {
|
|
1774
|
-
|
|
1775
|
-
this.actions().forEach((action) => {
|
|
1776
|
-
selMap[action.id] = true;
|
|
1777
|
-
});
|
|
1778
|
-
this._selectionMap.set(selMap);
|
|
1326
|
+
this.setAllSelection(true);
|
|
1779
1327
|
}
|
|
1780
|
-
/**
|
|
1781
|
-
* Deselect all actions
|
|
1782
|
-
*/
|
|
1783
1328
|
deselectAll() {
|
|
1329
|
+
this.setAllSelection(false);
|
|
1330
|
+
}
|
|
1331
|
+
setAllSelection(value) {
|
|
1784
1332
|
const selMap = {};
|
|
1785
|
-
this.actions()
|
|
1786
|
-
selMap[action.id] =
|
|
1787
|
-
}
|
|
1333
|
+
for (const action of this.actions()) {
|
|
1334
|
+
selMap[action.id] = value;
|
|
1335
|
+
}
|
|
1788
1336
|
this._selectionMap.set(selMap);
|
|
1789
1337
|
}
|
|
1790
1338
|
/**
|
|
@@ -1801,19 +1349,7 @@ class RoleActionSelectorComponent {
|
|
|
1801
1349
|
return;
|
|
1802
1350
|
}
|
|
1803
1351
|
// 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
|
-
});
|
|
1352
|
+
const items = this.buildPayloadItems();
|
|
1817
1353
|
if (items.length === 0)
|
|
1818
1354
|
return;
|
|
1819
1355
|
this.saving.set(true);
|
|
@@ -1837,17 +1373,28 @@ class RoleActionSelectorComponent {
|
|
|
1837
1373
|
this.saving.set(false);
|
|
1838
1374
|
}
|
|
1839
1375
|
}
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1376
|
+
applySelection(selMap) {
|
|
1377
|
+
this._selectionMap.set(selMap);
|
|
1378
|
+
this._initialSelection.set({ ...selMap });
|
|
1379
|
+
}
|
|
1380
|
+
buildPayloadItems() {
|
|
1381
|
+
const items = [];
|
|
1382
|
+
for (const action of this.pendingAdd()) {
|
|
1383
|
+
items.push({ id: action.id, action: 'add' });
|
|
1384
|
+
}
|
|
1385
|
+
for (const action of this.pendingRemove()) {
|
|
1386
|
+
items.push({ id: action.id, action: 'remove' });
|
|
1387
|
+
}
|
|
1388
|
+
return items;
|
|
1389
|
+
}
|
|
1843
1390
|
resetState() {
|
|
1844
1391
|
this._actionsTree.set([]);
|
|
1845
1392
|
this._actions.set([]);
|
|
1846
1393
|
this._selectionMap.set({});
|
|
1847
1394
|
this._initialSelection.set({});
|
|
1848
1395
|
}
|
|
1849
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
1850
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
1396
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: RoleActionSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1397
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: RoleActionSelectorComponent, isStandalone: true, selector: "flusys-role-action-selector", ngImport: i0, template: `
|
|
1851
1398
|
<div class="role-action-selector">
|
|
1852
1399
|
<!-- Role Selector -->
|
|
1853
1400
|
<div class="mb-4">
|
|
@@ -2070,9 +1617,9 @@ class RoleActionSelectorComponent {
|
|
|
2070
1617
|
</div>
|
|
2071
1618
|
`, 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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2072
1619
|
}
|
|
2073
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
1620
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: RoleActionSelectorComponent, decorators: [{
|
|
2074
1621
|
type: Component,
|
|
2075
|
-
args: [{ selector: 'flusys-role-action-selector',
|
|
1622
|
+
args: [{ selector: 'flusys-role-action-selector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective], template: `
|
|
2076
1623
|
<div class="role-action-selector">
|
|
2077
1624
|
<!-- Role Selector -->
|
|
2078
1625
|
<div class="mb-4">
|
|
@@ -2384,17 +1931,14 @@ class CompanyActionSelectorComponent {
|
|
|
2384
1931
|
const selMap = this.selectionMap();
|
|
2385
1932
|
const allActions = this.actions();
|
|
2386
1933
|
const unmetSet = new Set();
|
|
2387
|
-
|
|
2388
|
-
if (action.id &&
|
|
2389
|
-
this.permissionLogic.hasUnmetPrerequisites(action, selMap, allActions)) {
|
|
1934
|
+
for (const action of allActions) {
|
|
1935
|
+
if (action.id && this.permissionLogic.hasUnmetPrerequisites(action, selMap, allActions)) {
|
|
2390
1936
|
unmetSet.add(action.id);
|
|
2391
1937
|
}
|
|
2392
|
-
}
|
|
1938
|
+
}
|
|
2393
1939
|
return unmetSet;
|
|
2394
1940
|
}, ...(ngDevMode ? [{ debugName: "actionsWithUnmetPrerequisites" }] : []));
|
|
2395
|
-
invalidActionsCount = computed(() => {
|
|
2396
|
-
return this.actionsWithUnmetPrerequisites().size;
|
|
2397
|
-
}, ...(ngDevMode ? [{ debugName: "invalidActionsCount" }] : []));
|
|
1941
|
+
invalidActionsCount = computed(() => this.actionsWithUnmetPrerequisites().size, ...(ngDevMode ? [{ debugName: "invalidActionsCount" }] : []));
|
|
2398
1942
|
// Computed - Pending Changes
|
|
2399
1943
|
pendingAdd = computed(() => {
|
|
2400
1944
|
const current = this.selectionMap();
|
|
@@ -2478,13 +2022,7 @@ class CompanyActionSelectorComponent {
|
|
|
2478
2022
|
if (signal.aborted)
|
|
2479
2023
|
return;
|
|
2480
2024
|
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
|
-
});
|
|
2025
|
+
const selMap = this.buildSelectionMap(actionsList, (action) => assignedIds.has(action.id));
|
|
2488
2026
|
this._selectionMap.set(selMap);
|
|
2489
2027
|
this._initialSelection.set({ ...selMap });
|
|
2490
2028
|
}
|
|
@@ -2535,36 +2073,17 @@ class CompanyActionSelectorComponent {
|
|
|
2535
2073
|
this.permissionLogic.handleCheck(action, this.selectionMap(), this.actions(), (newMap) => this._selectionMap.set(newMap), (previousState) => this._selectionMap.set(previousState));
|
|
2536
2074
|
}
|
|
2537
2075
|
}
|
|
2538
|
-
/**
|
|
2539
|
-
* Toggle all actions
|
|
2540
|
-
*/
|
|
2541
2076
|
toggleAll() {
|
|
2542
|
-
|
|
2543
|
-
const selMap = {};
|
|
2544
|
-
this.actions().forEach((action) => {
|
|
2545
|
-
selMap[action.id] = newValue;
|
|
2546
|
-
});
|
|
2547
|
-
this._selectionMap.set(selMap);
|
|
2077
|
+
this.setAllSelection(!this.allSelected());
|
|
2548
2078
|
}
|
|
2549
|
-
/**
|
|
2550
|
-
* Select all actions
|
|
2551
|
-
*/
|
|
2552
2079
|
selectAll() {
|
|
2553
|
-
|
|
2554
|
-
this.actions().forEach((action) => {
|
|
2555
|
-
selMap[action.id] = true;
|
|
2556
|
-
});
|
|
2557
|
-
this._selectionMap.set(selMap);
|
|
2080
|
+
this.setAllSelection(true);
|
|
2558
2081
|
}
|
|
2559
|
-
/**
|
|
2560
|
-
* Deselect all actions
|
|
2561
|
-
*/
|
|
2562
2082
|
deselectAll() {
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
this._selectionMap.set(selMap);
|
|
2083
|
+
this.setAllSelection(false);
|
|
2084
|
+
}
|
|
2085
|
+
setAllSelection(value) {
|
|
2086
|
+
this._selectionMap.set(this.buildSelectionMap(this.actions(), () => value));
|
|
2568
2087
|
}
|
|
2569
2088
|
/**
|
|
2570
2089
|
* Save changes to backend
|
|
@@ -2579,20 +2098,7 @@ class CompanyActionSelectorComponent {
|
|
|
2579
2098
|
this.permissionLogic.showValidationErrorDialog(invalidActions, this.selectionMap(), this.actions(), (newMap) => this._selectionMap.set(newMap));
|
|
2580
2099
|
return;
|
|
2581
2100
|
}
|
|
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
|
-
});
|
|
2101
|
+
const items = this.buildPayloadItems();
|
|
2596
2102
|
if (items.length === 0)
|
|
2597
2103
|
return;
|
|
2598
2104
|
this.saving.set(true);
|
|
@@ -2670,17 +2176,31 @@ class CompanyActionSelectorComponent {
|
|
|
2670
2176
|
},
|
|
2671
2177
|
});
|
|
2672
2178
|
}
|
|
2673
|
-
/**
|
|
2674
|
-
* Reset component state
|
|
2675
|
-
*/
|
|
2676
2179
|
resetState() {
|
|
2677
2180
|
this._actionsTree.set([]);
|
|
2678
2181
|
this._actions.set([]);
|
|
2679
2182
|
this._selectionMap.set({});
|
|
2680
2183
|
this._initialSelection.set({});
|
|
2681
2184
|
}
|
|
2682
|
-
|
|
2683
|
-
|
|
2185
|
+
buildSelectionMap(actions, predicate) {
|
|
2186
|
+
const selMap = {};
|
|
2187
|
+
for (const action of actions) {
|
|
2188
|
+
selMap[action.id] = predicate(action);
|
|
2189
|
+
}
|
|
2190
|
+
return selMap;
|
|
2191
|
+
}
|
|
2192
|
+
buildPayloadItems() {
|
|
2193
|
+
const items = [];
|
|
2194
|
+
for (const action of this.pendingAdd()) {
|
|
2195
|
+
items.push({ id: action.id, action: 'add' });
|
|
2196
|
+
}
|
|
2197
|
+
for (const action of this.pendingRemove()) {
|
|
2198
|
+
items.push({ id: action.id, action: 'remove' });
|
|
2199
|
+
}
|
|
2200
|
+
return items;
|
|
2201
|
+
}
|
|
2202
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: CompanyActionSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2203
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: CompanyActionSelectorComponent, isStandalone: true, selector: "flusys-company-action-selector", ngImport: i0, template: `
|
|
2684
2204
|
<div class="company-action-selector">
|
|
2685
2205
|
<!-- Company Selector -->
|
|
2686
2206
|
<div class="mb-4">
|
|
@@ -2897,9 +2417,9 @@ class CompanyActionSelectorComponent {
|
|
|
2897
2417
|
</div>
|
|
2898
2418
|
`, 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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2899
2419
|
}
|
|
2900
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
2420
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: CompanyActionSelectorComponent, decorators: [{
|
|
2901
2421
|
type: Component,
|
|
2902
|
-
args: [{ selector: 'flusys-company-action-selector',
|
|
2422
|
+
args: [{ selector: 'flusys-company-action-selector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective], template: `
|
|
2903
2423
|
<div class="company-action-selector">
|
|
2904
2424
|
<!-- Company Selector -->
|
|
2905
2425
|
<div class="mb-4">
|
|
@@ -3157,9 +2677,6 @@ class UserRoleSelectorComponent {
|
|
|
3157
2677
|
roleApi = inject(RoleApiService);
|
|
3158
2678
|
permissionApi = inject(PermissionApiService);
|
|
3159
2679
|
messageService = inject(MessageService);
|
|
3160
|
-
destroyRef = inject(DestroyRef);
|
|
3161
|
-
// AbortController for data loading
|
|
3162
|
-
loadDataAbortController = null;
|
|
3163
2680
|
// State - User/Branch Selection
|
|
3164
2681
|
selectedUserId = signal(null, ...(ngDevMode ? [{ debugName: "selectedUserId" }] : []));
|
|
3165
2682
|
selectedBranchId = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedBranchId" }] : []));
|
|
@@ -3220,10 +2737,6 @@ class UserRoleSelectorComponent {
|
|
|
3220
2737
|
return this.hasChanges() && !this.saving();
|
|
3221
2738
|
}, ...(ngDevMode ? [{ debugName: "canSave" }] : []));
|
|
3222
2739
|
constructor() {
|
|
3223
|
-
// Cleanup on destroy
|
|
3224
|
-
this.destroyRef.onDestroy(() => {
|
|
3225
|
-
this.loadDataAbortController?.abort();
|
|
3226
|
-
});
|
|
3227
2740
|
// Effect: Load user branches and data when user changes
|
|
3228
2741
|
effect(() => {
|
|
3229
2742
|
const userId = this.selectedUserId();
|
|
@@ -3260,9 +2773,7 @@ class UserRoleSelectorComponent {
|
|
|
3260
2773
|
this.branches.set([]);
|
|
3261
2774
|
return;
|
|
3262
2775
|
}
|
|
3263
|
-
const response = await this.userPermissionProvider
|
|
3264
|
-
.getUserBranchPermissions(userId)
|
|
3265
|
-
.toPromise();
|
|
2776
|
+
const response = await firstValueFrom(this.userPermissionProvider.getUserBranchPermissions(userId));
|
|
3266
2777
|
const userBranches = response?.data?.map((ub) => ({
|
|
3267
2778
|
id: ub.branchId,
|
|
3268
2779
|
name: ub.branchName,
|
|
@@ -3285,20 +2796,16 @@ class UserRoleSelectorComponent {
|
|
|
3285
2796
|
this.loading.set(true);
|
|
3286
2797
|
try {
|
|
3287
2798
|
// Load all roles
|
|
3288
|
-
const rolesResponse = await this.roleApi
|
|
3289
|
-
.getAll('', {
|
|
2799
|
+
const rolesResponse = await firstValueFrom(this.roleApi.getAll('', {
|
|
3290
2800
|
pagination: { currentPage: 0, pageSize: MAX_DROPDOWN_ITEMS },
|
|
3291
|
-
})
|
|
3292
|
-
.toPromise();
|
|
2801
|
+
}));
|
|
3293
2802
|
const rolesList = rolesResponse?.data ?? [];
|
|
3294
2803
|
this._roles.set(rolesList);
|
|
3295
2804
|
// Load existing user-role assignments
|
|
3296
2805
|
const query = isCompanyFeatureEnabled(this.appConfig) && branchId
|
|
3297
2806
|
? { branchId }
|
|
3298
2807
|
: undefined;
|
|
3299
|
-
const assignedResponse = await this.permissionApi
|
|
3300
|
-
.getUserRoles(userId, query)
|
|
3301
|
-
.toPromise();
|
|
2808
|
+
const assignedResponse = await firstValueFrom(this.permissionApi.getUserRoles(userId, query));
|
|
3302
2809
|
const assignedIds = new Set((assignedResponse?.data || []).map((r) => r.roleId));
|
|
3303
2810
|
// Build selection map
|
|
3304
2811
|
const selMap = {};
|
|
@@ -3338,30 +2845,27 @@ class UserRoleSelectorComponent {
|
|
|
3338
2845
|
* Toggle all roles
|
|
3339
2846
|
*/
|
|
3340
2847
|
toggleAll() {
|
|
3341
|
-
|
|
3342
|
-
const selMap = {};
|
|
3343
|
-
this.roles().forEach((role) => {
|
|
3344
|
-
selMap[role.id] = newValue;
|
|
3345
|
-
});
|
|
3346
|
-
this._selectionMap.set(selMap);
|
|
2848
|
+
this.setAllSelections(!this.allSelected());
|
|
3347
2849
|
}
|
|
3348
2850
|
/**
|
|
3349
2851
|
* Select all roles
|
|
3350
2852
|
*/
|
|
3351
2853
|
selectAll() {
|
|
3352
|
-
|
|
3353
|
-
this.roles().forEach((role) => {
|
|
3354
|
-
selMap[role.id] = true;
|
|
3355
|
-
});
|
|
3356
|
-
this._selectionMap.set(selMap);
|
|
2854
|
+
this.setAllSelections(true);
|
|
3357
2855
|
}
|
|
3358
2856
|
/**
|
|
3359
2857
|
* Deselect all roles
|
|
3360
2858
|
*/
|
|
3361
2859
|
deselectAll() {
|
|
2860
|
+
this.setAllSelections(false);
|
|
2861
|
+
}
|
|
2862
|
+
/**
|
|
2863
|
+
* Set all role selections to a given value
|
|
2864
|
+
*/
|
|
2865
|
+
setAllSelections(value) {
|
|
3362
2866
|
const selMap = {};
|
|
3363
2867
|
this.roles().forEach((role) => {
|
|
3364
|
-
selMap[role.id] =
|
|
2868
|
+
selMap[role.id] = value;
|
|
3365
2869
|
});
|
|
3366
2870
|
this._selectionMap.set(selMap);
|
|
3367
2871
|
}
|
|
@@ -3404,9 +2908,7 @@ class UserRoleSelectorComponent {
|
|
|
3404
2908
|
payload.branchId = branchId;
|
|
3405
2909
|
}
|
|
3406
2910
|
}
|
|
3407
|
-
const response = await this.permissionApi
|
|
3408
|
-
.assignUserRoles(payload)
|
|
3409
|
-
.toPromise();
|
|
2911
|
+
const response = await firstValueFrom(this.permissionApi.assignUserRoles(payload));
|
|
3410
2912
|
this.messageService.add({
|
|
3411
2913
|
severity: 'success',
|
|
3412
2914
|
summary: 'Success',
|
|
@@ -3431,8 +2933,8 @@ class UserRoleSelectorComponent {
|
|
|
3431
2933
|
this._selectionMap.set({});
|
|
3432
2934
|
this._initialSelection.set({});
|
|
3433
2935
|
}
|
|
3434
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
3435
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
2936
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserRoleSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2937
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: UserRoleSelectorComponent, isStandalone: true, selector: "flusys-user-role-selector", ngImport: i0, template: `
|
|
3436
2938
|
<div class="user-role-selector">
|
|
3437
2939
|
<!-- User and Branch Selectors -->
|
|
3438
2940
|
<div class="surface-card p-4 rounded-border mb-4 shadow-sm">
|
|
@@ -3654,11 +3156,11 @@ class UserRoleSelectorComponent {
|
|
|
3654
3156
|
}
|
|
3655
3157
|
}
|
|
3656
3158
|
</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: ["
|
|
3159
|
+
`, 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
3160
|
}
|
|
3659
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
3161
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserRoleSelectorComponent, decorators: [{
|
|
3660
3162
|
type: Component,
|
|
3661
|
-
args: [{ selector: 'flusys-user-role-selector',
|
|
3163
|
+
args: [{ selector: 'flusys-user-role-selector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective, UserSelectComponent], template: `
|
|
3662
3164
|
<div class="user-role-selector">
|
|
3663
3165
|
<!-- User and Branch Selectors -->
|
|
3664
3166
|
<div class="surface-card p-4 rounded-border mb-4 shadow-sm">
|
|
@@ -3925,9 +3427,6 @@ class UserActionSelectorComponent {
|
|
|
3925
3427
|
permissionApi = inject(PermissionApiService);
|
|
3926
3428
|
permissionLogic = inject(ActionPermissionLogicService);
|
|
3927
3429
|
messageService = inject(MessageService);
|
|
3928
|
-
destroyRef = inject(DestroyRef);
|
|
3929
|
-
// AbortController for data loading
|
|
3930
|
-
loadDataAbortController = null;
|
|
3931
3430
|
// State - User/Branch Selection
|
|
3932
3431
|
selectedUserId = signal(null, ...(ngDevMode ? [{ debugName: "selectedUserId" }] : []));
|
|
3933
3432
|
selectedBranchId = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedBranchId" }] : []));
|
|
@@ -3946,8 +3445,10 @@ class UserActionSelectorComponent {
|
|
|
3946
3445
|
selectionMap = this._selectionMap.asReadonly();
|
|
3947
3446
|
_initialSelection = signal({}, ...(ngDevMode ? [{ debugName: "_initialSelection" }] : []));
|
|
3948
3447
|
initialSelection = this._initialSelection.asReadonly();
|
|
3448
|
+
// Computed - Company Feature Flag (cached)
|
|
3449
|
+
isCompanyFeatureActive = computed(() => isCompanyFeatureEnabled(this.appConfig) === true, ...(ngDevMode ? [{ debugName: "isCompanyFeatureActive" }] : []));
|
|
3949
3450
|
// Computed - UI Flags
|
|
3950
|
-
showBranchSelector =
|
|
3451
|
+
showBranchSelector = this.isCompanyFeatureActive;
|
|
3951
3452
|
filteredBranches = computed(() => {
|
|
3952
3453
|
const currentCompanyId = this.companyContext.currentCompanyInfo()?.id || undefined;
|
|
3953
3454
|
if (!currentCompanyId)
|
|
@@ -4009,15 +3510,11 @@ class UserActionSelectorComponent {
|
|
|
4009
3510
|
return (this.hasChanges() && !this.saving() && this.invalidActionsCount() === 0);
|
|
4010
3511
|
}, ...(ngDevMode ? [{ debugName: "canSave" }] : []));
|
|
4011
3512
|
constructor() {
|
|
4012
|
-
// Cleanup on destroy
|
|
4013
|
-
this.destroyRef.onDestroy(() => {
|
|
4014
|
-
this.loadDataAbortController?.abort();
|
|
4015
|
-
});
|
|
4016
3513
|
// Effect: Load user branches and data when user changes
|
|
4017
3514
|
effect(() => {
|
|
4018
3515
|
const userId = this.selectedUserId();
|
|
4019
3516
|
if (userId) {
|
|
4020
|
-
if (
|
|
3517
|
+
if (this.isCompanyFeatureActive()) {
|
|
4021
3518
|
this.loadUserBranches(userId);
|
|
4022
3519
|
}
|
|
4023
3520
|
this.loadData();
|
|
@@ -4032,9 +3529,7 @@ class UserActionSelectorComponent {
|
|
|
4032
3529
|
effect(() => {
|
|
4033
3530
|
const userId = this.selectedUserId();
|
|
4034
3531
|
const branchId = this.selectedBranchId();
|
|
4035
|
-
if (
|
|
4036
|
-
userId &&
|
|
4037
|
-
branchId !== undefined) {
|
|
3532
|
+
if (this.isCompanyFeatureActive() && userId && branchId !== undefined) {
|
|
4038
3533
|
this.loadData();
|
|
4039
3534
|
}
|
|
4040
3535
|
});
|
|
@@ -4043,15 +3538,13 @@ class UserActionSelectorComponent {
|
|
|
4043
3538
|
* Load user's permitted branches
|
|
4044
3539
|
*/
|
|
4045
3540
|
async loadUserBranches(userId) {
|
|
3541
|
+
const companyId = this.companyContext.currentCompanyInfo()?.id;
|
|
3542
|
+
if (!companyId) {
|
|
3543
|
+
this.branches.set([]);
|
|
3544
|
+
return;
|
|
3545
|
+
}
|
|
4046
3546
|
try {
|
|
4047
|
-
const
|
|
4048
|
-
if (!companyId) {
|
|
4049
|
-
this.branches.set([]);
|
|
4050
|
-
return;
|
|
4051
|
-
}
|
|
4052
|
-
const response = await this.userPermissionProvider
|
|
4053
|
-
.getUserBranchPermissions(userId)
|
|
4054
|
-
.toPromise();
|
|
3547
|
+
const response = await firstValueFrom(this.userPermissionProvider.getUserBranchPermissions(userId));
|
|
4055
3548
|
const userBranches = response?.data?.map((ub) => ({
|
|
4056
3549
|
id: ub.branchId,
|
|
4057
3550
|
name: ub.branchName,
|
|
@@ -4073,30 +3566,15 @@ class UserActionSelectorComponent {
|
|
|
4073
3566
|
const branchId = this.selectedBranchId();
|
|
4074
3567
|
this.loading.set(true);
|
|
4075
3568
|
try {
|
|
4076
|
-
|
|
4077
|
-
const actionsResponse = await this.actionApi
|
|
4078
|
-
.getActionsForPermission()
|
|
4079
|
-
.toPromise();
|
|
4080
|
-
// Get flat list (filtered by company if enabled)
|
|
3569
|
+
const actionsResponse = await firstValueFrom(this.actionApi.getActionsForPermission());
|
|
4081
3570
|
const flatActions = actionsResponse?.data ?? [];
|
|
4082
|
-
// Build tree structure from flat list with parentId
|
|
4083
3571
|
const actionsTree = buildTreeFromFlat(flatActions);
|
|
4084
|
-
// Store both representations
|
|
4085
3572
|
this._actionsTree.set(actionsTree);
|
|
4086
3573
|
this._actions.set(flatActions);
|
|
4087
|
-
|
|
4088
|
-
const
|
|
4089
|
-
? { branchId }
|
|
4090
|
-
: undefined;
|
|
4091
|
-
const assignedResponse = await this.permissionApi
|
|
4092
|
-
.getUserActions(userId, query)
|
|
4093
|
-
.toPromise();
|
|
3574
|
+
const query = this.isCompanyFeatureActive() && branchId ? { branchId } : undefined;
|
|
3575
|
+
const assignedResponse = await firstValueFrom(this.permissionApi.getUserActions(userId, query));
|
|
4094
3576
|
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
|
-
});
|
|
3577
|
+
const selMap = this.buildSelectionMap(flatActions, (action) => assignedIds.has(action.id));
|
|
4100
3578
|
this._selectionMap.set(selMap);
|
|
4101
3579
|
this._initialSelection.set({ ...selMap });
|
|
4102
3580
|
}
|
|
@@ -4148,48 +3626,25 @@ class UserActionSelectorComponent {
|
|
|
4148
3626
|
this.permissionLogic.handleCheck(action, this.selectionMap(), this.actions(), (newMap) => this._selectionMap.set(newMap), (previousState) => this._selectionMap.set(previousState));
|
|
4149
3627
|
}
|
|
4150
3628
|
}
|
|
4151
|
-
/**
|
|
4152
|
-
* Toggle all actions
|
|
4153
|
-
*/
|
|
4154
3629
|
toggleAll() {
|
|
4155
|
-
|
|
4156
|
-
const selMap = {};
|
|
4157
|
-
this.actions().forEach((action) => {
|
|
4158
|
-
selMap[action.id] = newValue;
|
|
4159
|
-
});
|
|
4160
|
-
this._selectionMap.set(selMap);
|
|
3630
|
+
this.setAllActions(!this.allSelected());
|
|
4161
3631
|
}
|
|
4162
|
-
/**
|
|
4163
|
-
* Select all actions
|
|
4164
|
-
*/
|
|
4165
3632
|
selectAll() {
|
|
4166
|
-
|
|
4167
|
-
this.actions().forEach((action) => {
|
|
4168
|
-
selMap[action.id] = true;
|
|
4169
|
-
});
|
|
4170
|
-
this._selectionMap.set(selMap);
|
|
3633
|
+
this.setAllActions(true);
|
|
4171
3634
|
}
|
|
4172
|
-
/**
|
|
4173
|
-
* Deselect all actions
|
|
4174
|
-
*/
|
|
4175
3635
|
deselectAll() {
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
this._selectionMap.set(selMap);
|
|
3636
|
+
this.setAllActions(false);
|
|
3637
|
+
}
|
|
3638
|
+
setAllActions(value) {
|
|
3639
|
+
this._selectionMap.set(this.buildSelectionMap(this.actions(), () => value));
|
|
4181
3640
|
}
|
|
4182
|
-
/**
|
|
4183
|
-
* Save changes to backend
|
|
4184
|
-
*/
|
|
4185
3641
|
async saveChanges() {
|
|
4186
3642
|
const userId = this.selectedUserId();
|
|
4187
3643
|
if (!userId)
|
|
4188
3644
|
return;
|
|
4189
3645
|
const branchId = this.selectedBranchId();
|
|
4190
|
-
const companyId = this.companyContext.currentCompanyInfo()?.id
|
|
4191
|
-
|
|
4192
|
-
if (isCompanyFeatureEnabled(this.appConfig) && !companyId) {
|
|
3646
|
+
const companyId = this.companyContext.currentCompanyInfo()?.id;
|
|
3647
|
+
if (this.isCompanyFeatureActive() && !companyId) {
|
|
4193
3648
|
this.messageService.add({
|
|
4194
3649
|
severity: 'warn',
|
|
4195
3650
|
summary: 'Company Required',
|
|
@@ -4197,49 +3652,30 @@ class UserActionSelectorComponent {
|
|
|
4197
3652
|
});
|
|
4198
3653
|
return;
|
|
4199
3654
|
}
|
|
4200
|
-
// Pre-save validation
|
|
4201
3655
|
const invalidActions = this.permissionLogic.getActionsWithUnmetPrerequisites(this.selectionMap(), this.actions());
|
|
4202
3656
|
if (invalidActions.length > 0) {
|
|
4203
3657
|
this.permissionLogic.showValidationErrorDialog(invalidActions, this.selectionMap(), this.actions(), (newMap) => this._selectionMap.set(newMap));
|
|
4204
3658
|
return;
|
|
4205
3659
|
}
|
|
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
|
-
});
|
|
3660
|
+
const items = this.buildPermissionItems();
|
|
4220
3661
|
if (items.length === 0)
|
|
4221
3662
|
return;
|
|
4222
3663
|
this.saving.set(true);
|
|
4223
3664
|
try {
|
|
4224
3665
|
const payload = { userId, items };
|
|
4225
|
-
if (
|
|
4226
|
-
if (companyId)
|
|
3666
|
+
if (this.isCompanyFeatureActive()) {
|
|
3667
|
+
if (companyId)
|
|
4227
3668
|
payload.companyId = companyId;
|
|
4228
|
-
|
|
4229
|
-
if (branchId) {
|
|
3669
|
+
if (branchId)
|
|
4230
3670
|
payload.branchId = branchId;
|
|
4231
|
-
}
|
|
4232
3671
|
}
|
|
4233
|
-
const response = await this.permissionApi
|
|
4234
|
-
.assignUserActions(payload)
|
|
4235
|
-
.toPromise();
|
|
3672
|
+
const response = await firstValueFrom(this.permissionApi.assignUserActions(payload));
|
|
4236
3673
|
this.messageService.add({
|
|
4237
3674
|
severity: 'success',
|
|
4238
3675
|
summary: 'Success',
|
|
4239
3676
|
detail: response?.data?.message ||
|
|
4240
3677
|
'User action permissions updated successfully',
|
|
4241
3678
|
});
|
|
4242
|
-
// Update baseline
|
|
4243
3679
|
this._initialSelection.set({ ...this.selectionMap() });
|
|
4244
3680
|
}
|
|
4245
3681
|
catch {
|
|
@@ -4249,17 +3685,31 @@ class UserActionSelectorComponent {
|
|
|
4249
3685
|
this.saving.set(false);
|
|
4250
3686
|
}
|
|
4251
3687
|
}
|
|
4252
|
-
/**
|
|
4253
|
-
* Reset component state
|
|
4254
|
-
*/
|
|
4255
3688
|
resetState() {
|
|
4256
3689
|
this._actionsTree.set([]);
|
|
4257
3690
|
this._actions.set([]);
|
|
4258
3691
|
this._selectionMap.set({});
|
|
4259
3692
|
this._initialSelection.set({});
|
|
4260
3693
|
}
|
|
4261
|
-
|
|
4262
|
-
|
|
3694
|
+
buildSelectionMap(actions, predicate) {
|
|
3695
|
+
const selMap = {};
|
|
3696
|
+
actions.forEach((action) => {
|
|
3697
|
+
selMap[action.id] = predicate(action);
|
|
3698
|
+
});
|
|
3699
|
+
return selMap;
|
|
3700
|
+
}
|
|
3701
|
+
buildPermissionItems() {
|
|
3702
|
+
const items = [];
|
|
3703
|
+
this.pendingAdd().forEach((action) => {
|
|
3704
|
+
items.push({ id: action.id, action: 'add' });
|
|
3705
|
+
});
|
|
3706
|
+
this.pendingRemove().forEach((action) => {
|
|
3707
|
+
items.push({ id: action.id, action: 'remove' });
|
|
3708
|
+
});
|
|
3709
|
+
return items;
|
|
3710
|
+
}
|
|
3711
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserActionSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3712
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: UserActionSelectorComponent, isStandalone: true, selector: "flusys-user-action-selector", ngImport: i0, template: `
|
|
4263
3713
|
<div class="user-action-selector">
|
|
4264
3714
|
<!-- User and Branch Selectors -->
|
|
4265
3715
|
<div class="surface-card p-4 rounded-border mb-4 shadow-sm">
|
|
@@ -4513,11 +3963,11 @@ class UserActionSelectorComponent {
|
|
|
4513
3963
|
}
|
|
4514
3964
|
}
|
|
4515
3965
|
</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: ["
|
|
3966
|
+
`, 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
3967
|
}
|
|
4518
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
3968
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserActionSelectorComponent, decorators: [{
|
|
4519
3969
|
type: Component,
|
|
4520
|
-
args: [{ selector: 'flusys-user-action-selector',
|
|
3970
|
+
args: [{ selector: 'flusys-user-action-selector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule, HasPermissionDirective, UserSelectComponent], template: `
|
|
4521
3971
|
<div class="user-action-selector">
|
|
4522
3972
|
<!-- User and Branch Selectors -->
|
|
4523
3973
|
<div class="surface-card p-4 rounded-border mb-4 shadow-sm">
|
|
@@ -4821,97 +4271,83 @@ class ProfilePermissionProviderAdapter {
|
|
|
4821
4271
|
})),
|
|
4822
4272
|
})));
|
|
4823
4273
|
}
|
|
4824
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
4825
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
4274
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ProfilePermissionProviderAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4275
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ProfilePermissionProviderAdapter });
|
|
4826
4276
|
}
|
|
4827
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
4277
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ProfilePermissionProviderAdapter, decorators: [{
|
|
4828
4278
|
type: Injectable
|
|
4829
4279
|
}] });
|
|
4830
4280
|
|
|
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
|
-
*/
|
|
4281
|
+
/** Registers IAM provider adapters for ng-shared interfaces */
|
|
4850
4282
|
function provideIamProviders() {
|
|
4851
4283
|
return [
|
|
4852
|
-
|
|
4853
|
-
{
|
|
4854
|
-
provide: PROFILE_PERMISSION_PROVIDER,
|
|
4855
|
-
useClass: ProfilePermissionProviderAdapter,
|
|
4856
|
-
},
|
|
4284
|
+
{ provide: PROFILE_PERMISSION_PROVIDER, useClass: ProfilePermissionProviderAdapter },
|
|
4857
4285
|
];
|
|
4858
4286
|
}
|
|
4859
4287
|
|
|
4860
4288
|
/**
|
|
4861
4289
|
* IAM Routes Configuration
|
|
4862
4290
|
*
|
|
4863
|
-
* Identity and Access Management routing
|
|
4864
|
-
* - Actions: Permission actions
|
|
4291
|
+
* Identity and Access Management routing with permission guards.
|
|
4292
|
+
* - Actions: Permission actions management
|
|
4865
4293
|
* - 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.
|
|
4294
|
+
* - Permissions: User permission assignments
|
|
4869
4295
|
*/
|
|
4870
4296
|
const IAM_ROUTES = [
|
|
4871
4297
|
{
|
|
4872
4298
|
path: '',
|
|
4873
|
-
loadComponent: () => import('./flusys-ng-iam-iam-container.component-
|
|
4299
|
+
loadComponent: () => import('./flusys-ng-iam-iam-container.component-UYJjqYV9.mjs').then((m) => m.IamContainerComponent),
|
|
4874
4300
|
children: [
|
|
4875
4301
|
// Actions Management
|
|
4876
4302
|
{
|
|
4877
4303
|
path: 'actions',
|
|
4304
|
+
canActivate: [permissionGuard(ACTION_PERMISSIONS.READ)],
|
|
4878
4305
|
children: [
|
|
4879
4306
|
{
|
|
4880
4307
|
path: '',
|
|
4881
|
-
loadComponent: () => import('./flusys-ng-iam-action-list-page.component-
|
|
4308
|
+
loadComponent: () => import('./flusys-ng-iam-action-list-page.component-BtJlGcTj.mjs').then((m) => m.ActionListPageComponent),
|
|
4882
4309
|
},
|
|
4883
4310
|
{
|
|
4884
4311
|
path: 'new',
|
|
4885
|
-
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-
|
|
4312
|
+
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-eXpZNJ_H.mjs').then((m) => m.ActionFormPageComponent),
|
|
4886
4313
|
},
|
|
4887
4314
|
{
|
|
4888
4315
|
path: ':id',
|
|
4889
|
-
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-
|
|
4316
|
+
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-eXpZNJ_H.mjs').then((m) => m.ActionFormPageComponent),
|
|
4890
4317
|
},
|
|
4891
4318
|
],
|
|
4892
4319
|
},
|
|
4893
4320
|
// Roles Management
|
|
4894
4321
|
{
|
|
4895
4322
|
path: 'roles',
|
|
4323
|
+
canActivate: [permissionGuard(ROLE_PERMISSIONS.READ)],
|
|
4896
4324
|
children: [
|
|
4897
4325
|
{
|
|
4898
4326
|
path: '',
|
|
4899
|
-
loadComponent: () => import('./flusys-ng-iam-role-list-page.component-
|
|
4327
|
+
loadComponent: () => import('./flusys-ng-iam-role-list-page.component-D4J1by6Q.mjs').then((m) => m.RoleListPageComponent),
|
|
4900
4328
|
},
|
|
4901
4329
|
{
|
|
4902
4330
|
path: 'new',
|
|
4903
|
-
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-
|
|
4331
|
+
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-D_AAEay2.mjs').then((m) => m.RoleFormPageComponent),
|
|
4904
4332
|
},
|
|
4905
4333
|
{
|
|
4906
4334
|
path: ':id',
|
|
4907
|
-
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-
|
|
4335
|
+
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-D_AAEay2.mjs').then((m) => m.RoleFormPageComponent),
|
|
4908
4336
|
},
|
|
4909
4337
|
],
|
|
4910
4338
|
},
|
|
4911
|
-
// Permissions Management (
|
|
4339
|
+
// Permissions Management (requires any permission tab access)
|
|
4912
4340
|
{
|
|
4913
4341
|
path: 'permissions',
|
|
4914
|
-
|
|
4342
|
+
canActivate: [
|
|
4343
|
+
anyPermissionGuard([
|
|
4344
|
+
ROLE_ACTION_PERMISSIONS.READ,
|
|
4345
|
+
USER_ROLE_PERMISSIONS.READ,
|
|
4346
|
+
USER_ACTION_PERMISSIONS.READ,
|
|
4347
|
+
COMPANY_ACTION_PERMISSIONS.READ,
|
|
4348
|
+
]),
|
|
4349
|
+
],
|
|
4350
|
+
loadComponent: () => import('./flusys-ng-iam-permission-page.component-DcgT7L3_.mjs').then((m) => m.PermissionPageComponent),
|
|
4915
4351
|
},
|
|
4916
4352
|
// Default redirect to actions
|
|
4917
4353
|
{
|
|
@@ -4930,4 +4366,4 @@ const IAM_ROUTES = [
|
|
|
4930
4366
|
*/
|
|
4931
4367
|
|
|
4932
4368
|
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-
|
|
4369
|
+
//# sourceMappingURL=flusys-ng-iam-flusys-ng-iam-CJAQT60K.mjs.map
|