@flusys/ng-iam 1.0.0-rc → 1.1.1-beta
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 +24 -175
- package/fesm2022/{flusys-ng-iam-action-form-page.component-C_BRrrWW.mjs → flusys-ng-iam-action-form-page.component-C1j10Qhw.mjs} +202 -174
- package/fesm2022/flusys-ng-iam-action-form-page.component-C1j10Qhw.mjs.map +1 -0
- package/fesm2022/flusys-ng-iam-action-list-page.component-BCzSardO.mjs +281 -0
- package/fesm2022/flusys-ng-iam-action-list-page.component-BCzSardO.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-flusys-ng-iam-BPIpfrjN.mjs → flusys-ng-iam-flusys-ng-iam-DISrddPh.mjs} +829 -822
- package/fesm2022/flusys-ng-iam-flusys-ng-iam-DISrddPh.mjs.map +1 -0
- package/fesm2022/flusys-ng-iam-iam-container.component-BkhqmzLi.mjs +97 -0
- package/fesm2022/flusys-ng-iam-iam-container.component-BkhqmzLi.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-permission-page.component-CmxOBJPu.mjs → flusys-ng-iam-permission-page.component-BSQFPt_N.mjs} +12 -41
- package/fesm2022/flusys-ng-iam-permission-page.component-BSQFPt_N.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-role-form-page.component-ByNueI1a.mjs → flusys-ng-iam-role-form-page.component-Cqziu_BM.mjs} +134 -106
- package/fesm2022/flusys-ng-iam-role-form-page.component-Cqziu_BM.mjs.map +1 -0
- package/fesm2022/flusys-ng-iam-role-list-page.component-BObCxHiB.mjs +266 -0
- package/fesm2022/flusys-ng-iam-role-list-page.component-BObCxHiB.mjs.map +1 -0
- package/fesm2022/flusys-ng-iam.mjs +1 -1
- package/package.json +2 -2
- package/types/flusys-ng-iam.d.ts +21 -40
- 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 +0 -289
- 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 +0 -92
- 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 +0 -316
- package/fesm2022/flusys-ng-iam-role-list-page.component-CFly5KnH.mjs.map +0 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Injectable, signal, input, output, computed,
|
|
3
|
-
import { HttpClient } from '@angular/common/http';
|
|
4
|
-
import { ApiResourceService, PermissionValidatorService, AngularModule, PrimeModule,
|
|
2
|
+
import { inject, Injectable, signal, input, output, computed, ChangeDetectionStrategy, Component, effect } from '@angular/core';
|
|
3
|
+
import { HttpClient, HttpParams } from '@angular/common/http';
|
|
4
|
+
import { ApiResourceService, PermissionValidatorService, AngularModule, PrimeModule, COMPANY_API_PROVIDER, USER_PERMISSION_PROVIDER, UserSelectComponent, PROFILE_PERMISSION_PROVIDER } from '@flusys/ng-shared';
|
|
5
5
|
import { APP_CONFIG, BaseApiService, isCompanyFeatureEnabled } from '@flusys/ng-core';
|
|
6
|
+
import * as i2$1 from 'primeng/api';
|
|
6
7
|
import { ConfirmationService, MessageService } from 'primeng/api';
|
|
7
8
|
import { of, firstValueFrom, map as map$1 } from 'rxjs';
|
|
8
9
|
import { tap, catchError, map } from 'rxjs/operators';
|
|
@@ -11,11 +12,12 @@ import { CommonModule } from '@angular/common';
|
|
|
11
12
|
import * as i1$1 from '@angular/forms';
|
|
12
13
|
import { FormsModule } from '@angular/forms';
|
|
13
14
|
import * as i2 from 'primeng/button';
|
|
14
|
-
import
|
|
15
|
-
import * as
|
|
16
|
-
import * as i4 from 'primeng/
|
|
17
|
-
import * as i5 from 'primeng/
|
|
18
|
-
import * as
|
|
15
|
+
import { ButtonModule } from 'primeng/button';
|
|
16
|
+
import * as i7 from 'primeng/tooltip';
|
|
17
|
+
import * as i4 from 'primeng/checkbox';
|
|
18
|
+
import * as i5 from 'primeng/select';
|
|
19
|
+
import * as i6 from 'primeng/tag';
|
|
20
|
+
import * as i8 from 'primeng/treetable';
|
|
19
21
|
import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
|
|
20
22
|
import * as i5$1 from 'primeng/table';
|
|
21
23
|
|
|
@@ -81,11 +83,11 @@ class ActionApiService extends ApiResourceService {
|
|
|
81
83
|
}
|
|
82
84
|
/**
|
|
83
85
|
* Get actions for permission assignment
|
|
84
|
-
*
|
|
86
|
+
* GET /iam/actions/tree-for-permission
|
|
85
87
|
* Returns actions filtered by company whitelist if enabled
|
|
86
88
|
*/
|
|
87
89
|
getActionsForPermission() {
|
|
88
|
-
return this.http.
|
|
90
|
+
return this.http.get(`${this.appConfig.apiBaseUrl}/iam/actions/tree-for-permission`);
|
|
89
91
|
}
|
|
90
92
|
/**
|
|
91
93
|
* Get actions in hierarchical tree structure
|
|
@@ -833,10 +835,14 @@ class PermissionApiService extends BaseApiService {
|
|
|
833
835
|
}
|
|
834
836
|
/**
|
|
835
837
|
* Get user's direct action permissions
|
|
836
|
-
*
|
|
838
|
+
* GET /permissions/user-actions/:userId
|
|
837
839
|
*/
|
|
838
840
|
getUserActions(userId, query) {
|
|
839
|
-
|
|
841
|
+
let params = new HttpParams();
|
|
842
|
+
if (query?.branchId) {
|
|
843
|
+
params = params.set('branchId', query.branchId);
|
|
844
|
+
}
|
|
845
|
+
return this.http.get(this.getUrl(`permissions/user-actions/${userId}`), { params });
|
|
840
846
|
}
|
|
841
847
|
// =============================================================================
|
|
842
848
|
// User → Role (Role Assignments)
|
|
@@ -850,10 +856,14 @@ class PermissionApiService extends BaseApiService {
|
|
|
850
856
|
}
|
|
851
857
|
/**
|
|
852
858
|
* Get user's role assignments
|
|
853
|
-
*
|
|
859
|
+
* GET /permissions/user-roles/:userId
|
|
854
860
|
*/
|
|
855
861
|
getUserRoles(userId, query) {
|
|
856
|
-
|
|
862
|
+
let params = new HttpParams();
|
|
863
|
+
if (query?.branchId) {
|
|
864
|
+
params = params.set('branchId', query.branchId);
|
|
865
|
+
}
|
|
866
|
+
return this.http.get(this.getUrl(`permissions/user-roles/${userId}`), { params });
|
|
857
867
|
}
|
|
858
868
|
// =============================================================================
|
|
859
869
|
// Role → Action (Role Permissions)
|
|
@@ -867,10 +877,10 @@ class PermissionApiService extends BaseApiService {
|
|
|
867
877
|
}
|
|
868
878
|
/**
|
|
869
879
|
* Get role's action permissions
|
|
870
|
-
*
|
|
880
|
+
* GET /permissions/role-actions/:roleId
|
|
871
881
|
*/
|
|
872
882
|
getRoleActions(roleId, query) {
|
|
873
|
-
return this.http.
|
|
883
|
+
return this.http.get(this.getUrl(`permissions/role-actions/${roleId}`));
|
|
874
884
|
}
|
|
875
885
|
// =============================================================================
|
|
876
886
|
// Company → Action (Company Whitelisting)
|
|
@@ -884,10 +894,10 @@ class PermissionApiService extends BaseApiService {
|
|
|
884
894
|
}
|
|
885
895
|
/**
|
|
886
896
|
* Get company's whitelisted actions
|
|
887
|
-
*
|
|
897
|
+
* GET /permissions/company-actions/:companyId
|
|
888
898
|
*/
|
|
889
899
|
getCompanyActions(companyId) {
|
|
890
|
-
return this.http.
|
|
900
|
+
return this.http.get(this.getUrl(`permissions/company-actions/${companyId}`));
|
|
891
901
|
}
|
|
892
902
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PermissionApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
893
903
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PermissionApiService, providedIn: 'root' });
|
|
@@ -1027,85 +1037,77 @@ class LogicBuilderComponent {
|
|
|
1027
1037
|
actions = input([], ...(ngDevMode ? [{ debugName: "actions" }] : []));
|
|
1028
1038
|
logicChange = output();
|
|
1029
1039
|
availableActions = computed(() => this.actions(), ...(ngDevMode ? [{ debugName: "availableActions" }] : []));
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
}
|
|
1043
|
-
}, { allowSignalWrites: true });
|
|
1044
|
-
}
|
|
1040
|
+
builderTree = null;
|
|
1041
|
+
builderLogic = computed(() => {
|
|
1042
|
+
const logic = this.logic();
|
|
1043
|
+
if (!logic) {
|
|
1044
|
+
this.builderTree = null;
|
|
1045
|
+
return null;
|
|
1046
|
+
}
|
|
1047
|
+
if (!this.builderTree) {
|
|
1048
|
+
this.builderTree = toBuilderNode(logic);
|
|
1049
|
+
}
|
|
1050
|
+
return this.builderTree;
|
|
1051
|
+
}, ...(ngDevMode ? [{ debugName: "builderLogic" }] : []));
|
|
1045
1052
|
initializeLogic() {
|
|
1046
|
-
this.
|
|
1053
|
+
this.builderTree = {
|
|
1047
1054
|
id: crypto.randomUUID(),
|
|
1048
1055
|
type: 'group',
|
|
1049
1056
|
operator: 'AND',
|
|
1050
1057
|
children: [],
|
|
1051
|
-
}
|
|
1058
|
+
};
|
|
1052
1059
|
this.emitChange();
|
|
1053
1060
|
}
|
|
1054
1061
|
clearLogic() {
|
|
1055
|
-
this.
|
|
1062
|
+
this.builderTree = null;
|
|
1056
1063
|
this.logicChange.emit(null);
|
|
1057
1064
|
}
|
|
1058
1065
|
toggleOperator(nodeId) {
|
|
1059
|
-
|
|
1060
|
-
if (!tree)
|
|
1066
|
+
if (!this.builderTree)
|
|
1061
1067
|
return;
|
|
1062
|
-
this.
|
|
1068
|
+
this.builderTree = this.updateNodeInTree(this.builderTree, nodeId, (node) => ({
|
|
1063
1069
|
...node,
|
|
1064
1070
|
operator: node.operator === 'AND' ? 'OR' : 'AND',
|
|
1065
|
-
}))
|
|
1071
|
+
}));
|
|
1066
1072
|
this.emitChange();
|
|
1067
1073
|
}
|
|
1068
1074
|
addChildNode(parentId, type) {
|
|
1069
|
-
|
|
1070
|
-
if (!tree)
|
|
1075
|
+
if (!this.builderTree)
|
|
1071
1076
|
return;
|
|
1072
1077
|
const newNode = type === 'group'
|
|
1073
1078
|
? { id: crypto.randomUUID(), type: 'group', operator: 'AND', children: [] }
|
|
1074
1079
|
: { id: crypto.randomUUID(), type: 'action', actionId: '' };
|
|
1075
|
-
this.
|
|
1080
|
+
this.builderTree = this.updateNodeInTree(this.builderTree, parentId, (node) => ({
|
|
1076
1081
|
...node,
|
|
1077
1082
|
children: [...(node.children || []), newNode],
|
|
1078
|
-
}))
|
|
1083
|
+
}));
|
|
1079
1084
|
this.emitChange();
|
|
1080
1085
|
}
|
|
1081
1086
|
removeNode(nodeId) {
|
|
1082
|
-
|
|
1083
|
-
if (!tree)
|
|
1087
|
+
if (!this.builderTree)
|
|
1084
1088
|
return;
|
|
1085
|
-
if (
|
|
1089
|
+
if (this.builderTree.id === nodeId) {
|
|
1086
1090
|
this.clearLogic();
|
|
1087
1091
|
return;
|
|
1088
1092
|
}
|
|
1089
|
-
this.
|
|
1093
|
+
this.builderTree = this.removeNodeFromTree(this.builderTree, nodeId);
|
|
1090
1094
|
this.emitChange();
|
|
1091
1095
|
}
|
|
1092
1096
|
updateActionId(nodeId, actionId) {
|
|
1093
|
-
|
|
1094
|
-
if (!tree)
|
|
1097
|
+
if (!this.builderTree)
|
|
1095
1098
|
return;
|
|
1096
|
-
this.
|
|
1099
|
+
this.builderTree = this.updateNodeInTree(this.builderTree, nodeId, (node) => ({
|
|
1097
1100
|
...node,
|
|
1098
1101
|
actionId,
|
|
1099
|
-
}))
|
|
1102
|
+
}));
|
|
1100
1103
|
this.emitChange();
|
|
1101
1104
|
}
|
|
1102
1105
|
emitChange() {
|
|
1103
|
-
|
|
1104
|
-
if (!tree) {
|
|
1106
|
+
if (!this.builderTree) {
|
|
1105
1107
|
this.logicChange.emit(null);
|
|
1106
1108
|
return;
|
|
1107
1109
|
}
|
|
1108
|
-
this.logicChange.emit(toLogicNode(
|
|
1110
|
+
this.logicChange.emit(toLogicNode(this.builderTree));
|
|
1109
1111
|
}
|
|
1110
1112
|
updateNodeInTree(node, targetId, updater) {
|
|
1111
1113
|
if (node.id === targetId)
|
|
@@ -1133,7 +1135,7 @@ class LogicBuilderComponent {
|
|
|
1133
1135
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: LogicBuilderComponent, isStandalone: true, selector: "lib-logic-builder", inputs: { logic: { classPropertyName: "logic", publicName: "logic", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { logicChange: "logicChange" }, ngImport: i0, template: `
|
|
1134
1136
|
<div class="logic-builder">
|
|
1135
1137
|
<div class="flex justify-between items-center mb-3">
|
|
1136
|
-
<h4 class="text-sm font-semibold
|
|
1138
|
+
<h4 class="text-sm font-semibold">Permission Logic</h4>
|
|
1137
1139
|
@if (!builderLogic()) {
|
|
1138
1140
|
<p-button
|
|
1139
1141
|
label="Add Logic"
|
|
@@ -1152,13 +1154,13 @@ class LogicBuilderComponent {
|
|
|
1152
1154
|
</div>
|
|
1153
1155
|
|
|
1154
1156
|
@if (builderLogic()) {
|
|
1155
|
-
<div class="border
|
|
1157
|
+
<div class="border rounded p-3 bg-surface-50">
|
|
1156
1158
|
<div class="mb-3 text-sm text-muted-color">
|
|
1157
1159
|
Define permission requirements using AND/OR logic with actions
|
|
1158
1160
|
</div>
|
|
1159
1161
|
|
|
1160
1162
|
<!-- Root Node -->
|
|
1161
|
-
<div class="
|
|
1163
|
+
<div class="logic-tree bg-surface-0 rounded border">
|
|
1162
1164
|
<ng-container *ngTemplateOutlet="nodeTemplate; context: { $implicit: builderLogic()!, depth: 0 }"></ng-container>
|
|
1163
1165
|
</div>
|
|
1164
1166
|
</div>
|
|
@@ -1167,11 +1169,11 @@ class LogicBuilderComponent {
|
|
|
1167
1169
|
|
|
1168
1170
|
<!-- Recursive Node Template -->
|
|
1169
1171
|
<ng-template #nodeTemplate let-node let-depth="depth">
|
|
1170
|
-
<div class="
|
|
1171
|
-
<div class="
|
|
1172
|
+
<div class="node" [class]="depth % 2 === 0 ? 'bg-surface-0' : 'bg-surface-50'">
|
|
1173
|
+
<div class="node-header">
|
|
1172
1174
|
<span class="node-type" [ngClass]="node.type">{{ node.type.toUpperCase() }}</span>
|
|
1173
1175
|
|
|
1174
|
-
<div class="
|
|
1176
|
+
<div class="node-content">
|
|
1175
1177
|
@if (node.type === 'group') {
|
|
1176
1178
|
<!-- Group: show operator toggle -->
|
|
1177
1179
|
<span
|
|
@@ -1187,7 +1189,7 @@ class LogicBuilderComponent {
|
|
|
1187
1189
|
@if (node.type === 'action') {
|
|
1188
1190
|
<!-- Action: show action selector -->
|
|
1189
1191
|
<select
|
|
1190
|
-
class="
|
|
1192
|
+
class="flex-1"
|
|
1191
1193
|
[ngModel]="node.actionId"
|
|
1192
1194
|
(ngModelChange)="updateActionId(node.id, $event)">
|
|
1193
1195
|
<option [value]="null">Select Action... ({{ availableActions().length }} available)</option>
|
|
@@ -1198,7 +1200,7 @@ class LogicBuilderComponent {
|
|
|
1198
1200
|
}
|
|
1199
1201
|
</div>
|
|
1200
1202
|
|
|
1201
|
-
<div class="
|
|
1203
|
+
<div class="node-actions">
|
|
1202
1204
|
<p-button
|
|
1203
1205
|
icon="pi pi-trash"
|
|
1204
1206
|
[text]="true"
|
|
@@ -1211,7 +1213,7 @@ class LogicBuilderComponent {
|
|
|
1211
1213
|
|
|
1212
1214
|
<!-- Children for group nodes -->
|
|
1213
1215
|
@if (node.type === 'group' && node.children && node.children.length > 0) {
|
|
1214
|
-
<div class="
|
|
1216
|
+
<div class="node-children">
|
|
1215
1217
|
@for (child of node.children; track child.id) {
|
|
1216
1218
|
<ng-container *ngTemplateOutlet="nodeTemplate; context: { $implicit: child, depth: depth + 1 }"></ng-container>
|
|
1217
1219
|
}
|
|
@@ -1220,9 +1222,9 @@ class LogicBuilderComponent {
|
|
|
1220
1222
|
|
|
1221
1223
|
<!-- Add child buttons for group nodes -->
|
|
1222
1224
|
@if (node.type === 'group') {
|
|
1223
|
-
<div class="
|
|
1225
|
+
<div class="add-child-section">
|
|
1224
1226
|
<div class="text-xs font-semibold text-muted-color mb-2">Add Condition:</div>
|
|
1225
|
-
<div class="
|
|
1227
|
+
<div class="add-child-buttons">
|
|
1226
1228
|
<p-button
|
|
1227
1229
|
label="Group"
|
|
1228
1230
|
icon="pi pi-sitemap"
|
|
@@ -1241,14 +1243,14 @@ class LogicBuilderComponent {
|
|
|
1241
1243
|
}
|
|
1242
1244
|
</div>
|
|
1243
1245
|
</ng-template>
|
|
1244
|
-
`, 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)}
|
|
1246
|
+
`, isInline: true, styles: [":host{display:block}.logic-tree{font-size:.875rem}.node{padding:.75rem;border-bottom:1px solid var(--p-surface-200, #e5e7eb)}.node:last-child{border-bottom:none}.node-header{display:flex;align-items:center;gap:.75rem;margin-bottom:.5rem}.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)}.node-content{flex:1;display:flex;align-items:center;gap:.5rem}.node-actions{display:flex;gap:.25rem}.node-children{margin-left:1.5rem;margin-top:.5rem;padding-left:1rem;border-left:2px solid var(--p-surface-200, #e5e7eb)}.operator-badge{display:inline-flex;align-items:center;padding:.125rem .5rem;border-radius:9999px;font-weight:600;font-size:.75rem;cursor:pointer;transition:all .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)}.operator-badge:hover{opacity:.8}select{padding:.375rem .75rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.375rem;font-size:.875rem;background-color:var(--p-surface-0, white);color:var(--p-text-color, inherit)}select:focus{outline:none;border-color:var(--p-primary-color, #3b82f6);box-shadow:0 0 0 3px rgba(var(--p-primary-color-rgb, 59, 130, 246),.1)}.add-child-section{margin-top:.75rem;padding:.75rem;background-color:var(--p-surface-50, #f9fafb);border-radius:.375rem;border:1px dashed var(--p-surface-300, #d1d5db)}.add-child-buttons{display:flex;gap:.5rem;flex-wrap:wrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { 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: AngularModule }, { 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: i7.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: "ngmodule", type: ButtonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1245
1247
|
}
|
|
1246
1248
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LogicBuilderComponent, decorators: [{
|
|
1247
1249
|
type: Component,
|
|
1248
|
-
args: [{ selector: 'lib-logic-builder', standalone: true, imports: [AngularModule, PrimeModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1250
|
+
args: [{ selector: 'lib-logic-builder', standalone: true, imports: [CommonModule, FormsModule, AngularModule, PrimeModule, ButtonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1249
1251
|
<div class="logic-builder">
|
|
1250
1252
|
<div class="flex justify-between items-center mb-3">
|
|
1251
|
-
<h4 class="text-sm font-semibold
|
|
1253
|
+
<h4 class="text-sm font-semibold">Permission Logic</h4>
|
|
1252
1254
|
@if (!builderLogic()) {
|
|
1253
1255
|
<p-button
|
|
1254
1256
|
label="Add Logic"
|
|
@@ -1267,13 +1269,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
1267
1269
|
</div>
|
|
1268
1270
|
|
|
1269
1271
|
@if (builderLogic()) {
|
|
1270
|
-
<div class="border
|
|
1272
|
+
<div class="border rounded p-3 bg-surface-50">
|
|
1271
1273
|
<div class="mb-3 text-sm text-muted-color">
|
|
1272
1274
|
Define permission requirements using AND/OR logic with actions
|
|
1273
1275
|
</div>
|
|
1274
1276
|
|
|
1275
1277
|
<!-- Root Node -->
|
|
1276
|
-
<div class="
|
|
1278
|
+
<div class="logic-tree bg-surface-0 rounded border">
|
|
1277
1279
|
<ng-container *ngTemplateOutlet="nodeTemplate; context: { $implicit: builderLogic()!, depth: 0 }"></ng-container>
|
|
1278
1280
|
</div>
|
|
1279
1281
|
</div>
|
|
@@ -1282,11 +1284,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
1282
1284
|
|
|
1283
1285
|
<!-- Recursive Node Template -->
|
|
1284
1286
|
<ng-template #nodeTemplate let-node let-depth="depth">
|
|
1285
|
-
<div class="
|
|
1286
|
-
<div class="
|
|
1287
|
+
<div class="node" [class]="depth % 2 === 0 ? 'bg-surface-0' : 'bg-surface-50'">
|
|
1288
|
+
<div class="node-header">
|
|
1287
1289
|
<span class="node-type" [ngClass]="node.type">{{ node.type.toUpperCase() }}</span>
|
|
1288
1290
|
|
|
1289
|
-
<div class="
|
|
1291
|
+
<div class="node-content">
|
|
1290
1292
|
@if (node.type === 'group') {
|
|
1291
1293
|
<!-- Group: show operator toggle -->
|
|
1292
1294
|
<span
|
|
@@ -1302,7 +1304,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
1302
1304
|
@if (node.type === 'action') {
|
|
1303
1305
|
<!-- Action: show action selector -->
|
|
1304
1306
|
<select
|
|
1305
|
-
class="
|
|
1307
|
+
class="flex-1"
|
|
1306
1308
|
[ngModel]="node.actionId"
|
|
1307
1309
|
(ngModelChange)="updateActionId(node.id, $event)">
|
|
1308
1310
|
<option [value]="null">Select Action... ({{ availableActions().length }} available)</option>
|
|
@@ -1313,7 +1315,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
1313
1315
|
}
|
|
1314
1316
|
</div>
|
|
1315
1317
|
|
|
1316
|
-
<div class="
|
|
1318
|
+
<div class="node-actions">
|
|
1317
1319
|
<p-button
|
|
1318
1320
|
icon="pi pi-trash"
|
|
1319
1321
|
[text]="true"
|
|
@@ -1326,7 +1328,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
1326
1328
|
|
|
1327
1329
|
<!-- Children for group nodes -->
|
|
1328
1330
|
@if (node.type === 'group' && node.children && node.children.length > 0) {
|
|
1329
|
-
<div class="
|
|
1331
|
+
<div class="node-children">
|
|
1330
1332
|
@for (child of node.children; track child.id) {
|
|
1331
1333
|
<ng-container *ngTemplateOutlet="nodeTemplate; context: { $implicit: child, depth: depth + 1 }"></ng-container>
|
|
1332
1334
|
}
|
|
@@ -1335,9 +1337,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
1335
1337
|
|
|
1336
1338
|
<!-- Add child buttons for group nodes -->
|
|
1337
1339
|
@if (node.type === 'group') {
|
|
1338
|
-
<div class="
|
|
1340
|
+
<div class="add-child-section">
|
|
1339
1341
|
<div class="text-xs font-semibold text-muted-color mb-2">Add Condition:</div>
|
|
1340
|
-
<div class="
|
|
1342
|
+
<div class="add-child-buttons">
|
|
1341
1343
|
<p-button
|
|
1342
1344
|
label="Group"
|
|
1343
1345
|
icon="pi pi-sitemap"
|
|
@@ -1356,8 +1358,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
1356
1358
|
}
|
|
1357
1359
|
</div>
|
|
1358
1360
|
</ng-template>
|
|
1359
|
-
`, 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)}
|
|
1360
|
-
}],
|
|
1361
|
+
`, styles: [":host{display:block}.logic-tree{font-size:.875rem}.node{padding:.75rem;border-bottom:1px solid var(--p-surface-200, #e5e7eb)}.node:last-child{border-bottom:none}.node-header{display:flex;align-items:center;gap:.75rem;margin-bottom:.5rem}.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)}.node-content{flex:1;display:flex;align-items:center;gap:.5rem}.node-actions{display:flex;gap:.25rem}.node-children{margin-left:1.5rem;margin-top:.5rem;padding-left:1rem;border-left:2px solid var(--p-surface-200, #e5e7eb)}.operator-badge{display:inline-flex;align-items:center;padding:.125rem .5rem;border-radius:9999px;font-weight:600;font-size:.75rem;cursor:pointer;transition:all .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)}.operator-badge:hover{opacity:.8}select{padding:.375rem .75rem;border:1px solid var(--p-surface-300, #d1d5db);border-radius:.375rem;font-size:.875rem;background-color:var(--p-surface-0, white);color:var(--p-text-color, inherit)}select:focus{outline:none;border-color:var(--p-primary-color, #3b82f6);box-shadow:0 0 0 3px rgba(var(--p-primary-color-rgb, 59, 130, 246),.1)}.add-child-section{margin-top:.75rem;padding:.75rem;background-color:var(--p-surface-50, #f9fafb);border-radius:.375rem;border:1px dashed var(--p-surface-300, #d1d5db)}.add-child-buttons{display:flex;gap:.5rem;flex-wrap:wrap}\n"] }]
|
|
1362
|
+
}], 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
1363
|
|
|
1362
1364
|
/**
|
|
1363
1365
|
* Tree Utility Functions
|
|
@@ -1554,10 +1556,7 @@ function convertActionToTreeNode(actions) {
|
|
|
1554
1556
|
* ```
|
|
1555
1557
|
*/
|
|
1556
1558
|
class RoleActionSelectorComponent {
|
|
1557
|
-
// Permission constants for template
|
|
1558
|
-
ROLE_ACTION_PERMISSIONS = ROLE_ACTION_PERMISSIONS;
|
|
1559
1559
|
// Dependencies
|
|
1560
|
-
destroyRef = inject(DestroyRef);
|
|
1561
1560
|
roleApi = inject(RoleApiService);
|
|
1562
1561
|
actionApi = inject(ActionApiService);
|
|
1563
1562
|
permissionApi = inject(PermissionApiService);
|
|
@@ -1638,10 +1637,6 @@ class RoleActionSelectorComponent {
|
|
|
1638
1637
|
loadDataAbortController = null;
|
|
1639
1638
|
constructor() {
|
|
1640
1639
|
this.loadRoles();
|
|
1641
|
-
// Cleanup on destroy
|
|
1642
|
-
this.destroyRef.onDestroy(() => {
|
|
1643
|
-
this.loadDataAbortController?.abort();
|
|
1644
|
-
});
|
|
1645
1640
|
// Effect: Load data when role selection changes
|
|
1646
1641
|
effect(() => {
|
|
1647
1642
|
const roleId = this.selectedRoleId();
|
|
@@ -1651,7 +1646,11 @@ class RoleActionSelectorComponent {
|
|
|
1651
1646
|
this.loadDataAbortController = new AbortController();
|
|
1652
1647
|
this.onRoleChange(this.loadDataAbortController.signal).catch((err) => {
|
|
1653
1648
|
if (err.name !== 'AbortError') {
|
|
1654
|
-
|
|
1649
|
+
this.messageService.add({
|
|
1650
|
+
severity: 'error',
|
|
1651
|
+
summary: 'Error',
|
|
1652
|
+
detail: 'Failed to load role permissions. Please refresh.',
|
|
1653
|
+
});
|
|
1655
1654
|
this.loading.set(false);
|
|
1656
1655
|
}
|
|
1657
1656
|
});
|
|
@@ -1661,6 +1660,9 @@ class RoleActionSelectorComponent {
|
|
|
1661
1660
|
}
|
|
1662
1661
|
});
|
|
1663
1662
|
}
|
|
1663
|
+
ngOnDestroy() {
|
|
1664
|
+
this.loadDataAbortController?.abort();
|
|
1665
|
+
}
|
|
1664
1666
|
/**
|
|
1665
1667
|
* Load roles from API
|
|
1666
1668
|
*/
|
|
@@ -1672,7 +1674,11 @@ class RoleActionSelectorComponent {
|
|
|
1672
1674
|
this.roles.set(response?.data ?? []);
|
|
1673
1675
|
}
|
|
1674
1676
|
catch {
|
|
1675
|
-
|
|
1677
|
+
this.messageService.add({
|
|
1678
|
+
severity: 'error',
|
|
1679
|
+
summary: 'Error',
|
|
1680
|
+
detail: 'Failed to load roles',
|
|
1681
|
+
});
|
|
1676
1682
|
}
|
|
1677
1683
|
}
|
|
1678
1684
|
/**
|
|
@@ -1830,8 +1836,13 @@ class RoleActionSelectorComponent {
|
|
|
1830
1836
|
// Update baseline
|
|
1831
1837
|
this._initialSelection.set({ ...this.selectionMap() });
|
|
1832
1838
|
}
|
|
1833
|
-
catch {
|
|
1834
|
-
|
|
1839
|
+
catch (err) {
|
|
1840
|
+
const error = err;
|
|
1841
|
+
this.messageService.add({
|
|
1842
|
+
severity: 'error',
|
|
1843
|
+
summary: 'Error',
|
|
1844
|
+
detail: error?.error?.message || 'Failed to update role permissions',
|
|
1845
|
+
});
|
|
1835
1846
|
}
|
|
1836
1847
|
finally {
|
|
1837
1848
|
this.saving.set(false);
|
|
@@ -1851,12 +1862,11 @@ class RoleActionSelectorComponent {
|
|
|
1851
1862
|
<div class="role-action-selector">
|
|
1852
1863
|
<!-- Role Selector -->
|
|
1853
1864
|
<div class="mb-4">
|
|
1854
|
-
<div class="grid
|
|
1855
|
-
<div>
|
|
1865
|
+
<div class="grid">
|
|
1866
|
+
<div class="col-12 md:col-6 lg:col-4">
|
|
1856
1867
|
<label class="block font-semibold mb-2">Select Role</label>
|
|
1857
1868
|
<p-select
|
|
1858
|
-
[ngModel]="selectedRoleId
|
|
1859
|
-
(ngModelChange)="selectedRoleId.set($event)"
|
|
1869
|
+
[(ngModel)]="selectedRoleId"
|
|
1860
1870
|
[options]="roles()"
|
|
1861
1871
|
optionLabel="name"
|
|
1862
1872
|
optionValue="id"
|
|
@@ -1873,21 +1883,24 @@ class RoleActionSelectorComponent {
|
|
|
1873
1883
|
<!-- Loading State -->
|
|
1874
1884
|
@if (loading()) {
|
|
1875
1885
|
<div
|
|
1876
|
-
class="surface-card p-5
|
|
1886
|
+
class="surface-card p-5 border-round shadow-1 flex justify-content-center"
|
|
1877
1887
|
>
|
|
1878
|
-
<i
|
|
1888
|
+
<i
|
|
1889
|
+
class="pi pi-spin pi-spinner text-primary"
|
|
1890
|
+
style="font-size: 3rem"
|
|
1891
|
+
></i>
|
|
1879
1892
|
</div>
|
|
1880
1893
|
}
|
|
1881
1894
|
|
|
1882
1895
|
<!-- Action List -->
|
|
1883
1896
|
@if (!loading() && actions().length > 0) {
|
|
1884
|
-
<div class="surface-card p-4
|
|
1897
|
+
<div class="surface-card p-4 border-round shadow-1">
|
|
1885
1898
|
<div
|
|
1886
|
-
class="flex flex-
|
|
1899
|
+
class="flex flex-column md:flex-row justify-content-between align-items-start md:align-items-center gap-3 mb-4"
|
|
1887
1900
|
>
|
|
1888
1901
|
<div>
|
|
1889
1902
|
<h5 class="m-0 mb-1">Action Permissions</h5>
|
|
1890
|
-
<p class="text-sm text-
|
|
1903
|
+
<p class="text-sm text-color-secondary m-0">
|
|
1891
1904
|
{{ actions().length }} actions available
|
|
1892
1905
|
</p>
|
|
1893
1906
|
</div>
|
|
@@ -1909,7 +1922,6 @@ class RoleActionSelectorComponent {
|
|
|
1909
1922
|
(onClick)="deselectAll()"
|
|
1910
1923
|
/>
|
|
1911
1924
|
<p-button
|
|
1912
|
-
*hasPermission="ROLE_ACTION_PERMISSIONS.ASSIGN"
|
|
1913
1925
|
label="Save Changes"
|
|
1914
1926
|
icon="pi pi-save"
|
|
1915
1927
|
[disabled]="!canSave()"
|
|
@@ -1923,12 +1935,14 @@ class RoleActionSelectorComponent {
|
|
|
1923
1935
|
|
|
1924
1936
|
<!-- Validation Warning -->
|
|
1925
1937
|
@if (invalidActionsCount() > 0) {
|
|
1926
|
-
<div class="
|
|
1927
|
-
<div class="flex items-
|
|
1928
|
-
<i
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1938
|
+
<div class="bg-warn/10 border-l-4 border-warn p-4 mb-4">
|
|
1939
|
+
<div class="flex items-center">
|
|
1940
|
+
<i
|
|
1941
|
+
class="pi pi-exclamation-triangle text-warn mr-2"
|
|
1942
|
+
></i>
|
|
1943
|
+
<div>
|
|
1944
|
+
<strong class="text-warn">Validation Warning:</strong>
|
|
1945
|
+
<p class="text-sm text-warn mt-1">
|
|
1932
1946
|
{{ invalidActionsCount() }} selected
|
|
1933
1947
|
action{{ invalidActionsCount() > 1 ? 's have' : ' has' }}
|
|
1934
1948
|
unmet prerequisites. Fix before saving or use auto-fix on
|
|
@@ -1940,33 +1954,33 @@ class RoleActionSelectorComponent {
|
|
|
1940
1954
|
}
|
|
1941
1955
|
|
|
1942
1956
|
<!-- Action Tree Table -->
|
|
1943
|
-
<
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
<ng-template
|
|
1968
|
-
<tr [class.
|
|
1969
|
-
<td
|
|
1957
|
+
<p-treeTable
|
|
1958
|
+
[value]="treeNodes()"
|
|
1959
|
+
[scrollable]="true"
|
|
1960
|
+
scrollHeight="flex"
|
|
1961
|
+
dataKey="id"
|
|
1962
|
+
styleClass="p-treetable-sm"
|
|
1963
|
+
>
|
|
1964
|
+
<ng-template pTemplate="header">
|
|
1965
|
+
<tr>
|
|
1966
|
+
<th style="width: 3rem">
|
|
1967
|
+
<p-checkbox
|
|
1968
|
+
[ngModel]="allSelected()"
|
|
1969
|
+
[binary]="true"
|
|
1970
|
+
(ngModelChange)="toggleAll()"
|
|
1971
|
+
pTooltip="Select/Deselect All"
|
|
1972
|
+
tooltipPosition="top"
|
|
1973
|
+
/>
|
|
1974
|
+
</th>
|
|
1975
|
+
<th>Name</th>
|
|
1976
|
+
<th>Code</th>
|
|
1977
|
+
<th>Type</th>
|
|
1978
|
+
<th>Requirements</th>
|
|
1979
|
+
</tr>
|
|
1980
|
+
</ng-template>
|
|
1981
|
+
<ng-template pTemplate="body" let-rowNode let-rowData="rowData">
|
|
1982
|
+
<tr [class.bg-danger/10]="hasUnmetPrerequisites(rowData)">
|
|
1983
|
+
<td style="width: 3rem">
|
|
1970
1984
|
<p-checkbox
|
|
1971
1985
|
[ngModel]="selectionMap()[rowData.id]"
|
|
1972
1986
|
[binary]="true"
|
|
@@ -1977,18 +1991,18 @@ class RoleActionSelectorComponent {
|
|
|
1977
1991
|
</td>
|
|
1978
1992
|
<td>
|
|
1979
1993
|
<p-treeTableToggler [rowNode]="rowNode" />
|
|
1980
|
-
<span class="inline-flex items-center gap-2">
|
|
1994
|
+
<span class="inline-flex align-items-center gap-2">
|
|
1981
1995
|
{{ rowData.name }}
|
|
1982
1996
|
@if (hasUnmetPrerequisites(rowData)) {
|
|
1983
1997
|
<i
|
|
1984
|
-
class="pi pi-exclamation-triangle text-
|
|
1998
|
+
class="pi pi-exclamation-triangle text-warn"
|
|
1985
1999
|
pTooltip="This action has unmet prerequisites and will fail validation on save"
|
|
1986
2000
|
tooltipPosition="top"
|
|
1987
2001
|
></i>
|
|
1988
2002
|
}
|
|
1989
2003
|
</span>
|
|
1990
2004
|
</td>
|
|
1991
|
-
<td
|
|
2005
|
+
<td>{{ rowData.code || '-' }}</td>
|
|
1992
2006
|
<td>
|
|
1993
2007
|
<p-tag
|
|
1994
2008
|
[value]="rowData.actionType"
|
|
@@ -1997,41 +2011,42 @@ class RoleActionSelectorComponent {
|
|
|
1997
2011
|
"
|
|
1998
2012
|
/>
|
|
1999
2013
|
</td>
|
|
2000
|
-
<td
|
|
2014
|
+
<td>
|
|
2001
2015
|
@if (rowData.permissionLogic) {
|
|
2002
|
-
<span class="text-sm text-muted
|
|
2016
|
+
<span class="text-sm text-muted">Has prerequisites</span>
|
|
2003
2017
|
} @else {
|
|
2004
|
-
<span class="text-muted
|
|
2018
|
+
<span class="text-muted">-</span>
|
|
2005
2019
|
}
|
|
2006
2020
|
</td>
|
|
2007
2021
|
</tr>
|
|
2008
2022
|
</ng-template>
|
|
2009
|
-
<ng-template
|
|
2023
|
+
<ng-template pTemplate="emptymessage">
|
|
2010
2024
|
<tr>
|
|
2011
|
-
<td colspan="5" class="text-center p-4
|
|
2025
|
+
<td colspan="5" class="text-center p-4">
|
|
2012
2026
|
@if (loading()) {
|
|
2013
2027
|
<i class="pi pi-spin pi-spinner"></i> Loading actions...
|
|
2014
2028
|
} @else { No actions available. }
|
|
2015
2029
|
</td>
|
|
2016
2030
|
</tr>
|
|
2017
2031
|
</ng-template>
|
|
2018
|
-
|
|
2019
|
-
</div>
|
|
2032
|
+
</p-treeTable>
|
|
2020
2033
|
</div>
|
|
2021
2034
|
|
|
2022
2035
|
<!-- Change Summary -->
|
|
2023
2036
|
@if (hasChanges()) {
|
|
2024
|
-
<div class="border border-
|
|
2025
|
-
<div class="flex items-center gap-2 mb-3">
|
|
2026
|
-
<i class="pi pi-info-circle text-primary"></i>
|
|
2027
|
-
<
|
|
2037
|
+
<div class="surface-border border-1 border-round p-3 mt-4">
|
|
2038
|
+
<div class="flex align-items-center gap-2 mb-3">
|
|
2039
|
+
<i class="pi pi-info-circle text-primary content-center"></i>
|
|
2040
|
+
<p class="m-0 mb-1 font-bold">Pending Changes</p>
|
|
2028
2041
|
</div>
|
|
2029
|
-
<div class="
|
|
2042
|
+
<div class="flex flex-col md:flex-row gap-4">
|
|
2030
2043
|
@if (pendingAdd().length > 0) {
|
|
2031
|
-
<div>
|
|
2032
|
-
<div class="flex items-center gap-2 mb-2">
|
|
2033
|
-
<i class="pi pi-plus-circle text-
|
|
2034
|
-
<strong class="text-sm"
|
|
2044
|
+
<div class="w-full md:w-1/2">
|
|
2045
|
+
<div class="flex align-items-center gap-2 mb-2">
|
|
2046
|
+
<i class="pi pi-plus-circle text-success"></i>
|
|
2047
|
+
<strong class="text-sm"
|
|
2048
|
+
>To Add ({{ pendingAdd().length }})</strong
|
|
2049
|
+
>
|
|
2035
2050
|
</div>
|
|
2036
2051
|
<ul class="list-none p-0 m-0 pl-4">
|
|
2037
2052
|
@for (action of pendingAdd(); track action.id) {
|
|
@@ -2041,10 +2056,12 @@ class RoleActionSelectorComponent {
|
|
|
2041
2056
|
</div>
|
|
2042
2057
|
}
|
|
2043
2058
|
@if (pendingRemove().length > 0) {
|
|
2044
|
-
<div>
|
|
2045
|
-
<div class="flex items-center gap-2 mb-2">
|
|
2046
|
-
<i class="pi pi-minus-circle text-
|
|
2047
|
-
<strong class="text-sm"
|
|
2059
|
+
<div class="w-full md:w-1/2">
|
|
2060
|
+
<div class="flex align-items-center gap-2 mb-2">
|
|
2061
|
+
<i class="pi pi-minus-circle text-danger"></i>
|
|
2062
|
+
<strong class="text-sm"
|
|
2063
|
+
>To Remove ({{ pendingRemove().length }})</strong
|
|
2064
|
+
>
|
|
2048
2065
|
</div>
|
|
2049
2066
|
<ul class="list-none p-0 m-0 pl-4">
|
|
2050
2067
|
@for (action of pendingRemove(); track action.id) {
|
|
@@ -2059,29 +2076,31 @@ class RoleActionSelectorComponent {
|
|
|
2059
2076
|
}
|
|
2060
2077
|
|
|
2061
2078
|
@if (!loading() && actions().length === 0) {
|
|
2062
|
-
<div class="surface-card p-5
|
|
2063
|
-
<i
|
|
2064
|
-
|
|
2079
|
+
<div class="surface-card p-5 border-round shadow-1 text-center">
|
|
2080
|
+
<i
|
|
2081
|
+
class="pi pi-info-circle text-color-secondary mb-3"
|
|
2082
|
+
style="font-size: 3rem; display: block;"
|
|
2083
|
+
></i>
|
|
2084
|
+
<p class="text-color-secondary m-0">
|
|
2065
2085
|
No actions available for this role.
|
|
2066
2086
|
</p>
|
|
2067
2087
|
</div>
|
|
2068
2088
|
}
|
|
2069
2089
|
}
|
|
2070
2090
|
</div>
|
|
2071
|
-
`, isInline: true, styles: [":host{display:block}
|
|
2091
|
+
`, 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: "directive", type: i2$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { 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: i4.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: i5.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: i6.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i7.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: i8.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: i8.TreeTableToggler, selector: "p-treeTableToggler, p-treetabletoggler, p-treetable-toggler", inputs: ["rowNode"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2072
2092
|
}
|
|
2073
2093
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoleActionSelectorComponent, decorators: [{
|
|
2074
2094
|
type: Component,
|
|
2075
|
-
args: [{ selector: 'flusys-role-action-selector', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule
|
|
2095
|
+
args: [{ selector: 'flusys-role-action-selector', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule], template: `
|
|
2076
2096
|
<div class="role-action-selector">
|
|
2077
2097
|
<!-- Role Selector -->
|
|
2078
2098
|
<div class="mb-4">
|
|
2079
|
-
<div class="grid
|
|
2080
|
-
<div>
|
|
2099
|
+
<div class="grid">
|
|
2100
|
+
<div class="col-12 md:col-6 lg:col-4">
|
|
2081
2101
|
<label class="block font-semibold mb-2">Select Role</label>
|
|
2082
2102
|
<p-select
|
|
2083
|
-
[ngModel]="selectedRoleId
|
|
2084
|
-
(ngModelChange)="selectedRoleId.set($event)"
|
|
2103
|
+
[(ngModel)]="selectedRoleId"
|
|
2085
2104
|
[options]="roles()"
|
|
2086
2105
|
optionLabel="name"
|
|
2087
2106
|
optionValue="id"
|
|
@@ -2098,21 +2117,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
2098
2117
|
<!-- Loading State -->
|
|
2099
2118
|
@if (loading()) {
|
|
2100
2119
|
<div
|
|
2101
|
-
class="surface-card p-5
|
|
2120
|
+
class="surface-card p-5 border-round shadow-1 flex justify-content-center"
|
|
2102
2121
|
>
|
|
2103
|
-
<i
|
|
2122
|
+
<i
|
|
2123
|
+
class="pi pi-spin pi-spinner text-primary"
|
|
2124
|
+
style="font-size: 3rem"
|
|
2125
|
+
></i>
|
|
2104
2126
|
</div>
|
|
2105
2127
|
}
|
|
2106
2128
|
|
|
2107
2129
|
<!-- Action List -->
|
|
2108
2130
|
@if (!loading() && actions().length > 0) {
|
|
2109
|
-
<div class="surface-card p-4
|
|
2131
|
+
<div class="surface-card p-4 border-round shadow-1">
|
|
2110
2132
|
<div
|
|
2111
|
-
class="flex flex-
|
|
2133
|
+
class="flex flex-column md:flex-row justify-content-between align-items-start md:align-items-center gap-3 mb-4"
|
|
2112
2134
|
>
|
|
2113
2135
|
<div>
|
|
2114
2136
|
<h5 class="m-0 mb-1">Action Permissions</h5>
|
|
2115
|
-
<p class="text-sm text-
|
|
2137
|
+
<p class="text-sm text-color-secondary m-0">
|
|
2116
2138
|
{{ actions().length }} actions available
|
|
2117
2139
|
</p>
|
|
2118
2140
|
</div>
|
|
@@ -2134,7 +2156,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
2134
2156
|
(onClick)="deselectAll()"
|
|
2135
2157
|
/>
|
|
2136
2158
|
<p-button
|
|
2137
|
-
*hasPermission="ROLE_ACTION_PERMISSIONS.ASSIGN"
|
|
2138
2159
|
label="Save Changes"
|
|
2139
2160
|
icon="pi pi-save"
|
|
2140
2161
|
[disabled]="!canSave()"
|
|
@@ -2148,12 +2169,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
2148
2169
|
|
|
2149
2170
|
<!-- Validation Warning -->
|
|
2150
2171
|
@if (invalidActionsCount() > 0) {
|
|
2151
|
-
<div class="
|
|
2152
|
-
<div class="flex items-
|
|
2153
|
-
<i
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2172
|
+
<div class="bg-warn/10 border-l-4 border-warn p-4 mb-4">
|
|
2173
|
+
<div class="flex items-center">
|
|
2174
|
+
<i
|
|
2175
|
+
class="pi pi-exclamation-triangle text-warn mr-2"
|
|
2176
|
+
></i>
|
|
2177
|
+
<div>
|
|
2178
|
+
<strong class="text-warn">Validation Warning:</strong>
|
|
2179
|
+
<p class="text-sm text-warn mt-1">
|
|
2157
2180
|
{{ invalidActionsCount() }} selected
|
|
2158
2181
|
action{{ invalidActionsCount() > 1 ? 's have' : ' has' }}
|
|
2159
2182
|
unmet prerequisites. Fix before saving or use auto-fix on
|
|
@@ -2165,33 +2188,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
2165
2188
|
}
|
|
2166
2189
|
|
|
2167
2190
|
<!-- Action Tree Table -->
|
|
2168
|
-
<
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
<ng-template
|
|
2193
|
-
<tr [class.
|
|
2194
|
-
<td
|
|
2191
|
+
<p-treeTable
|
|
2192
|
+
[value]="treeNodes()"
|
|
2193
|
+
[scrollable]="true"
|
|
2194
|
+
scrollHeight="flex"
|
|
2195
|
+
dataKey="id"
|
|
2196
|
+
styleClass="p-treetable-sm"
|
|
2197
|
+
>
|
|
2198
|
+
<ng-template pTemplate="header">
|
|
2199
|
+
<tr>
|
|
2200
|
+
<th style="width: 3rem">
|
|
2201
|
+
<p-checkbox
|
|
2202
|
+
[ngModel]="allSelected()"
|
|
2203
|
+
[binary]="true"
|
|
2204
|
+
(ngModelChange)="toggleAll()"
|
|
2205
|
+
pTooltip="Select/Deselect All"
|
|
2206
|
+
tooltipPosition="top"
|
|
2207
|
+
/>
|
|
2208
|
+
</th>
|
|
2209
|
+
<th>Name</th>
|
|
2210
|
+
<th>Code</th>
|
|
2211
|
+
<th>Type</th>
|
|
2212
|
+
<th>Requirements</th>
|
|
2213
|
+
</tr>
|
|
2214
|
+
</ng-template>
|
|
2215
|
+
<ng-template pTemplate="body" let-rowNode let-rowData="rowData">
|
|
2216
|
+
<tr [class.bg-danger/10]="hasUnmetPrerequisites(rowData)">
|
|
2217
|
+
<td style="width: 3rem">
|
|
2195
2218
|
<p-checkbox
|
|
2196
2219
|
[ngModel]="selectionMap()[rowData.id]"
|
|
2197
2220
|
[binary]="true"
|
|
@@ -2202,18 +2225,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
2202
2225
|
</td>
|
|
2203
2226
|
<td>
|
|
2204
2227
|
<p-treeTableToggler [rowNode]="rowNode" />
|
|
2205
|
-
<span class="inline-flex items-center gap-2">
|
|
2228
|
+
<span class="inline-flex align-items-center gap-2">
|
|
2206
2229
|
{{ rowData.name }}
|
|
2207
2230
|
@if (hasUnmetPrerequisites(rowData)) {
|
|
2208
2231
|
<i
|
|
2209
|
-
class="pi pi-exclamation-triangle text-
|
|
2232
|
+
class="pi pi-exclamation-triangle text-warn"
|
|
2210
2233
|
pTooltip="This action has unmet prerequisites and will fail validation on save"
|
|
2211
2234
|
tooltipPosition="top"
|
|
2212
2235
|
></i>
|
|
2213
2236
|
}
|
|
2214
2237
|
</span>
|
|
2215
2238
|
</td>
|
|
2216
|
-
<td
|
|
2239
|
+
<td>{{ rowData.code || '-' }}</td>
|
|
2217
2240
|
<td>
|
|
2218
2241
|
<p-tag
|
|
2219
2242
|
[value]="rowData.actionType"
|
|
@@ -2222,41 +2245,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
2222
2245
|
"
|
|
2223
2246
|
/>
|
|
2224
2247
|
</td>
|
|
2225
|
-
<td
|
|
2248
|
+
<td>
|
|
2226
2249
|
@if (rowData.permissionLogic) {
|
|
2227
|
-
<span class="text-sm text-muted
|
|
2250
|
+
<span class="text-sm text-muted">Has prerequisites</span>
|
|
2228
2251
|
} @else {
|
|
2229
|
-
<span class="text-muted
|
|
2252
|
+
<span class="text-muted">-</span>
|
|
2230
2253
|
}
|
|
2231
2254
|
</td>
|
|
2232
2255
|
</tr>
|
|
2233
2256
|
</ng-template>
|
|
2234
|
-
<ng-template
|
|
2257
|
+
<ng-template pTemplate="emptymessage">
|
|
2235
2258
|
<tr>
|
|
2236
|
-
<td colspan="5" class="text-center p-4
|
|
2259
|
+
<td colspan="5" class="text-center p-4">
|
|
2237
2260
|
@if (loading()) {
|
|
2238
2261
|
<i class="pi pi-spin pi-spinner"></i> Loading actions...
|
|
2239
2262
|
} @else { No actions available. }
|
|
2240
2263
|
</td>
|
|
2241
2264
|
</tr>
|
|
2242
2265
|
</ng-template>
|
|
2243
|
-
|
|
2244
|
-
</div>
|
|
2266
|
+
</p-treeTable>
|
|
2245
2267
|
</div>
|
|
2246
2268
|
|
|
2247
2269
|
<!-- Change Summary -->
|
|
2248
2270
|
@if (hasChanges()) {
|
|
2249
|
-
<div class="border border-
|
|
2250
|
-
<div class="flex items-center gap-2 mb-3">
|
|
2251
|
-
<i class="pi pi-info-circle text-primary"></i>
|
|
2252
|
-
<
|
|
2271
|
+
<div class="surface-border border-1 border-round p-3 mt-4">
|
|
2272
|
+
<div class="flex align-items-center gap-2 mb-3">
|
|
2273
|
+
<i class="pi pi-info-circle text-primary content-center"></i>
|
|
2274
|
+
<p class="m-0 mb-1 font-bold">Pending Changes</p>
|
|
2253
2275
|
</div>
|
|
2254
|
-
<div class="
|
|
2276
|
+
<div class="flex flex-col md:flex-row gap-4">
|
|
2255
2277
|
@if (pendingAdd().length > 0) {
|
|
2256
|
-
<div>
|
|
2257
|
-
<div class="flex items-center gap-2 mb-2">
|
|
2258
|
-
<i class="pi pi-plus-circle text-
|
|
2259
|
-
<strong class="text-sm"
|
|
2278
|
+
<div class="w-full md:w-1/2">
|
|
2279
|
+
<div class="flex align-items-center gap-2 mb-2">
|
|
2280
|
+
<i class="pi pi-plus-circle text-success"></i>
|
|
2281
|
+
<strong class="text-sm"
|
|
2282
|
+
>To Add ({{ pendingAdd().length }})</strong
|
|
2283
|
+
>
|
|
2260
2284
|
</div>
|
|
2261
2285
|
<ul class="list-none p-0 m-0 pl-4">
|
|
2262
2286
|
@for (action of pendingAdd(); track action.id) {
|
|
@@ -2266,10 +2290,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
2266
2290
|
</div>
|
|
2267
2291
|
}
|
|
2268
2292
|
@if (pendingRemove().length > 0) {
|
|
2269
|
-
<div>
|
|
2270
|
-
<div class="flex items-center gap-2 mb-2">
|
|
2271
|
-
<i class="pi pi-minus-circle text-
|
|
2272
|
-
<strong class="text-sm"
|
|
2293
|
+
<div class="w-full md:w-1/2">
|
|
2294
|
+
<div class="flex align-items-center gap-2 mb-2">
|
|
2295
|
+
<i class="pi pi-minus-circle text-danger"></i>
|
|
2296
|
+
<strong class="text-sm"
|
|
2297
|
+
>To Remove ({{ pendingRemove().length }})</strong
|
|
2298
|
+
>
|
|
2273
2299
|
</div>
|
|
2274
2300
|
<ul class="list-none p-0 m-0 pl-4">
|
|
2275
2301
|
@for (action of pendingRemove(); track action.id) {
|
|
@@ -2284,16 +2310,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
2284
2310
|
}
|
|
2285
2311
|
|
|
2286
2312
|
@if (!loading() && actions().length === 0) {
|
|
2287
|
-
<div class="surface-card p-5
|
|
2288
|
-
<i
|
|
2289
|
-
|
|
2313
|
+
<div class="surface-card p-5 border-round shadow-1 text-center">
|
|
2314
|
+
<i
|
|
2315
|
+
class="pi pi-info-circle text-color-secondary mb-3"
|
|
2316
|
+
style="font-size: 3rem; display: block;"
|
|
2317
|
+
></i>
|
|
2318
|
+
<p class="text-color-secondary m-0">
|
|
2290
2319
|
No actions available for this role.
|
|
2291
2320
|
</p>
|
|
2292
2321
|
</div>
|
|
2293
2322
|
}
|
|
2294
2323
|
}
|
|
2295
2324
|
</div>
|
|
2296
|
-
`, styles: [":host{display:block}
|
|
2325
|
+
`, styles: [":host{display:block}\n"] }]
|
|
2297
2326
|
}], ctorParameters: () => [] });
|
|
2298
2327
|
|
|
2299
2328
|
/**
|
|
@@ -2331,8 +2360,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
2331
2360
|
* ```
|
|
2332
2361
|
*/
|
|
2333
2362
|
class CompanyActionSelectorComponent {
|
|
2334
|
-
// Permission constants for template
|
|
2335
|
-
COMPANY_ACTION_PERMISSIONS = COMPANY_ACTION_PERMISSIONS;
|
|
2336
2363
|
// Dependencies
|
|
2337
2364
|
companyApiProvider = inject(COMPANY_API_PROVIDER);
|
|
2338
2365
|
actionApi = inject(ActionApiService);
|
|
@@ -2340,7 +2367,6 @@ class CompanyActionSelectorComponent {
|
|
|
2340
2367
|
messageService = inject(MessageService);
|
|
2341
2368
|
confirmationService = inject(ConfirmationService);
|
|
2342
2369
|
permissionLogic = inject(ActionPermissionLogicService);
|
|
2343
|
-
destroyRef = inject(DestroyRef);
|
|
2344
2370
|
// State - Company Selection
|
|
2345
2371
|
selectedCompanyId = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedCompanyId" }] : []));
|
|
2346
2372
|
companies = signal([], ...(ngDevMode ? [{ debugName: "companies" }] : []));
|
|
@@ -2415,10 +2441,6 @@ class CompanyActionSelectorComponent {
|
|
|
2415
2441
|
// AbortController for data loading
|
|
2416
2442
|
loadDataAbortController = null;
|
|
2417
2443
|
constructor() {
|
|
2418
|
-
// Cleanup on destroy
|
|
2419
|
-
this.destroyRef.onDestroy(() => {
|
|
2420
|
-
this.loadDataAbortController?.abort();
|
|
2421
|
-
});
|
|
2422
2444
|
this.loadCompanies();
|
|
2423
2445
|
// Effect: Load data when company selection changes
|
|
2424
2446
|
effect(() => {
|
|
@@ -2429,7 +2451,11 @@ class CompanyActionSelectorComponent {
|
|
|
2429
2451
|
this.loadDataAbortController = new AbortController();
|
|
2430
2452
|
this.loadData(this.loadDataAbortController.signal).catch((err) => {
|
|
2431
2453
|
if (err.name !== 'AbortError') {
|
|
2432
|
-
|
|
2454
|
+
this.messageService.add({
|
|
2455
|
+
severity: 'error',
|
|
2456
|
+
summary: 'Error',
|
|
2457
|
+
detail: 'Failed to load company actions. Please refresh.',
|
|
2458
|
+
});
|
|
2433
2459
|
this.loading.set(false);
|
|
2434
2460
|
}
|
|
2435
2461
|
});
|
|
@@ -2439,6 +2465,9 @@ class CompanyActionSelectorComponent {
|
|
|
2439
2465
|
}
|
|
2440
2466
|
});
|
|
2441
2467
|
}
|
|
2468
|
+
ngOnDestroy() {
|
|
2469
|
+
this.loadDataAbortController?.abort();
|
|
2470
|
+
}
|
|
2442
2471
|
/**
|
|
2443
2472
|
* Load companies from API
|
|
2444
2473
|
*/
|
|
@@ -2451,7 +2480,11 @@ class CompanyActionSelectorComponent {
|
|
|
2451
2480
|
this.companies.set(response?.data ?? []);
|
|
2452
2481
|
}
|
|
2453
2482
|
catch {
|
|
2454
|
-
|
|
2483
|
+
this.messageService.add({
|
|
2484
|
+
severity: 'error',
|
|
2485
|
+
summary: 'Error',
|
|
2486
|
+
detail: 'Failed to load companies',
|
|
2487
|
+
});
|
|
2455
2488
|
}
|
|
2456
2489
|
}
|
|
2457
2490
|
/**
|
|
@@ -2616,8 +2649,13 @@ class CompanyActionSelectorComponent {
|
|
|
2616
2649
|
// Update baseline
|
|
2617
2650
|
this._initialSelection.set({ ...this.selectionMap() });
|
|
2618
2651
|
}
|
|
2619
|
-
catch {
|
|
2620
|
-
|
|
2652
|
+
catch (err) {
|
|
2653
|
+
const error = err;
|
|
2654
|
+
this.messageService.add({
|
|
2655
|
+
severity: 'error',
|
|
2656
|
+
summary: 'Error',
|
|
2657
|
+
detail: error?.error?.message || 'Failed to update company action whitelist',
|
|
2658
|
+
});
|
|
2621
2659
|
}
|
|
2622
2660
|
finally {
|
|
2623
2661
|
this.saving.set(false);
|
|
@@ -2684,12 +2722,11 @@ class CompanyActionSelectorComponent {
|
|
|
2684
2722
|
<div class="company-action-selector">
|
|
2685
2723
|
<!-- Company Selector -->
|
|
2686
2724
|
<div class="mb-4">
|
|
2687
|
-
<div class="grid
|
|
2688
|
-
<div>
|
|
2725
|
+
<div class="grid">
|
|
2726
|
+
<div class="col-12 md:col-6 lg:col-4">
|
|
2689
2727
|
<label class="block font-semibold mb-2">Select Company</label>
|
|
2690
2728
|
<p-select
|
|
2691
|
-
[ngModel]="selectedCompanyId
|
|
2692
|
-
(ngModelChange)="selectedCompanyId.set($event)"
|
|
2729
|
+
[(ngModel)]="selectedCompanyId"
|
|
2693
2730
|
[options]="companies()"
|
|
2694
2731
|
optionLabel="name"
|
|
2695
2732
|
optionValue="id"
|
|
@@ -2706,21 +2743,24 @@ class CompanyActionSelectorComponent {
|
|
|
2706
2743
|
<!-- Loading State -->
|
|
2707
2744
|
@if (loading()) {
|
|
2708
2745
|
<div
|
|
2709
|
-
class="surface-card p-5
|
|
2746
|
+
class="surface-card p-5 border-round shadow-1 flex justify-content-center"
|
|
2710
2747
|
>
|
|
2711
|
-
<i
|
|
2748
|
+
<i
|
|
2749
|
+
class="pi pi-spin pi-spinner text-primary"
|
|
2750
|
+
style="font-size: 3rem"
|
|
2751
|
+
></i>
|
|
2712
2752
|
</div>
|
|
2713
2753
|
}
|
|
2714
2754
|
|
|
2715
2755
|
<!-- Action List -->
|
|
2716
2756
|
@if (!loading() && actions().length > 0) {
|
|
2717
|
-
<div class="surface-card p-4
|
|
2757
|
+
<div class="surface-card p-4 border-round shadow-1">
|
|
2718
2758
|
<div
|
|
2719
|
-
class="flex flex-
|
|
2759
|
+
class="flex flex-column md:flex-row justify-content-between align-items-start md:align-items-center gap-3 mb-4"
|
|
2720
2760
|
>
|
|
2721
2761
|
<div>
|
|
2722
2762
|
<h5 class="m-0 mb-1">Action Whitelist</h5>
|
|
2723
|
-
<p class="text-sm text-
|
|
2763
|
+
<p class="text-sm text-color-secondary m-0">
|
|
2724
2764
|
{{ actions().length }} actions available
|
|
2725
2765
|
</p>
|
|
2726
2766
|
</div>
|
|
@@ -2742,7 +2782,6 @@ class CompanyActionSelectorComponent {
|
|
|
2742
2782
|
(onClick)="deselectAll()"
|
|
2743
2783
|
/>
|
|
2744
2784
|
<p-button
|
|
2745
|
-
*hasPermission="COMPANY_ACTION_PERMISSIONS.ASSIGN"
|
|
2746
2785
|
label="Save Changes"
|
|
2747
2786
|
icon="pi pi-save"
|
|
2748
2787
|
[disabled]="!canSave()"
|
|
@@ -2756,12 +2795,14 @@ class CompanyActionSelectorComponent {
|
|
|
2756
2795
|
|
|
2757
2796
|
<!-- Validation Warning -->
|
|
2758
2797
|
@if (invalidActionsCount() > 0) {
|
|
2759
|
-
<div class="
|
|
2760
|
-
<div class="flex items-
|
|
2761
|
-
<i
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2798
|
+
<div class="bg-warn/10 border-l-4 border-warn p-4 mb-4">
|
|
2799
|
+
<div class="flex items-center">
|
|
2800
|
+
<i
|
|
2801
|
+
class="pi pi-exclamation-triangle text-warn mr-2"
|
|
2802
|
+
></i>
|
|
2803
|
+
<div>
|
|
2804
|
+
<strong class="text-warn">Validation Warning:</strong>
|
|
2805
|
+
<p class="text-sm text-warn mt-1">
|
|
2765
2806
|
{{ invalidActionsCount() }} selected
|
|
2766
2807
|
action{{ invalidActionsCount() > 1 ? 's have' : ' has' }}
|
|
2767
2808
|
unmet prerequisites. Fix before saving or use auto-fix on
|
|
@@ -2773,33 +2814,33 @@ class CompanyActionSelectorComponent {
|
|
|
2773
2814
|
}
|
|
2774
2815
|
|
|
2775
2816
|
<!-- Action Tree Table -->
|
|
2776
|
-
<
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
<ng-template
|
|
2801
|
-
<tr [class.
|
|
2802
|
-
<td
|
|
2817
|
+
<p-treeTable
|
|
2818
|
+
[value]="treeNodes()"
|
|
2819
|
+
[scrollable]="true"
|
|
2820
|
+
scrollHeight="flex"
|
|
2821
|
+
dataKey="id"
|
|
2822
|
+
styleClass="p-treetable-sm"
|
|
2823
|
+
>
|
|
2824
|
+
<ng-template pTemplate="header">
|
|
2825
|
+
<tr>
|
|
2826
|
+
<th style="width: 3rem">
|
|
2827
|
+
<p-checkbox
|
|
2828
|
+
[ngModel]="allSelected()"
|
|
2829
|
+
[binary]="true"
|
|
2830
|
+
(ngModelChange)="toggleAll()"
|
|
2831
|
+
pTooltip="Select/Deselect All"
|
|
2832
|
+
tooltipPosition="top"
|
|
2833
|
+
/>
|
|
2834
|
+
</th>
|
|
2835
|
+
<th>Name</th>
|
|
2836
|
+
<th>Code</th>
|
|
2837
|
+
<th>Type</th>
|
|
2838
|
+
<th>Description</th>
|
|
2839
|
+
</tr>
|
|
2840
|
+
</ng-template>
|
|
2841
|
+
<ng-template pTemplate="body" let-rowNode let-rowData="rowData">
|
|
2842
|
+
<tr [class.bg-danger/10]="hasUnmetPrerequisites(rowData)">
|
|
2843
|
+
<td style="width: 3rem">
|
|
2803
2844
|
<p-checkbox
|
|
2804
2845
|
[ngModel]="selectionMap()[rowData.id]"
|
|
2805
2846
|
[binary]="true"
|
|
@@ -2810,18 +2851,18 @@ class CompanyActionSelectorComponent {
|
|
|
2810
2851
|
</td>
|
|
2811
2852
|
<td>
|
|
2812
2853
|
<p-treeTableToggler [rowNode]="rowNode" />
|
|
2813
|
-
<span class="inline-flex items-center gap-2">
|
|
2854
|
+
<span class="inline-flex align-items-center gap-2">
|
|
2814
2855
|
{{ rowData.name }}
|
|
2815
2856
|
@if (hasUnmetPrerequisites(rowData)) {
|
|
2816
2857
|
<i
|
|
2817
|
-
class="pi pi-exclamation-triangle text-
|
|
2858
|
+
class="pi pi-exclamation-triangle text-warn"
|
|
2818
2859
|
pTooltip="This action has unmet prerequisites and will fail validation on save"
|
|
2819
2860
|
tooltipPosition="top"
|
|
2820
2861
|
></i>
|
|
2821
2862
|
}
|
|
2822
2863
|
</span>
|
|
2823
2864
|
</td>
|
|
2824
|
-
<td
|
|
2865
|
+
<td>{{ rowData.code || '-' }}</td>
|
|
2825
2866
|
<td>
|
|
2826
2867
|
<p-tag
|
|
2827
2868
|
[value]="rowData.actionType"
|
|
@@ -2830,35 +2871,36 @@ class CompanyActionSelectorComponent {
|
|
|
2830
2871
|
"
|
|
2831
2872
|
/>
|
|
2832
2873
|
</td>
|
|
2833
|
-
<td
|
|
2874
|
+
<td>{{ rowData.description || '-' }}</td>
|
|
2834
2875
|
</tr>
|
|
2835
2876
|
</ng-template>
|
|
2836
|
-
<ng-template
|
|
2877
|
+
<ng-template pTemplate="emptymessage">
|
|
2837
2878
|
<tr>
|
|
2838
|
-
<td colspan="5" class="text-center p-4
|
|
2879
|
+
<td colspan="5" class="text-center p-4">
|
|
2839
2880
|
@if (loading()) {
|
|
2840
2881
|
<i class="pi pi-spin pi-spinner"></i> Loading actions...
|
|
2841
2882
|
} @else { No actions available. }
|
|
2842
2883
|
</td>
|
|
2843
2884
|
</tr>
|
|
2844
2885
|
</ng-template>
|
|
2845
|
-
|
|
2846
|
-
</div>
|
|
2886
|
+
</p-treeTable>
|
|
2847
2887
|
</div>
|
|
2848
2888
|
|
|
2849
2889
|
<!-- Change Summary -->
|
|
2850
2890
|
@if (hasChanges()) {
|
|
2851
|
-
<div class="border border-
|
|
2852
|
-
<div class="flex items-center gap-2 mb-3">
|
|
2853
|
-
<i class="pi pi-info-circle text-primary"></i>
|
|
2854
|
-
<
|
|
2891
|
+
<div class="surface-border border-1 border-round p-3 mt-4">
|
|
2892
|
+
<div class="flex align-items-center gap-2 mb-3">
|
|
2893
|
+
<i class="pi pi-info-circle text-primary content-center"></i>
|
|
2894
|
+
<p class="m-0 mb-1 font-bold">Pending Changes</p>
|
|
2855
2895
|
</div>
|
|
2856
|
-
<div class="
|
|
2896
|
+
<div class="flex flex-col md:flex-row gap-4">
|
|
2857
2897
|
@if (pendingAdd().length > 0) {
|
|
2858
|
-
<div>
|
|
2898
|
+
<div class="w-full md:w-1/2">
|
|
2859
2899
|
<div class="flex items-center gap-2 mb-2">
|
|
2860
|
-
<i class="pi pi-plus-circle text-
|
|
2861
|
-
<strong class="text-sm">
|
|
2900
|
+
<i class="pi pi-plus-circle text-success"></i>
|
|
2901
|
+
<strong class="text-sm">
|
|
2902
|
+
To Whitelist ({{ pendingAdd().length }})
|
|
2903
|
+
</strong>
|
|
2862
2904
|
</div>
|
|
2863
2905
|
<ul class="list-none p-0 m-0 pl-4">
|
|
2864
2906
|
@for (action of pendingAdd(); track action.id) {
|
|
@@ -2867,11 +2909,14 @@ class CompanyActionSelectorComponent {
|
|
|
2867
2909
|
</ul>
|
|
2868
2910
|
</div>
|
|
2869
2911
|
}
|
|
2912
|
+
|
|
2870
2913
|
@if (pendingRemove().length > 0) {
|
|
2871
|
-
<div>
|
|
2914
|
+
<div class="w-full md:w-1/2">
|
|
2872
2915
|
<div class="flex items-center gap-2 mb-2">
|
|
2873
|
-
<i class="pi pi-minus-circle text-
|
|
2874
|
-
<strong class="text-sm">
|
|
2916
|
+
<i class="pi pi-minus-circle text-danger"></i>
|
|
2917
|
+
<strong class="text-sm">
|
|
2918
|
+
To Remove ({{ pendingRemove().length }})
|
|
2919
|
+
</strong>
|
|
2875
2920
|
</div>
|
|
2876
2921
|
<ul class="list-none p-0 m-0 pl-4">
|
|
2877
2922
|
@for (action of pendingRemove(); track action.id) {
|
|
@@ -2886,29 +2931,31 @@ class CompanyActionSelectorComponent {
|
|
|
2886
2931
|
}
|
|
2887
2932
|
|
|
2888
2933
|
@if (!loading() && actions().length === 0) {
|
|
2889
|
-
<div class="surface-card p-5
|
|
2890
|
-
<i
|
|
2891
|
-
|
|
2934
|
+
<div class="surface-card p-5 border-round shadow-1 text-center">
|
|
2935
|
+
<i
|
|
2936
|
+
class="pi pi-info-circle text-color-secondary mb-3"
|
|
2937
|
+
style="font-size: 3rem; display: block;"
|
|
2938
|
+
></i>
|
|
2939
|
+
<p class="text-color-secondary m-0">
|
|
2892
2940
|
No actions available for this company.
|
|
2893
2941
|
</p>
|
|
2894
2942
|
</div>
|
|
2895
2943
|
}
|
|
2896
2944
|
}
|
|
2897
2945
|
</div>
|
|
2898
|
-
`, isInline: true, styles: [":host{display:block}
|
|
2946
|
+
`, 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: "directive", type: i2$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { 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: i4.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: i5.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: i6.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i7.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: i8.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: i8.TreeTableToggler, selector: "p-treeTableToggler, p-treetabletoggler, p-treetable-toggler", inputs: ["rowNode"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2899
2947
|
}
|
|
2900
2948
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CompanyActionSelectorComponent, decorators: [{
|
|
2901
2949
|
type: Component,
|
|
2902
|
-
args: [{ selector: 'flusys-company-action-selector', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule
|
|
2950
|
+
args: [{ selector: 'flusys-company-action-selector', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule], template: `
|
|
2903
2951
|
<div class="company-action-selector">
|
|
2904
2952
|
<!-- Company Selector -->
|
|
2905
2953
|
<div class="mb-4">
|
|
2906
|
-
<div class="grid
|
|
2907
|
-
<div>
|
|
2954
|
+
<div class="grid">
|
|
2955
|
+
<div class="col-12 md:col-6 lg:col-4">
|
|
2908
2956
|
<label class="block font-semibold mb-2">Select Company</label>
|
|
2909
2957
|
<p-select
|
|
2910
|
-
[ngModel]="selectedCompanyId
|
|
2911
|
-
(ngModelChange)="selectedCompanyId.set($event)"
|
|
2958
|
+
[(ngModel)]="selectedCompanyId"
|
|
2912
2959
|
[options]="companies()"
|
|
2913
2960
|
optionLabel="name"
|
|
2914
2961
|
optionValue="id"
|
|
@@ -2925,21 +2972,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
2925
2972
|
<!-- Loading State -->
|
|
2926
2973
|
@if (loading()) {
|
|
2927
2974
|
<div
|
|
2928
|
-
class="surface-card p-5
|
|
2975
|
+
class="surface-card p-5 border-round shadow-1 flex justify-content-center"
|
|
2929
2976
|
>
|
|
2930
|
-
<i
|
|
2977
|
+
<i
|
|
2978
|
+
class="pi pi-spin pi-spinner text-primary"
|
|
2979
|
+
style="font-size: 3rem"
|
|
2980
|
+
></i>
|
|
2931
2981
|
</div>
|
|
2932
2982
|
}
|
|
2933
2983
|
|
|
2934
2984
|
<!-- Action List -->
|
|
2935
2985
|
@if (!loading() && actions().length > 0) {
|
|
2936
|
-
<div class="surface-card p-4
|
|
2986
|
+
<div class="surface-card p-4 border-round shadow-1">
|
|
2937
2987
|
<div
|
|
2938
|
-
class="flex flex-
|
|
2988
|
+
class="flex flex-column md:flex-row justify-content-between align-items-start md:align-items-center gap-3 mb-4"
|
|
2939
2989
|
>
|
|
2940
2990
|
<div>
|
|
2941
2991
|
<h5 class="m-0 mb-1">Action Whitelist</h5>
|
|
2942
|
-
<p class="text-sm text-
|
|
2992
|
+
<p class="text-sm text-color-secondary m-0">
|
|
2943
2993
|
{{ actions().length }} actions available
|
|
2944
2994
|
</p>
|
|
2945
2995
|
</div>
|
|
@@ -2961,7 +3011,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
2961
3011
|
(onClick)="deselectAll()"
|
|
2962
3012
|
/>
|
|
2963
3013
|
<p-button
|
|
2964
|
-
*hasPermission="COMPANY_ACTION_PERMISSIONS.ASSIGN"
|
|
2965
3014
|
label="Save Changes"
|
|
2966
3015
|
icon="pi pi-save"
|
|
2967
3016
|
[disabled]="!canSave()"
|
|
@@ -2975,12 +3024,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
2975
3024
|
|
|
2976
3025
|
<!-- Validation Warning -->
|
|
2977
3026
|
@if (invalidActionsCount() > 0) {
|
|
2978
|
-
<div class="
|
|
2979
|
-
<div class="flex items-
|
|
2980
|
-
<i
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
3027
|
+
<div class="bg-warn/10 border-l-4 border-warn p-4 mb-4">
|
|
3028
|
+
<div class="flex items-center">
|
|
3029
|
+
<i
|
|
3030
|
+
class="pi pi-exclamation-triangle text-warn mr-2"
|
|
3031
|
+
></i>
|
|
3032
|
+
<div>
|
|
3033
|
+
<strong class="text-warn">Validation Warning:</strong>
|
|
3034
|
+
<p class="text-sm text-warn mt-1">
|
|
2984
3035
|
{{ invalidActionsCount() }} selected
|
|
2985
3036
|
action{{ invalidActionsCount() > 1 ? 's have' : ' has' }}
|
|
2986
3037
|
unmet prerequisites. Fix before saving or use auto-fix on
|
|
@@ -2992,33 +3043,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
2992
3043
|
}
|
|
2993
3044
|
|
|
2994
3045
|
<!-- Action Tree Table -->
|
|
2995
|
-
<
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
<ng-template
|
|
3020
|
-
<tr [class.
|
|
3021
|
-
<td
|
|
3046
|
+
<p-treeTable
|
|
3047
|
+
[value]="treeNodes()"
|
|
3048
|
+
[scrollable]="true"
|
|
3049
|
+
scrollHeight="flex"
|
|
3050
|
+
dataKey="id"
|
|
3051
|
+
styleClass="p-treetable-sm"
|
|
3052
|
+
>
|
|
3053
|
+
<ng-template pTemplate="header">
|
|
3054
|
+
<tr>
|
|
3055
|
+
<th style="width: 3rem">
|
|
3056
|
+
<p-checkbox
|
|
3057
|
+
[ngModel]="allSelected()"
|
|
3058
|
+
[binary]="true"
|
|
3059
|
+
(ngModelChange)="toggleAll()"
|
|
3060
|
+
pTooltip="Select/Deselect All"
|
|
3061
|
+
tooltipPosition="top"
|
|
3062
|
+
/>
|
|
3063
|
+
</th>
|
|
3064
|
+
<th>Name</th>
|
|
3065
|
+
<th>Code</th>
|
|
3066
|
+
<th>Type</th>
|
|
3067
|
+
<th>Description</th>
|
|
3068
|
+
</tr>
|
|
3069
|
+
</ng-template>
|
|
3070
|
+
<ng-template pTemplate="body" let-rowNode let-rowData="rowData">
|
|
3071
|
+
<tr [class.bg-danger/10]="hasUnmetPrerequisites(rowData)">
|
|
3072
|
+
<td style="width: 3rem">
|
|
3022
3073
|
<p-checkbox
|
|
3023
3074
|
[ngModel]="selectionMap()[rowData.id]"
|
|
3024
3075
|
[binary]="true"
|
|
@@ -3029,18 +3080,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
3029
3080
|
</td>
|
|
3030
3081
|
<td>
|
|
3031
3082
|
<p-treeTableToggler [rowNode]="rowNode" />
|
|
3032
|
-
<span class="inline-flex items-center gap-2">
|
|
3083
|
+
<span class="inline-flex align-items-center gap-2">
|
|
3033
3084
|
{{ rowData.name }}
|
|
3034
3085
|
@if (hasUnmetPrerequisites(rowData)) {
|
|
3035
3086
|
<i
|
|
3036
|
-
class="pi pi-exclamation-triangle text-
|
|
3087
|
+
class="pi pi-exclamation-triangle text-warn"
|
|
3037
3088
|
pTooltip="This action has unmet prerequisites and will fail validation on save"
|
|
3038
3089
|
tooltipPosition="top"
|
|
3039
3090
|
></i>
|
|
3040
3091
|
}
|
|
3041
3092
|
</span>
|
|
3042
3093
|
</td>
|
|
3043
|
-
<td
|
|
3094
|
+
<td>{{ rowData.code || '-' }}</td>
|
|
3044
3095
|
<td>
|
|
3045
3096
|
<p-tag
|
|
3046
3097
|
[value]="rowData.actionType"
|
|
@@ -3049,35 +3100,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
3049
3100
|
"
|
|
3050
3101
|
/>
|
|
3051
3102
|
</td>
|
|
3052
|
-
<td
|
|
3103
|
+
<td>{{ rowData.description || '-' }}</td>
|
|
3053
3104
|
</tr>
|
|
3054
3105
|
</ng-template>
|
|
3055
|
-
<ng-template
|
|
3106
|
+
<ng-template pTemplate="emptymessage">
|
|
3056
3107
|
<tr>
|
|
3057
|
-
<td colspan="5" class="text-center p-4
|
|
3108
|
+
<td colspan="5" class="text-center p-4">
|
|
3058
3109
|
@if (loading()) {
|
|
3059
3110
|
<i class="pi pi-spin pi-spinner"></i> Loading actions...
|
|
3060
3111
|
} @else { No actions available. }
|
|
3061
3112
|
</td>
|
|
3062
3113
|
</tr>
|
|
3063
3114
|
</ng-template>
|
|
3064
|
-
|
|
3065
|
-
</div>
|
|
3115
|
+
</p-treeTable>
|
|
3066
3116
|
</div>
|
|
3067
3117
|
|
|
3068
3118
|
<!-- Change Summary -->
|
|
3069
3119
|
@if (hasChanges()) {
|
|
3070
|
-
<div class="border border-
|
|
3071
|
-
<div class="flex items-center gap-2 mb-3">
|
|
3072
|
-
<i class="pi pi-info-circle text-primary"></i>
|
|
3073
|
-
<
|
|
3120
|
+
<div class="surface-border border-1 border-round p-3 mt-4">
|
|
3121
|
+
<div class="flex align-items-center gap-2 mb-3">
|
|
3122
|
+
<i class="pi pi-info-circle text-primary content-center"></i>
|
|
3123
|
+
<p class="m-0 mb-1 font-bold">Pending Changes</p>
|
|
3074
3124
|
</div>
|
|
3075
|
-
<div class="
|
|
3125
|
+
<div class="flex flex-col md:flex-row gap-4">
|
|
3076
3126
|
@if (pendingAdd().length > 0) {
|
|
3077
|
-
<div>
|
|
3127
|
+
<div class="w-full md:w-1/2">
|
|
3078
3128
|
<div class="flex items-center gap-2 mb-2">
|
|
3079
|
-
<i class="pi pi-plus-circle text-
|
|
3080
|
-
<strong class="text-sm">
|
|
3129
|
+
<i class="pi pi-plus-circle text-success"></i>
|
|
3130
|
+
<strong class="text-sm">
|
|
3131
|
+
To Whitelist ({{ pendingAdd().length }})
|
|
3132
|
+
</strong>
|
|
3081
3133
|
</div>
|
|
3082
3134
|
<ul class="list-none p-0 m-0 pl-4">
|
|
3083
3135
|
@for (action of pendingAdd(); track action.id) {
|
|
@@ -3086,11 +3138,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
3086
3138
|
</ul>
|
|
3087
3139
|
</div>
|
|
3088
3140
|
}
|
|
3141
|
+
|
|
3089
3142
|
@if (pendingRemove().length > 0) {
|
|
3090
|
-
<div>
|
|
3143
|
+
<div class="w-full md:w-1/2">
|
|
3091
3144
|
<div class="flex items-center gap-2 mb-2">
|
|
3092
|
-
<i class="pi pi-minus-circle text-
|
|
3093
|
-
<strong class="text-sm">
|
|
3145
|
+
<i class="pi pi-minus-circle text-danger"></i>
|
|
3146
|
+
<strong class="text-sm">
|
|
3147
|
+
To Remove ({{ pendingRemove().length }})
|
|
3148
|
+
</strong>
|
|
3094
3149
|
</div>
|
|
3095
3150
|
<ul class="list-none p-0 m-0 pl-4">
|
|
3096
3151
|
@for (action of pendingRemove(); track action.id) {
|
|
@@ -3105,16 +3160,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
3105
3160
|
}
|
|
3106
3161
|
|
|
3107
3162
|
@if (!loading() && actions().length === 0) {
|
|
3108
|
-
<div class="surface-card p-5
|
|
3109
|
-
<i
|
|
3110
|
-
|
|
3163
|
+
<div class="surface-card p-5 border-round shadow-1 text-center">
|
|
3164
|
+
<i
|
|
3165
|
+
class="pi pi-info-circle text-color-secondary mb-3"
|
|
3166
|
+
style="font-size: 3rem; display: block;"
|
|
3167
|
+
></i>
|
|
3168
|
+
<p class="text-color-secondary m-0">
|
|
3111
3169
|
No actions available for this company.
|
|
3112
3170
|
</p>
|
|
3113
3171
|
</div>
|
|
3114
3172
|
}
|
|
3115
3173
|
}
|
|
3116
3174
|
</div>
|
|
3117
|
-
`, styles: [":host{display:block}
|
|
3175
|
+
`, styles: [":host{display:block}\n"] }]
|
|
3118
3176
|
}], ctorParameters: () => [] });
|
|
3119
3177
|
|
|
3120
3178
|
/**
|
|
@@ -3148,8 +3206,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
3148
3206
|
* ```
|
|
3149
3207
|
*/
|
|
3150
3208
|
class UserRoleSelectorComponent {
|
|
3151
|
-
// Permission constants for template
|
|
3152
|
-
USER_ROLE_PERMISSIONS = USER_ROLE_PERMISSIONS;
|
|
3153
3209
|
// Dependencies
|
|
3154
3210
|
appConfig = inject(APP_CONFIG);
|
|
3155
3211
|
companyContext = inject(LAYOUT_AUTH_STATE);
|
|
@@ -3157,9 +3213,6 @@ class UserRoleSelectorComponent {
|
|
|
3157
3213
|
roleApi = inject(RoleApiService);
|
|
3158
3214
|
permissionApi = inject(PermissionApiService);
|
|
3159
3215
|
messageService = inject(MessageService);
|
|
3160
|
-
destroyRef = inject(DestroyRef);
|
|
3161
|
-
// AbortController for data loading
|
|
3162
|
-
loadDataAbortController = null;
|
|
3163
3216
|
// State - User/Branch Selection
|
|
3164
3217
|
selectedUserId = signal(null, ...(ngDevMode ? [{ debugName: "selectedUserId" }] : []));
|
|
3165
3218
|
selectedBranchId = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedBranchId" }] : []));
|
|
@@ -3196,11 +3249,7 @@ class UserRoleSelectorComponent {
|
|
|
3196
3249
|
hasChanges = computed(() => {
|
|
3197
3250
|
const current = this.selectionMap();
|
|
3198
3251
|
const initial = this.initialSelection();
|
|
3199
|
-
|
|
3200
|
-
const initialKeys = Object.keys(initial);
|
|
3201
|
-
if (currentKeys.length !== initialKeys.length)
|
|
3202
|
-
return true;
|
|
3203
|
-
return currentKeys.some((key) => current[key] !== initial[key]);
|
|
3252
|
+
return JSON.stringify(current) !== JSON.stringify(initial);
|
|
3204
3253
|
}, ...(ngDevMode ? [{ debugName: "hasChanges" }] : []));
|
|
3205
3254
|
// Computed - Pending Changes
|
|
3206
3255
|
pendingAdd = computed(() => {
|
|
@@ -3220,10 +3269,6 @@ class UserRoleSelectorComponent {
|
|
|
3220
3269
|
return this.hasChanges() && !this.saving();
|
|
3221
3270
|
}, ...(ngDevMode ? [{ debugName: "canSave" }] : []));
|
|
3222
3271
|
constructor() {
|
|
3223
|
-
// Cleanup on destroy
|
|
3224
|
-
this.destroyRef.onDestroy(() => {
|
|
3225
|
-
this.loadDataAbortController?.abort();
|
|
3226
|
-
});
|
|
3227
3272
|
// Effect: Load user branches and data when user changes
|
|
3228
3273
|
effect(() => {
|
|
3229
3274
|
const userId = this.selectedUserId();
|
|
@@ -3271,7 +3316,11 @@ class UserRoleSelectorComponent {
|
|
|
3271
3316
|
this.branches.set(userBranches);
|
|
3272
3317
|
}
|
|
3273
3318
|
catch {
|
|
3274
|
-
|
|
3319
|
+
this.messageService.add({
|
|
3320
|
+
severity: 'error',
|
|
3321
|
+
summary: 'Error',
|
|
3322
|
+
detail: 'Failed to load user permitted branches',
|
|
3323
|
+
});
|
|
3275
3324
|
}
|
|
3276
3325
|
}
|
|
3277
3326
|
/**
|
|
@@ -3309,7 +3358,11 @@ class UserRoleSelectorComponent {
|
|
|
3309
3358
|
this._initialSelection.set({ ...selMap });
|
|
3310
3359
|
}
|
|
3311
3360
|
catch {
|
|
3312
|
-
|
|
3361
|
+
this.messageService.add({
|
|
3362
|
+
severity: 'error',
|
|
3363
|
+
summary: 'Error',
|
|
3364
|
+
detail: 'Failed to load user role assignments',
|
|
3365
|
+
});
|
|
3313
3366
|
}
|
|
3314
3367
|
finally {
|
|
3315
3368
|
this.loading.set(false);
|
|
@@ -3416,8 +3469,13 @@ class UserRoleSelectorComponent {
|
|
|
3416
3469
|
// Update baseline
|
|
3417
3470
|
this._initialSelection.set({ ...this.selectionMap() });
|
|
3418
3471
|
}
|
|
3419
|
-
catch {
|
|
3420
|
-
|
|
3472
|
+
catch (err) {
|
|
3473
|
+
const error = err;
|
|
3474
|
+
this.messageService.add({
|
|
3475
|
+
severity: 'error',
|
|
3476
|
+
summary: 'Error',
|
|
3477
|
+
detail: error?.error?.message || 'Failed to update user role assignments',
|
|
3478
|
+
});
|
|
3421
3479
|
}
|
|
3422
3480
|
finally {
|
|
3423
3481
|
this.saving.set(false);
|
|
@@ -3435,30 +3493,28 @@ class UserRoleSelectorComponent {
|
|
|
3435
3493
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: UserRoleSelectorComponent, isStandalone: true, selector: "flusys-user-role-selector", ngImport: i0, template: `
|
|
3436
3494
|
<div class="user-role-selector">
|
|
3437
3495
|
<!-- User and Branch Selectors -->
|
|
3438
|
-
<div class="surface-card p-4
|
|
3439
|
-
<div class="
|
|
3440
|
-
<div>
|
|
3441
|
-
<label class="block font-semibold mb-2">
|
|
3496
|
+
<div class="surface-card p-4 border-round mb-4 shadow-1">
|
|
3497
|
+
<div class="formgrid grid gap-5">
|
|
3498
|
+
<div class="field col-12 sm:col-6 mb-0">
|
|
3499
|
+
<label class="block font-semibold mb-2 text-900">
|
|
3442
3500
|
<i class="pi pi-user mr-2 text-primary"></i>
|
|
3443
3501
|
Select User
|
|
3444
3502
|
</label>
|
|
3445
3503
|
<lib-user-select
|
|
3446
|
-
[value]="selectedUserId
|
|
3447
|
-
(valueChange)="selectedUserId.set($event)"
|
|
3504
|
+
[(value)]="selectedUserId"
|
|
3448
3505
|
[isEditMode]="true"
|
|
3449
3506
|
placeHolder="Select a user"
|
|
3450
3507
|
/>
|
|
3451
3508
|
</div>
|
|
3452
3509
|
|
|
3453
3510
|
@if (showBranchSelector()) {
|
|
3454
|
-
<div>
|
|
3455
|
-
<label class="block font-semibold mb-2">
|
|
3511
|
+
<div class="field col-12 sm:col-6 mb-0">
|
|
3512
|
+
<label class="block font-semibold mb-2 text-900">
|
|
3456
3513
|
<i class="pi pi-building mr-2 text-primary"></i>
|
|
3457
3514
|
Select Branch
|
|
3458
3515
|
</label>
|
|
3459
3516
|
<p-select
|
|
3460
|
-
[ngModel]="selectedBranchId
|
|
3461
|
-
(ngModelChange)="selectedBranchId.set($event)"
|
|
3517
|
+
[(ngModel)]="selectedBranchId"
|
|
3462
3518
|
[options]="filteredBranches()"
|
|
3463
3519
|
optionLabel="name"
|
|
3464
3520
|
optionValue="id"
|
|
@@ -3469,20 +3525,20 @@ class UserRoleSelectorComponent {
|
|
|
3469
3525
|
>
|
|
3470
3526
|
<ng-template #selectedItem let-branch>
|
|
3471
3527
|
@if (branch) {
|
|
3472
|
-
<div class="flex items-center gap-2">
|
|
3528
|
+
<div class="flex align-items-center gap-2">
|
|
3473
3529
|
<i class="pi pi-building text-primary"></i>
|
|
3474
3530
|
<span class="font-semibold">{{ branch.name }}</span>
|
|
3475
3531
|
</div>
|
|
3476
3532
|
}
|
|
3477
3533
|
</ng-template>
|
|
3478
3534
|
<ng-template #item let-branch>
|
|
3479
|
-
<div class="flex items-center gap-2">
|
|
3480
|
-
<i class="pi pi-building text-
|
|
3535
|
+
<div class="flex align-items-center gap-2">
|
|
3536
|
+
<i class="pi pi-building text-color-secondary"></i>
|
|
3481
3537
|
<span>{{ branch.name }}</span>
|
|
3482
3538
|
</div>
|
|
3483
3539
|
</ng-template>
|
|
3484
3540
|
</p-select>
|
|
3485
|
-
<small class="text-
|
|
3541
|
+
<small class="text-color-secondary block mt-1">
|
|
3486
3542
|
{{ filteredBranches().length }} permitted branch{{
|
|
3487
3543
|
filteredBranches().length !== 1 ? 'es' : ''
|
|
3488
3544
|
}}
|
|
@@ -3497,21 +3553,24 @@ class UserRoleSelectorComponent {
|
|
|
3497
3553
|
<!-- Loading State -->
|
|
3498
3554
|
@if (loading()) {
|
|
3499
3555
|
<div
|
|
3500
|
-
class="surface-card p-5
|
|
3556
|
+
class="surface-card p-5 border-round shadow-1 flex justify-content-center"
|
|
3501
3557
|
>
|
|
3502
|
-
<i
|
|
3558
|
+
<i
|
|
3559
|
+
class="pi pi-spin pi-spinner text-primary"
|
|
3560
|
+
style="font-size: 3rem"
|
|
3561
|
+
></i>
|
|
3503
3562
|
</div>
|
|
3504
3563
|
}
|
|
3505
3564
|
|
|
3506
3565
|
<!-- Role List -->
|
|
3507
3566
|
@if (!loading() && roles().length > 0) {
|
|
3508
|
-
<div class="surface-card p-4
|
|
3567
|
+
<div class="surface-card p-4 border-round shadow-1">
|
|
3509
3568
|
<div
|
|
3510
|
-
class="flex flex-
|
|
3569
|
+
class="flex flex-column md:flex-row justify-content-between align-items-start md:align-items-center gap-3 mb-4"
|
|
3511
3570
|
>
|
|
3512
3571
|
<div>
|
|
3513
3572
|
<h5 class="m-0 mb-1">Role Assignments</h5>
|
|
3514
|
-
<p class="text-sm text-
|
|
3573
|
+
<p class="text-sm text-color-secondary m-0">
|
|
3515
3574
|
{{ roles().length }} roles available
|
|
3516
3575
|
</p>
|
|
3517
3576
|
</div>
|
|
@@ -3533,7 +3592,6 @@ class UserRoleSelectorComponent {
|
|
|
3533
3592
|
(onClick)="deselectAll()"
|
|
3534
3593
|
/>
|
|
3535
3594
|
<p-button
|
|
3536
|
-
*hasPermission="USER_ROLE_PERMISSIONS.ASSIGN"
|
|
3537
3595
|
label="Save Changes"
|
|
3538
3596
|
icon="pi pi-save"
|
|
3539
3597
|
[disabled]="!canSave()"
|
|
@@ -3546,78 +3604,77 @@ class UserRoleSelectorComponent {
|
|
|
3546
3604
|
</div>
|
|
3547
3605
|
|
|
3548
3606
|
<!-- Role Table -->
|
|
3549
|
-
<
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
</ng-template>
|
|
3604
|
-
</p-table>
|
|
3605
|
-
</div>
|
|
3607
|
+
<p-table
|
|
3608
|
+
[value]="roles()"
|
|
3609
|
+
[rows]="10"
|
|
3610
|
+
[paginator]="roles().length > 10"
|
|
3611
|
+
[rowsPerPageOptions]="[10, 20, 50]"
|
|
3612
|
+
[globalFilterFields]="['name', 'code', 'description']"
|
|
3613
|
+
[showCurrentPageReport]="true"
|
|
3614
|
+
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} roles"
|
|
3615
|
+
styleClass="p-datatable-sm"
|
|
3616
|
+
>
|
|
3617
|
+
<ng-template #header>
|
|
3618
|
+
<tr>
|
|
3619
|
+
<th style="width: 3rem">
|
|
3620
|
+
<p-checkbox
|
|
3621
|
+
[ngModel]="allSelected()"
|
|
3622
|
+
[binary]="true"
|
|
3623
|
+
(ngModelChange)="toggleAll()"
|
|
3624
|
+
pTooltip="Select/Deselect All"
|
|
3625
|
+
tooltipPosition="top"
|
|
3626
|
+
/>
|
|
3627
|
+
</th>
|
|
3628
|
+
<th>Name</th>
|
|
3629
|
+
<th>Code</th>
|
|
3630
|
+
<th>Description</th>
|
|
3631
|
+
</tr>
|
|
3632
|
+
</ng-template>
|
|
3633
|
+
<ng-template #body let-role>
|
|
3634
|
+
<tr>
|
|
3635
|
+
<td>
|
|
3636
|
+
<p-checkbox
|
|
3637
|
+
[ngModel]="selectionMap()[role.id]"
|
|
3638
|
+
[binary]="true"
|
|
3639
|
+
(ngModelChange)="onRoleToggle(role, $event)"
|
|
3640
|
+
[pTooltip]="getTooltip(role)"
|
|
3641
|
+
tooltipPosition="top"
|
|
3642
|
+
/>
|
|
3643
|
+
</td>
|
|
3644
|
+
<td>{{ role.name }}</td>
|
|
3645
|
+
<td>{{ role.code || '-' }}</td>
|
|
3646
|
+
<td>{{ role.description || '-' }}</td>
|
|
3647
|
+
</tr>
|
|
3648
|
+
</ng-template>
|
|
3649
|
+
<ng-template #emptymessage>
|
|
3650
|
+
<tr>
|
|
3651
|
+
<td colspan="4" class="text-center p-4">
|
|
3652
|
+
@if (loading()) {
|
|
3653
|
+
<i class="pi pi-spin pi-spinner"></i> Loading roles...
|
|
3654
|
+
} @else {
|
|
3655
|
+
No roles available.
|
|
3656
|
+
}
|
|
3657
|
+
</td>
|
|
3658
|
+
</tr>
|
|
3659
|
+
</ng-template>
|
|
3660
|
+
</p-table>
|
|
3606
3661
|
</div>
|
|
3607
3662
|
|
|
3608
3663
|
<!-- Change Summary -->
|
|
3609
3664
|
@if (hasChanges()) {
|
|
3610
|
-
<div class="border border-
|
|
3611
|
-
<div class="flex items-center gap-2 mb-3">
|
|
3612
|
-
<i class="pi pi-info-circle text-primary"></i>
|
|
3613
|
-
<
|
|
3665
|
+
<div class="surface-border border-1 border-round p-3 mt-4">
|
|
3666
|
+
<div class="flex align-items-center gap-2 mb-3">
|
|
3667
|
+
<i class="pi pi-info-circle text-primary content-center"></i>
|
|
3668
|
+
<p class="m-0 mb-1 font-bold">Pending Changes</p>
|
|
3614
3669
|
</div>
|
|
3615
|
-
<div class="
|
|
3670
|
+
<div class="flex flex-col md:flex-row gap-4">
|
|
3616
3671
|
@if (pendingAdd().length > 0) {
|
|
3617
|
-
<div>
|
|
3618
|
-
<div class="flex items-center gap-2 mb-2">
|
|
3619
|
-
<i class="pi pi-plus-circle text-
|
|
3620
|
-
<strong class="text-sm"
|
|
3672
|
+
<div class="w-full md:w-1/2">
|
|
3673
|
+
<div class="flex align-items-center gap-2 mb-2">
|
|
3674
|
+
<i class="pi pi-plus-circle text-success"></i>
|
|
3675
|
+
<strong class="text-sm"
|
|
3676
|
+
>To Assign ({{ pendingAdd().length }})</strong
|
|
3677
|
+
>
|
|
3621
3678
|
</div>
|
|
3622
3679
|
<ul class="list-none p-0 m-0 pl-4">
|
|
3623
3680
|
@for (role of pendingAdd(); track role.id) {
|
|
@@ -3627,10 +3684,12 @@ class UserRoleSelectorComponent {
|
|
|
3627
3684
|
</div>
|
|
3628
3685
|
}
|
|
3629
3686
|
@if (pendingRemove().length > 0) {
|
|
3630
|
-
<div>
|
|
3631
|
-
<div class="flex items-center gap-2 mb-2">
|
|
3632
|
-
<i class="pi pi-minus-circle text-
|
|
3633
|
-
<strong class="text-sm"
|
|
3687
|
+
<div class="w-full md:w-1/2">
|
|
3688
|
+
<div class="flex align-items-center gap-2 mb-2">
|
|
3689
|
+
<i class="pi pi-minus-circle text-danger"></i>
|
|
3690
|
+
<strong class="text-sm"
|
|
3691
|
+
>To Remove ({{ pendingRemove().length }})</strong
|
|
3692
|
+
>
|
|
3634
3693
|
</div>
|
|
3635
3694
|
<ul class="list-none p-0 m-0 pl-4">
|
|
3636
3695
|
@for (role of pendingRemove(); track role.id) {
|
|
@@ -3645,46 +3704,47 @@ class UserRoleSelectorComponent {
|
|
|
3645
3704
|
}
|
|
3646
3705
|
|
|
3647
3706
|
@if (!loading() && roles().length === 0) {
|
|
3648
|
-
<div class="surface-card p-5
|
|
3649
|
-
<i
|
|
3650
|
-
|
|
3707
|
+
<div class="surface-card p-5 border-round shadow-1 text-center">
|
|
3708
|
+
<i
|
|
3709
|
+
class="pi pi-info-circle text-color-secondary mb-3"
|
|
3710
|
+
style="font-size: 3rem; display: block;"
|
|
3711
|
+
></i>
|
|
3712
|
+
<p class="text-color-secondary m-0">
|
|
3651
3713
|
No roles available for this user.
|
|
3652
3714
|
</p>
|
|
3653
3715
|
</div>
|
|
3654
3716
|
}
|
|
3655
3717
|
}
|
|
3656
3718
|
</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:
|
|
3719
|
+
`, 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: i4.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: i5.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: i7.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: UserSelectComponent, selector: "lib-user-select", inputs: ["loadUsers", "placeHolder", "isEditMode", "filterActive", "additionalFilters", "pageSize", "value"], outputs: ["valueChange", "userSelected", "onError"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3658
3720
|
}
|
|
3659
3721
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserRoleSelectorComponent, decorators: [{
|
|
3660
3722
|
type: Component,
|
|
3661
|
-
args: [{ selector: 'flusys-user-role-selector', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule,
|
|
3723
|
+
args: [{ selector: 'flusys-user-role-selector', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule, UserSelectComponent], template: `
|
|
3662
3724
|
<div class="user-role-selector">
|
|
3663
3725
|
<!-- User and Branch Selectors -->
|
|
3664
|
-
<div class="surface-card p-4
|
|
3665
|
-
<div class="
|
|
3666
|
-
<div>
|
|
3667
|
-
<label class="block font-semibold mb-2">
|
|
3726
|
+
<div class="surface-card p-4 border-round mb-4 shadow-1">
|
|
3727
|
+
<div class="formgrid grid gap-5">
|
|
3728
|
+
<div class="field col-12 sm:col-6 mb-0">
|
|
3729
|
+
<label class="block font-semibold mb-2 text-900">
|
|
3668
3730
|
<i class="pi pi-user mr-2 text-primary"></i>
|
|
3669
3731
|
Select User
|
|
3670
3732
|
</label>
|
|
3671
3733
|
<lib-user-select
|
|
3672
|
-
[value]="selectedUserId
|
|
3673
|
-
(valueChange)="selectedUserId.set($event)"
|
|
3734
|
+
[(value)]="selectedUserId"
|
|
3674
3735
|
[isEditMode]="true"
|
|
3675
3736
|
placeHolder="Select a user"
|
|
3676
3737
|
/>
|
|
3677
3738
|
</div>
|
|
3678
3739
|
|
|
3679
3740
|
@if (showBranchSelector()) {
|
|
3680
|
-
<div>
|
|
3681
|
-
<label class="block font-semibold mb-2">
|
|
3741
|
+
<div class="field col-12 sm:col-6 mb-0">
|
|
3742
|
+
<label class="block font-semibold mb-2 text-900">
|
|
3682
3743
|
<i class="pi pi-building mr-2 text-primary"></i>
|
|
3683
3744
|
Select Branch
|
|
3684
3745
|
</label>
|
|
3685
3746
|
<p-select
|
|
3686
|
-
[ngModel]="selectedBranchId
|
|
3687
|
-
(ngModelChange)="selectedBranchId.set($event)"
|
|
3747
|
+
[(ngModel)]="selectedBranchId"
|
|
3688
3748
|
[options]="filteredBranches()"
|
|
3689
3749
|
optionLabel="name"
|
|
3690
3750
|
optionValue="id"
|
|
@@ -3695,20 +3755,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
3695
3755
|
>
|
|
3696
3756
|
<ng-template #selectedItem let-branch>
|
|
3697
3757
|
@if (branch) {
|
|
3698
|
-
<div class="flex items-center gap-2">
|
|
3758
|
+
<div class="flex align-items-center gap-2">
|
|
3699
3759
|
<i class="pi pi-building text-primary"></i>
|
|
3700
3760
|
<span class="font-semibold">{{ branch.name }}</span>
|
|
3701
3761
|
</div>
|
|
3702
3762
|
}
|
|
3703
3763
|
</ng-template>
|
|
3704
3764
|
<ng-template #item let-branch>
|
|
3705
|
-
<div class="flex items-center gap-2">
|
|
3706
|
-
<i class="pi pi-building text-
|
|
3765
|
+
<div class="flex align-items-center gap-2">
|
|
3766
|
+
<i class="pi pi-building text-color-secondary"></i>
|
|
3707
3767
|
<span>{{ branch.name }}</span>
|
|
3708
3768
|
</div>
|
|
3709
3769
|
</ng-template>
|
|
3710
3770
|
</p-select>
|
|
3711
|
-
<small class="text-
|
|
3771
|
+
<small class="text-color-secondary block mt-1">
|
|
3712
3772
|
{{ filteredBranches().length }} permitted branch{{
|
|
3713
3773
|
filteredBranches().length !== 1 ? 'es' : ''
|
|
3714
3774
|
}}
|
|
@@ -3723,21 +3783,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
3723
3783
|
<!-- Loading State -->
|
|
3724
3784
|
@if (loading()) {
|
|
3725
3785
|
<div
|
|
3726
|
-
class="surface-card p-5
|
|
3786
|
+
class="surface-card p-5 border-round shadow-1 flex justify-content-center"
|
|
3727
3787
|
>
|
|
3728
|
-
<i
|
|
3788
|
+
<i
|
|
3789
|
+
class="pi pi-spin pi-spinner text-primary"
|
|
3790
|
+
style="font-size: 3rem"
|
|
3791
|
+
></i>
|
|
3729
3792
|
</div>
|
|
3730
3793
|
}
|
|
3731
3794
|
|
|
3732
3795
|
<!-- Role List -->
|
|
3733
3796
|
@if (!loading() && roles().length > 0) {
|
|
3734
|
-
<div class="surface-card p-4
|
|
3797
|
+
<div class="surface-card p-4 border-round shadow-1">
|
|
3735
3798
|
<div
|
|
3736
|
-
class="flex flex-
|
|
3799
|
+
class="flex flex-column md:flex-row justify-content-between align-items-start md:align-items-center gap-3 mb-4"
|
|
3737
3800
|
>
|
|
3738
3801
|
<div>
|
|
3739
3802
|
<h5 class="m-0 mb-1">Role Assignments</h5>
|
|
3740
|
-
<p class="text-sm text-
|
|
3803
|
+
<p class="text-sm text-color-secondary m-0">
|
|
3741
3804
|
{{ roles().length }} roles available
|
|
3742
3805
|
</p>
|
|
3743
3806
|
</div>
|
|
@@ -3759,7 +3822,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
3759
3822
|
(onClick)="deselectAll()"
|
|
3760
3823
|
/>
|
|
3761
3824
|
<p-button
|
|
3762
|
-
*hasPermission="USER_ROLE_PERMISSIONS.ASSIGN"
|
|
3763
3825
|
label="Save Changes"
|
|
3764
3826
|
icon="pi pi-save"
|
|
3765
3827
|
[disabled]="!canSave()"
|
|
@@ -3772,78 +3834,77 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
3772
3834
|
</div>
|
|
3773
3835
|
|
|
3774
3836
|
<!-- Role Table -->
|
|
3775
|
-
<
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
</ng-template>
|
|
3830
|
-
</p-table>
|
|
3831
|
-
</div>
|
|
3837
|
+
<p-table
|
|
3838
|
+
[value]="roles()"
|
|
3839
|
+
[rows]="10"
|
|
3840
|
+
[paginator]="roles().length > 10"
|
|
3841
|
+
[rowsPerPageOptions]="[10, 20, 50]"
|
|
3842
|
+
[globalFilterFields]="['name', 'code', 'description']"
|
|
3843
|
+
[showCurrentPageReport]="true"
|
|
3844
|
+
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} roles"
|
|
3845
|
+
styleClass="p-datatable-sm"
|
|
3846
|
+
>
|
|
3847
|
+
<ng-template #header>
|
|
3848
|
+
<tr>
|
|
3849
|
+
<th style="width: 3rem">
|
|
3850
|
+
<p-checkbox
|
|
3851
|
+
[ngModel]="allSelected()"
|
|
3852
|
+
[binary]="true"
|
|
3853
|
+
(ngModelChange)="toggleAll()"
|
|
3854
|
+
pTooltip="Select/Deselect All"
|
|
3855
|
+
tooltipPosition="top"
|
|
3856
|
+
/>
|
|
3857
|
+
</th>
|
|
3858
|
+
<th>Name</th>
|
|
3859
|
+
<th>Code</th>
|
|
3860
|
+
<th>Description</th>
|
|
3861
|
+
</tr>
|
|
3862
|
+
</ng-template>
|
|
3863
|
+
<ng-template #body let-role>
|
|
3864
|
+
<tr>
|
|
3865
|
+
<td>
|
|
3866
|
+
<p-checkbox
|
|
3867
|
+
[ngModel]="selectionMap()[role.id]"
|
|
3868
|
+
[binary]="true"
|
|
3869
|
+
(ngModelChange)="onRoleToggle(role, $event)"
|
|
3870
|
+
[pTooltip]="getTooltip(role)"
|
|
3871
|
+
tooltipPosition="top"
|
|
3872
|
+
/>
|
|
3873
|
+
</td>
|
|
3874
|
+
<td>{{ role.name }}</td>
|
|
3875
|
+
<td>{{ role.code || '-' }}</td>
|
|
3876
|
+
<td>{{ role.description || '-' }}</td>
|
|
3877
|
+
</tr>
|
|
3878
|
+
</ng-template>
|
|
3879
|
+
<ng-template #emptymessage>
|
|
3880
|
+
<tr>
|
|
3881
|
+
<td colspan="4" class="text-center p-4">
|
|
3882
|
+
@if (loading()) {
|
|
3883
|
+
<i class="pi pi-spin pi-spinner"></i> Loading roles...
|
|
3884
|
+
} @else {
|
|
3885
|
+
No roles available.
|
|
3886
|
+
}
|
|
3887
|
+
</td>
|
|
3888
|
+
</tr>
|
|
3889
|
+
</ng-template>
|
|
3890
|
+
</p-table>
|
|
3832
3891
|
</div>
|
|
3833
3892
|
|
|
3834
3893
|
<!-- Change Summary -->
|
|
3835
3894
|
@if (hasChanges()) {
|
|
3836
|
-
<div class="border border-
|
|
3837
|
-
<div class="flex items-center gap-2 mb-3">
|
|
3838
|
-
<i class="pi pi-info-circle text-primary"></i>
|
|
3839
|
-
<
|
|
3895
|
+
<div class="surface-border border-1 border-round p-3 mt-4">
|
|
3896
|
+
<div class="flex align-items-center gap-2 mb-3">
|
|
3897
|
+
<i class="pi pi-info-circle text-primary content-center"></i>
|
|
3898
|
+
<p class="m-0 mb-1 font-bold">Pending Changes</p>
|
|
3840
3899
|
</div>
|
|
3841
|
-
<div class="
|
|
3900
|
+
<div class="flex flex-col md:flex-row gap-4">
|
|
3842
3901
|
@if (pendingAdd().length > 0) {
|
|
3843
|
-
<div>
|
|
3844
|
-
<div class="flex items-center gap-2 mb-2">
|
|
3845
|
-
<i class="pi pi-plus-circle text-
|
|
3846
|
-
<strong class="text-sm"
|
|
3902
|
+
<div class="w-full md:w-1/2">
|
|
3903
|
+
<div class="flex align-items-center gap-2 mb-2">
|
|
3904
|
+
<i class="pi pi-plus-circle text-success"></i>
|
|
3905
|
+
<strong class="text-sm"
|
|
3906
|
+
>To Assign ({{ pendingAdd().length }})</strong
|
|
3907
|
+
>
|
|
3847
3908
|
</div>
|
|
3848
3909
|
<ul class="list-none p-0 m-0 pl-4">
|
|
3849
3910
|
@for (role of pendingAdd(); track role.id) {
|
|
@@ -3853,10 +3914,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
3853
3914
|
</div>
|
|
3854
3915
|
}
|
|
3855
3916
|
@if (pendingRemove().length > 0) {
|
|
3856
|
-
<div>
|
|
3857
|
-
<div class="flex items-center gap-2 mb-2">
|
|
3858
|
-
<i class="pi pi-minus-circle text-
|
|
3859
|
-
<strong class="text-sm"
|
|
3917
|
+
<div class="w-full md:w-1/2">
|
|
3918
|
+
<div class="flex align-items-center gap-2 mb-2">
|
|
3919
|
+
<i class="pi pi-minus-circle text-danger"></i>
|
|
3920
|
+
<strong class="text-sm"
|
|
3921
|
+
>To Remove ({{ pendingRemove().length }})</strong
|
|
3922
|
+
>
|
|
3860
3923
|
</div>
|
|
3861
3924
|
<ul class="list-none p-0 m-0 pl-4">
|
|
3862
3925
|
@for (role of pendingRemove(); track role.id) {
|
|
@@ -3871,9 +3934,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
3871
3934
|
}
|
|
3872
3935
|
|
|
3873
3936
|
@if (!loading() && roles().length === 0) {
|
|
3874
|
-
<div class="surface-card p-5
|
|
3875
|
-
<i
|
|
3876
|
-
|
|
3937
|
+
<div class="surface-card p-5 border-round shadow-1 text-center">
|
|
3938
|
+
<i
|
|
3939
|
+
class="pi pi-info-circle text-color-secondary mb-3"
|
|
3940
|
+
style="font-size: 3rem; display: block;"
|
|
3941
|
+
></i>
|
|
3942
|
+
<p class="text-color-secondary m-0">
|
|
3877
3943
|
No roles available for this user.
|
|
3878
3944
|
</p>
|
|
3879
3945
|
</div>
|
|
@@ -3915,8 +3981,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
3915
3981
|
* ```
|
|
3916
3982
|
*/
|
|
3917
3983
|
class UserActionSelectorComponent {
|
|
3918
|
-
// Permission constants for template
|
|
3919
|
-
USER_ACTION_PERMISSIONS = USER_ACTION_PERMISSIONS;
|
|
3920
3984
|
// Dependencies
|
|
3921
3985
|
appConfig = inject(APP_CONFIG);
|
|
3922
3986
|
companyContext = inject(LAYOUT_AUTH_STATE);
|
|
@@ -3925,9 +3989,6 @@ class UserActionSelectorComponent {
|
|
|
3925
3989
|
permissionApi = inject(PermissionApiService);
|
|
3926
3990
|
permissionLogic = inject(ActionPermissionLogicService);
|
|
3927
3991
|
messageService = inject(MessageService);
|
|
3928
|
-
destroyRef = inject(DestroyRef);
|
|
3929
|
-
// AbortController for data loading
|
|
3930
|
-
loadDataAbortController = null;
|
|
3931
3992
|
// State - User/Branch Selection
|
|
3932
3993
|
selectedUserId = signal(null, ...(ngDevMode ? [{ debugName: "selectedUserId" }] : []));
|
|
3933
3994
|
selectedBranchId = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedBranchId" }] : []));
|
|
@@ -3969,11 +4030,7 @@ class UserActionSelectorComponent {
|
|
|
3969
4030
|
hasChanges = computed(() => {
|
|
3970
4031
|
const current = this.selectionMap();
|
|
3971
4032
|
const initial = this.initialSelection();
|
|
3972
|
-
|
|
3973
|
-
const initialKeys = Object.keys(initial);
|
|
3974
|
-
if (currentKeys.length !== initialKeys.length)
|
|
3975
|
-
return true;
|
|
3976
|
-
return currentKeys.some((key) => current[key] !== initial[key]);
|
|
4033
|
+
return JSON.stringify(current) !== JSON.stringify(initial);
|
|
3977
4034
|
}, ...(ngDevMode ? [{ debugName: "hasChanges" }] : []));
|
|
3978
4035
|
// Computed - Validation
|
|
3979
4036
|
actionsWithUnmetPrerequisites = computed(() => {
|
|
@@ -4009,10 +4066,6 @@ class UserActionSelectorComponent {
|
|
|
4009
4066
|
return (this.hasChanges() && !this.saving() && this.invalidActionsCount() === 0);
|
|
4010
4067
|
}, ...(ngDevMode ? [{ debugName: "canSave" }] : []));
|
|
4011
4068
|
constructor() {
|
|
4012
|
-
// Cleanup on destroy
|
|
4013
|
-
this.destroyRef.onDestroy(() => {
|
|
4014
|
-
this.loadDataAbortController?.abort();
|
|
4015
|
-
});
|
|
4016
4069
|
// Effect: Load user branches and data when user changes
|
|
4017
4070
|
effect(() => {
|
|
4018
4071
|
const userId = this.selectedUserId();
|
|
@@ -4060,7 +4113,11 @@ class UserActionSelectorComponent {
|
|
|
4060
4113
|
this.branches.set(userBranches);
|
|
4061
4114
|
}
|
|
4062
4115
|
catch {
|
|
4063
|
-
|
|
4116
|
+
this.messageService.add({
|
|
4117
|
+
severity: 'error',
|
|
4118
|
+
summary: 'Error',
|
|
4119
|
+
detail: 'Failed to load user permitted branches',
|
|
4120
|
+
});
|
|
4064
4121
|
}
|
|
4065
4122
|
}
|
|
4066
4123
|
/**
|
|
@@ -4101,7 +4158,11 @@ class UserActionSelectorComponent {
|
|
|
4101
4158
|
this._initialSelection.set({ ...selMap });
|
|
4102
4159
|
}
|
|
4103
4160
|
catch {
|
|
4104
|
-
|
|
4161
|
+
this.messageService.add({
|
|
4162
|
+
severity: 'error',
|
|
4163
|
+
summary: 'Error',
|
|
4164
|
+
detail: 'Failed to load user action permissions',
|
|
4165
|
+
});
|
|
4105
4166
|
}
|
|
4106
4167
|
finally {
|
|
4107
4168
|
this.loading.set(false);
|
|
@@ -4136,17 +4197,12 @@ class UserActionSelectorComponent {
|
|
|
4136
4197
|
: false;
|
|
4137
4198
|
}
|
|
4138
4199
|
/**
|
|
4139
|
-
* Handle action toggle
|
|
4200
|
+
* Handle action checkbox toggle
|
|
4140
4201
|
*/
|
|
4141
4202
|
onActionToggle(action, newValue) {
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
}
|
|
4146
|
-
else {
|
|
4147
|
-
// Checking - validate prerequisites
|
|
4148
|
-
this.permissionLogic.handleCheck(action, this.selectionMap(), this.actions(), (newMap) => this._selectionMap.set(newMap), (previousState) => this._selectionMap.set(previousState));
|
|
4149
|
-
}
|
|
4203
|
+
const selMap = { ...this.selectionMap() };
|
|
4204
|
+
selMap[action.id] = newValue;
|
|
4205
|
+
this._selectionMap.set(selMap);
|
|
4150
4206
|
}
|
|
4151
4207
|
/**
|
|
4152
4208
|
* Toggle all actions
|
|
@@ -4197,12 +4253,6 @@ class UserActionSelectorComponent {
|
|
|
4197
4253
|
});
|
|
4198
4254
|
return;
|
|
4199
4255
|
}
|
|
4200
|
-
// Pre-save validation
|
|
4201
|
-
const invalidActions = this.permissionLogic.getActionsWithUnmetPrerequisites(this.selectionMap(), this.actions());
|
|
4202
|
-
if (invalidActions.length > 0) {
|
|
4203
|
-
this.permissionLogic.showValidationErrorDialog(invalidActions, this.selectionMap(), this.actions(), (newMap) => this._selectionMap.set(newMap));
|
|
4204
|
-
return;
|
|
4205
|
-
}
|
|
4206
4256
|
// Build payload
|
|
4207
4257
|
const items = [];
|
|
4208
4258
|
this.pendingAdd().forEach((action) => {
|
|
@@ -4242,8 +4292,13 @@ class UserActionSelectorComponent {
|
|
|
4242
4292
|
// Update baseline
|
|
4243
4293
|
this._initialSelection.set({ ...this.selectionMap() });
|
|
4244
4294
|
}
|
|
4245
|
-
catch {
|
|
4246
|
-
|
|
4295
|
+
catch (err) {
|
|
4296
|
+
const error = err;
|
|
4297
|
+
this.messageService.add({
|
|
4298
|
+
severity: 'error',
|
|
4299
|
+
summary: 'Error',
|
|
4300
|
+
detail: error?.error?.message || 'Failed to update user action permissions',
|
|
4301
|
+
});
|
|
4247
4302
|
}
|
|
4248
4303
|
finally {
|
|
4249
4304
|
this.saving.set(false);
|
|
@@ -4262,30 +4317,28 @@ class UserActionSelectorComponent {
|
|
|
4262
4317
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: UserActionSelectorComponent, isStandalone: true, selector: "flusys-user-action-selector", ngImport: i0, template: `
|
|
4263
4318
|
<div class="user-action-selector">
|
|
4264
4319
|
<!-- User and Branch Selectors -->
|
|
4265
|
-
<div class="surface-card p-4
|
|
4266
|
-
<div class="
|
|
4267
|
-
<div>
|
|
4268
|
-
<label class="block font-semibold mb-2">
|
|
4320
|
+
<div class="surface-card p-4 border-round mb-4 shadow-1">
|
|
4321
|
+
<div class="formgrid grid gap-5">
|
|
4322
|
+
<div class="field col-12 sm:col-6 mb-0">
|
|
4323
|
+
<label class="block font-semibold mb-2 text-900">
|
|
4269
4324
|
<i class="pi pi-user mr-2 text-primary"></i>
|
|
4270
4325
|
Select User
|
|
4271
4326
|
</label>
|
|
4272
4327
|
<lib-user-select
|
|
4273
|
-
[value]="selectedUserId
|
|
4274
|
-
(valueChange)="selectedUserId.set($event)"
|
|
4328
|
+
[(value)]="selectedUserId"
|
|
4275
4329
|
[isEditMode]="true"
|
|
4276
4330
|
placeHolder="Select a user"
|
|
4277
4331
|
/>
|
|
4278
4332
|
</div>
|
|
4279
4333
|
|
|
4280
4334
|
@if (showBranchSelector()) {
|
|
4281
|
-
<div>
|
|
4282
|
-
<label class="block font-semibold mb-2">
|
|
4335
|
+
<div class="field col-12 sm:col-6 mb-0">
|
|
4336
|
+
<label class="block font-semibold mb-2 text-900">
|
|
4283
4337
|
<i class="pi pi-building mr-2 text-primary"></i>
|
|
4284
4338
|
Select Branch
|
|
4285
4339
|
</label>
|
|
4286
4340
|
<p-select
|
|
4287
|
-
[ngModel]="selectedBranchId
|
|
4288
|
-
(ngModelChange)="selectedBranchId.set($event)"
|
|
4341
|
+
[(ngModel)]="selectedBranchId"
|
|
4289
4342
|
[options]="filteredBranches()"
|
|
4290
4343
|
optionLabel="name"
|
|
4291
4344
|
optionValue="id"
|
|
@@ -4296,20 +4349,20 @@ class UserActionSelectorComponent {
|
|
|
4296
4349
|
>
|
|
4297
4350
|
<ng-template #selectedItem let-branch>
|
|
4298
4351
|
@if (branch) {
|
|
4299
|
-
<div class="flex items-center gap-2">
|
|
4352
|
+
<div class="flex align-items-center gap-2">
|
|
4300
4353
|
<i class="pi pi-building text-primary"></i>
|
|
4301
4354
|
<span class="font-semibold">{{ branch.name }}</span>
|
|
4302
4355
|
</div>
|
|
4303
4356
|
}
|
|
4304
4357
|
</ng-template>
|
|
4305
4358
|
<ng-template #item let-branch>
|
|
4306
|
-
<div class="flex items-center gap-2">
|
|
4307
|
-
<i class="pi pi-building text-
|
|
4359
|
+
<div class="flex align-items-center gap-2">
|
|
4360
|
+
<i class="pi pi-building text-color-secondary"></i>
|
|
4308
4361
|
<span>{{ branch.name }}</span>
|
|
4309
4362
|
</div>
|
|
4310
4363
|
</ng-template>
|
|
4311
4364
|
</p-select>
|
|
4312
|
-
<small class="text-
|
|
4365
|
+
<small class="text-color-secondary block mt-1">
|
|
4313
4366
|
{{ filteredBranches().length }} permitted branch{{
|
|
4314
4367
|
filteredBranches().length !== 1 ? 'es' : ''
|
|
4315
4368
|
}}
|
|
@@ -4323,20 +4376,20 @@ class UserActionSelectorComponent {
|
|
|
4323
4376
|
@if (selectedUserId()) {
|
|
4324
4377
|
<!-- Loading State -->
|
|
4325
4378
|
@if (loading()) {
|
|
4326
|
-
<div class="flex justify-center p-5">
|
|
4327
|
-
<i class="pi pi-spin pi-spinner
|
|
4379
|
+
<div class="flex justify-content-center p-5">
|
|
4380
|
+
<i class="pi pi-spin pi-spinner" style="font-size: 2rem"></i>
|
|
4328
4381
|
</div>
|
|
4329
4382
|
}
|
|
4330
4383
|
|
|
4331
4384
|
<!-- Action List -->
|
|
4332
4385
|
@if (!loading() && actions().length > 0) {
|
|
4333
|
-
<div class="surface-card p-4
|
|
4386
|
+
<div class="surface-card p-4 border-round shadow-1">
|
|
4334
4387
|
<div
|
|
4335
|
-
class="flex flex-
|
|
4388
|
+
class="flex flex-column md:flex-row justify-content-between align-items-start md:align-items-center gap-3 mb-4"
|
|
4336
4389
|
>
|
|
4337
4390
|
<div>
|
|
4338
4391
|
<h5 class="m-0 mb-1">Direct Action Permissions</h5>
|
|
4339
|
-
<p class="text-sm text-
|
|
4392
|
+
<p class="text-sm text-color-secondary m-0">
|
|
4340
4393
|
{{ actions().length }} actions available
|
|
4341
4394
|
</p>
|
|
4342
4395
|
</div>
|
|
@@ -4358,7 +4411,6 @@ class UserActionSelectorComponent {
|
|
|
4358
4411
|
(onClick)="deselectAll()"
|
|
4359
4412
|
/>
|
|
4360
4413
|
<p-button
|
|
4361
|
-
*hasPermission="USER_ACTION_PERMISSIONS.ASSIGN"
|
|
4362
4414
|
label="Save Changes"
|
|
4363
4415
|
icon="pi pi-save"
|
|
4364
4416
|
[disabled]="!canSave()"
|
|
@@ -4370,52 +4422,34 @@ class UserActionSelectorComponent {
|
|
|
4370
4422
|
</div>
|
|
4371
4423
|
</div>
|
|
4372
4424
|
|
|
4373
|
-
<!-- Validation Warning -->
|
|
4374
|
-
@if (invalidActionsCount() > 0) {
|
|
4375
|
-
<div class="validation-warning rounded-border p-3 mb-4">
|
|
4376
|
-
<div class="flex items-start gap-2">
|
|
4377
|
-
<i class="pi pi-exclamation-triangle text-xl"></i>
|
|
4378
|
-
<div class="flex-1">
|
|
4379
|
-
<span class="font-semibold">Validation Warning:</span>
|
|
4380
|
-
<p class="text-sm mt-1 mb-0">
|
|
4381
|
-
{{ invalidActionsCount() }} selected
|
|
4382
|
-
action{{ invalidActionsCount() > 1 ? 's have' : ' has' }}
|
|
4383
|
-
unmet prerequisites. Fix before saving or use auto-fix on
|
|
4384
|
-
save.
|
|
4385
|
-
</p>
|
|
4386
|
-
</div>
|
|
4387
|
-
</div>
|
|
4388
|
-
</div>
|
|
4389
|
-
}
|
|
4390
|
-
|
|
4391
4425
|
<!-- Action Tree Table -->
|
|
4392
|
-
<
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
<ng-template
|
|
4417
|
-
<tr
|
|
4418
|
-
<td
|
|
4426
|
+
<p-treeTable
|
|
4427
|
+
[value]="treeNodes()"
|
|
4428
|
+
[scrollable]="true"
|
|
4429
|
+
scrollHeight="flex"
|
|
4430
|
+
dataKey="id"
|
|
4431
|
+
styleClass="p-treetable-sm"
|
|
4432
|
+
>
|
|
4433
|
+
<ng-template pTemplate="header">
|
|
4434
|
+
<tr>
|
|
4435
|
+
<th style="width: 3rem">
|
|
4436
|
+
<p-checkbox
|
|
4437
|
+
[ngModel]="allSelected()"
|
|
4438
|
+
[binary]="true"
|
|
4439
|
+
(ngModelChange)="toggleAll()"
|
|
4440
|
+
pTooltip="Select/Deselect All"
|
|
4441
|
+
tooltipPosition="top"
|
|
4442
|
+
/>
|
|
4443
|
+
</th>
|
|
4444
|
+
<th>Name</th>
|
|
4445
|
+
<th>Code</th>
|
|
4446
|
+
<th>Type</th>
|
|
4447
|
+
<th>Description</th>
|
|
4448
|
+
</tr>
|
|
4449
|
+
</ng-template>
|
|
4450
|
+
<ng-template pTemplate="body" let-rowNode let-rowData="rowData">
|
|
4451
|
+
<tr>
|
|
4452
|
+
<td style="width: 3rem">
|
|
4419
4453
|
<p-checkbox
|
|
4420
4454
|
[ngModel]="selectionMap()[rowData.id]"
|
|
4421
4455
|
[binary]="true"
|
|
@@ -4426,18 +4460,9 @@ class UserActionSelectorComponent {
|
|
|
4426
4460
|
</td>
|
|
4427
4461
|
<td>
|
|
4428
4462
|
<p-treeTableToggler [rowNode]="rowNode" />
|
|
4429
|
-
<span
|
|
4430
|
-
{{ rowData.name }}
|
|
4431
|
-
@if (hasUnmetPrerequisites(rowData)) {
|
|
4432
|
-
<i
|
|
4433
|
-
class="pi pi-exclamation-triangle text-orange-500"
|
|
4434
|
-
pTooltip="This action has unmet prerequisites and will fail validation on save"
|
|
4435
|
-
tooltipPosition="top"
|
|
4436
|
-
></i>
|
|
4437
|
-
}
|
|
4438
|
-
</span>
|
|
4463
|
+
<span>{{ rowData.name }}</span>
|
|
4439
4464
|
</td>
|
|
4440
|
-
<td
|
|
4465
|
+
<td>{{ rowData.code || '-' }}</td>
|
|
4441
4466
|
<td>
|
|
4442
4467
|
<p-tag
|
|
4443
4468
|
[value]="rowData.actionType"
|
|
@@ -4446,12 +4471,12 @@ class UserActionSelectorComponent {
|
|
|
4446
4471
|
"
|
|
4447
4472
|
/>
|
|
4448
4473
|
</td>
|
|
4449
|
-
<td
|
|
4474
|
+
<td>{{ rowData.description || '-' }}</td>
|
|
4450
4475
|
</tr>
|
|
4451
4476
|
</ng-template>
|
|
4452
|
-
<ng-template
|
|
4477
|
+
<ng-template pTemplate="emptymessage">
|
|
4453
4478
|
<tr>
|
|
4454
|
-
<td colspan="5" class="text-center p-4
|
|
4479
|
+
<td colspan="5" class="text-center p-4">
|
|
4455
4480
|
@if (loading()) {
|
|
4456
4481
|
<i class="pi pi-spin pi-spinner"></i> Loading actions...
|
|
4457
4482
|
} @else {
|
|
@@ -4460,23 +4485,24 @@ class UserActionSelectorComponent {
|
|
|
4460
4485
|
</td>
|
|
4461
4486
|
</tr>
|
|
4462
4487
|
</ng-template>
|
|
4463
|
-
|
|
4464
|
-
</div>
|
|
4488
|
+
</p-treeTable>
|
|
4465
4489
|
</div>
|
|
4466
4490
|
|
|
4467
4491
|
<!-- Change Summary -->
|
|
4468
4492
|
@if (hasChanges()) {
|
|
4469
|
-
<div class="border border-
|
|
4470
|
-
<div class="flex items-center gap-2 mb-3">
|
|
4471
|
-
<i class="pi pi-info-circle text-primary"></i>
|
|
4472
|
-
<
|
|
4493
|
+
<div class="surface-border border-1 border-round p-3 mt-4">
|
|
4494
|
+
<div class="flex align-items-center gap-2 mb-3">
|
|
4495
|
+
<i class="pi pi-info-circle text-primary content-center"></i>
|
|
4496
|
+
<p class="m-0 mb-1 font-bold">Pending Changes</p>
|
|
4473
4497
|
</div>
|
|
4474
|
-
<div class="
|
|
4498
|
+
<div class="flex flex-col md:flex-row gap-4">
|
|
4475
4499
|
@if (pendingAdd().length > 0) {
|
|
4476
|
-
<div>
|
|
4477
|
-
<div class="flex items-center gap-2 mb-2">
|
|
4478
|
-
<i class="pi pi-plus-circle text-
|
|
4479
|
-
<strong class="text-sm"
|
|
4500
|
+
<div class="w-full md:w-1/2">
|
|
4501
|
+
<div class="flex align-items-center gap-2 mb-2">
|
|
4502
|
+
<i class="pi pi-plus-circle text-success"></i>
|
|
4503
|
+
<strong class="text-sm"
|
|
4504
|
+
>To Assign ({{ pendingAdd().length }})</strong
|
|
4505
|
+
>
|
|
4480
4506
|
</div>
|
|
4481
4507
|
<ul class="list-none p-0 m-0 pl-4">
|
|
4482
4508
|
@for (action of pendingAdd(); track action.id) {
|
|
@@ -4486,10 +4512,12 @@ class UserActionSelectorComponent {
|
|
|
4486
4512
|
</div>
|
|
4487
4513
|
}
|
|
4488
4514
|
@if (pendingRemove().length > 0) {
|
|
4489
|
-
<div>
|
|
4490
|
-
<div class="flex items-center gap-2 mb-2">
|
|
4491
|
-
<i class="pi pi-minus-circle text-
|
|
4492
|
-
<strong class="text-sm"
|
|
4515
|
+
<div class="w-full md:w-1/2">
|
|
4516
|
+
<div class="flex align-items-center gap-2 mb-2">
|
|
4517
|
+
<i class="pi pi-minus-circle text-danger"></i>
|
|
4518
|
+
<strong class="text-sm"
|
|
4519
|
+
>To Remove ({{ pendingRemove().length }})</strong
|
|
4520
|
+
>
|
|
4493
4521
|
</div>
|
|
4494
4522
|
<ul class="list-none p-0 m-0 pl-4">
|
|
4495
4523
|
@for (action of pendingRemove(); track action.id) {
|
|
@@ -4504,46 +4532,47 @@ class UserActionSelectorComponent {
|
|
|
4504
4532
|
}
|
|
4505
4533
|
|
|
4506
4534
|
@if (!loading() && actions().length === 0) {
|
|
4507
|
-
<div class="surface-card p-5
|
|
4508
|
-
<i
|
|
4509
|
-
|
|
4535
|
+
<div class="surface-card p-5 border-round shadow-1 text-center">
|
|
4536
|
+
<i
|
|
4537
|
+
class="pi pi-info-circle text-color-secondary mb-3"
|
|
4538
|
+
style="font-size: 3rem; display: block;"
|
|
4539
|
+
></i>
|
|
4540
|
+
<p class="text-color-secondary m-0">
|
|
4510
4541
|
No actions available for this user.
|
|
4511
4542
|
</p>
|
|
4512
4543
|
</div>
|
|
4513
4544
|
}
|
|
4514
4545
|
}
|
|
4515
4546
|
</div>
|
|
4516
|
-
`, isInline: true, styles: [":host{display:block}
|
|
4547
|
+
`, 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: "directive", type: i2$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { 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: i4.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: i5.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: i6.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i7.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: i8.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: i8.TreeTableToggler, selector: "p-treeTableToggler, p-treetabletoggler, p-treetable-toggler", inputs: ["rowNode"] }, { kind: "component", type: UserSelectComponent, selector: "lib-user-select", inputs: ["loadUsers", "placeHolder", "isEditMode", "filterActive", "additionalFilters", "pageSize", "value"], outputs: ["valueChange", "userSelected", "onError"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4517
4548
|
}
|
|
4518
4549
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserActionSelectorComponent, decorators: [{
|
|
4519
4550
|
type: Component,
|
|
4520
|
-
args: [{ selector: 'flusys-user-action-selector', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule,
|
|
4551
|
+
args: [{ selector: 'flusys-user-action-selector', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, PrimeModule, UserSelectComponent], template: `
|
|
4521
4552
|
<div class="user-action-selector">
|
|
4522
4553
|
<!-- User and Branch Selectors -->
|
|
4523
|
-
<div class="surface-card p-4
|
|
4524
|
-
<div class="
|
|
4525
|
-
<div>
|
|
4526
|
-
<label class="block font-semibold mb-2">
|
|
4554
|
+
<div class="surface-card p-4 border-round mb-4 shadow-1">
|
|
4555
|
+
<div class="formgrid grid gap-5">
|
|
4556
|
+
<div class="field col-12 sm:col-6 mb-0">
|
|
4557
|
+
<label class="block font-semibold mb-2 text-900">
|
|
4527
4558
|
<i class="pi pi-user mr-2 text-primary"></i>
|
|
4528
4559
|
Select User
|
|
4529
4560
|
</label>
|
|
4530
4561
|
<lib-user-select
|
|
4531
|
-
[value]="selectedUserId
|
|
4532
|
-
(valueChange)="selectedUserId.set($event)"
|
|
4562
|
+
[(value)]="selectedUserId"
|
|
4533
4563
|
[isEditMode]="true"
|
|
4534
4564
|
placeHolder="Select a user"
|
|
4535
4565
|
/>
|
|
4536
4566
|
</div>
|
|
4537
4567
|
|
|
4538
4568
|
@if (showBranchSelector()) {
|
|
4539
|
-
<div>
|
|
4540
|
-
<label class="block font-semibold mb-2">
|
|
4569
|
+
<div class="field col-12 sm:col-6 mb-0">
|
|
4570
|
+
<label class="block font-semibold mb-2 text-900">
|
|
4541
4571
|
<i class="pi pi-building mr-2 text-primary"></i>
|
|
4542
4572
|
Select Branch
|
|
4543
4573
|
</label>
|
|
4544
4574
|
<p-select
|
|
4545
|
-
[ngModel]="selectedBranchId
|
|
4546
|
-
(ngModelChange)="selectedBranchId.set($event)"
|
|
4575
|
+
[(ngModel)]="selectedBranchId"
|
|
4547
4576
|
[options]="filteredBranches()"
|
|
4548
4577
|
optionLabel="name"
|
|
4549
4578
|
optionValue="id"
|
|
@@ -4554,20 +4583,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4554
4583
|
>
|
|
4555
4584
|
<ng-template #selectedItem let-branch>
|
|
4556
4585
|
@if (branch) {
|
|
4557
|
-
<div class="flex items-center gap-2">
|
|
4586
|
+
<div class="flex align-items-center gap-2">
|
|
4558
4587
|
<i class="pi pi-building text-primary"></i>
|
|
4559
4588
|
<span class="font-semibold">{{ branch.name }}</span>
|
|
4560
4589
|
</div>
|
|
4561
4590
|
}
|
|
4562
4591
|
</ng-template>
|
|
4563
4592
|
<ng-template #item let-branch>
|
|
4564
|
-
<div class="flex items-center gap-2">
|
|
4565
|
-
<i class="pi pi-building text-
|
|
4593
|
+
<div class="flex align-items-center gap-2">
|
|
4594
|
+
<i class="pi pi-building text-color-secondary"></i>
|
|
4566
4595
|
<span>{{ branch.name }}</span>
|
|
4567
4596
|
</div>
|
|
4568
4597
|
</ng-template>
|
|
4569
4598
|
</p-select>
|
|
4570
|
-
<small class="text-
|
|
4599
|
+
<small class="text-color-secondary block mt-1">
|
|
4571
4600
|
{{ filteredBranches().length }} permitted branch{{
|
|
4572
4601
|
filteredBranches().length !== 1 ? 'es' : ''
|
|
4573
4602
|
}}
|
|
@@ -4581,20 +4610,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4581
4610
|
@if (selectedUserId()) {
|
|
4582
4611
|
<!-- Loading State -->
|
|
4583
4612
|
@if (loading()) {
|
|
4584
|
-
<div class="flex justify-center p-5">
|
|
4585
|
-
<i class="pi pi-spin pi-spinner
|
|
4613
|
+
<div class="flex justify-content-center p-5">
|
|
4614
|
+
<i class="pi pi-spin pi-spinner" style="font-size: 2rem"></i>
|
|
4586
4615
|
</div>
|
|
4587
4616
|
}
|
|
4588
4617
|
|
|
4589
4618
|
<!-- Action List -->
|
|
4590
4619
|
@if (!loading() && actions().length > 0) {
|
|
4591
|
-
<div class="surface-card p-4
|
|
4620
|
+
<div class="surface-card p-4 border-round shadow-1">
|
|
4592
4621
|
<div
|
|
4593
|
-
class="flex flex-
|
|
4622
|
+
class="flex flex-column md:flex-row justify-content-between align-items-start md:align-items-center gap-3 mb-4"
|
|
4594
4623
|
>
|
|
4595
4624
|
<div>
|
|
4596
4625
|
<h5 class="m-0 mb-1">Direct Action Permissions</h5>
|
|
4597
|
-
<p class="text-sm text-
|
|
4626
|
+
<p class="text-sm text-color-secondary m-0">
|
|
4598
4627
|
{{ actions().length }} actions available
|
|
4599
4628
|
</p>
|
|
4600
4629
|
</div>
|
|
@@ -4616,7 +4645,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4616
4645
|
(onClick)="deselectAll()"
|
|
4617
4646
|
/>
|
|
4618
4647
|
<p-button
|
|
4619
|
-
*hasPermission="USER_ACTION_PERMISSIONS.ASSIGN"
|
|
4620
4648
|
label="Save Changes"
|
|
4621
4649
|
icon="pi pi-save"
|
|
4622
4650
|
[disabled]="!canSave()"
|
|
@@ -4628,52 +4656,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4628
4656
|
</div>
|
|
4629
4657
|
</div>
|
|
4630
4658
|
|
|
4631
|
-
<!-- Validation Warning -->
|
|
4632
|
-
@if (invalidActionsCount() > 0) {
|
|
4633
|
-
<div class="validation-warning rounded-border p-3 mb-4">
|
|
4634
|
-
<div class="flex items-start gap-2">
|
|
4635
|
-
<i class="pi pi-exclamation-triangle text-xl"></i>
|
|
4636
|
-
<div class="flex-1">
|
|
4637
|
-
<span class="font-semibold">Validation Warning:</span>
|
|
4638
|
-
<p class="text-sm mt-1 mb-0">
|
|
4639
|
-
{{ invalidActionsCount() }} selected
|
|
4640
|
-
action{{ invalidActionsCount() > 1 ? 's have' : ' has' }}
|
|
4641
|
-
unmet prerequisites. Fix before saving or use auto-fix on
|
|
4642
|
-
save.
|
|
4643
|
-
</p>
|
|
4644
|
-
</div>
|
|
4645
|
-
</div>
|
|
4646
|
-
</div>
|
|
4647
|
-
}
|
|
4648
|
-
|
|
4649
4659
|
<!-- Action Tree Table -->
|
|
4650
|
-
<
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
<ng-template
|
|
4675
|
-
<tr
|
|
4676
|
-
<td
|
|
4660
|
+
<p-treeTable
|
|
4661
|
+
[value]="treeNodes()"
|
|
4662
|
+
[scrollable]="true"
|
|
4663
|
+
scrollHeight="flex"
|
|
4664
|
+
dataKey="id"
|
|
4665
|
+
styleClass="p-treetable-sm"
|
|
4666
|
+
>
|
|
4667
|
+
<ng-template pTemplate="header">
|
|
4668
|
+
<tr>
|
|
4669
|
+
<th style="width: 3rem">
|
|
4670
|
+
<p-checkbox
|
|
4671
|
+
[ngModel]="allSelected()"
|
|
4672
|
+
[binary]="true"
|
|
4673
|
+
(ngModelChange)="toggleAll()"
|
|
4674
|
+
pTooltip="Select/Deselect All"
|
|
4675
|
+
tooltipPosition="top"
|
|
4676
|
+
/>
|
|
4677
|
+
</th>
|
|
4678
|
+
<th>Name</th>
|
|
4679
|
+
<th>Code</th>
|
|
4680
|
+
<th>Type</th>
|
|
4681
|
+
<th>Description</th>
|
|
4682
|
+
</tr>
|
|
4683
|
+
</ng-template>
|
|
4684
|
+
<ng-template pTemplate="body" let-rowNode let-rowData="rowData">
|
|
4685
|
+
<tr>
|
|
4686
|
+
<td style="width: 3rem">
|
|
4677
4687
|
<p-checkbox
|
|
4678
4688
|
[ngModel]="selectionMap()[rowData.id]"
|
|
4679
4689
|
[binary]="true"
|
|
@@ -4684,18 +4694,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4684
4694
|
</td>
|
|
4685
4695
|
<td>
|
|
4686
4696
|
<p-treeTableToggler [rowNode]="rowNode" />
|
|
4687
|
-
<span
|
|
4688
|
-
{{ rowData.name }}
|
|
4689
|
-
@if (hasUnmetPrerequisites(rowData)) {
|
|
4690
|
-
<i
|
|
4691
|
-
class="pi pi-exclamation-triangle text-orange-500"
|
|
4692
|
-
pTooltip="This action has unmet prerequisites and will fail validation on save"
|
|
4693
|
-
tooltipPosition="top"
|
|
4694
|
-
></i>
|
|
4695
|
-
}
|
|
4696
|
-
</span>
|
|
4697
|
+
<span>{{ rowData.name }}</span>
|
|
4697
4698
|
</td>
|
|
4698
|
-
<td
|
|
4699
|
+
<td>{{ rowData.code || '-' }}</td>
|
|
4699
4700
|
<td>
|
|
4700
4701
|
<p-tag
|
|
4701
4702
|
[value]="rowData.actionType"
|
|
@@ -4704,12 +4705,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4704
4705
|
"
|
|
4705
4706
|
/>
|
|
4706
4707
|
</td>
|
|
4707
|
-
<td
|
|
4708
|
+
<td>{{ rowData.description || '-' }}</td>
|
|
4708
4709
|
</tr>
|
|
4709
4710
|
</ng-template>
|
|
4710
|
-
<ng-template
|
|
4711
|
+
<ng-template pTemplate="emptymessage">
|
|
4711
4712
|
<tr>
|
|
4712
|
-
<td colspan="5" class="text-center p-4
|
|
4713
|
+
<td colspan="5" class="text-center p-4">
|
|
4713
4714
|
@if (loading()) {
|
|
4714
4715
|
<i class="pi pi-spin pi-spinner"></i> Loading actions...
|
|
4715
4716
|
} @else {
|
|
@@ -4718,23 +4719,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4718
4719
|
</td>
|
|
4719
4720
|
</tr>
|
|
4720
4721
|
</ng-template>
|
|
4721
|
-
|
|
4722
|
-
</div>
|
|
4722
|
+
</p-treeTable>
|
|
4723
4723
|
</div>
|
|
4724
4724
|
|
|
4725
4725
|
<!-- Change Summary -->
|
|
4726
4726
|
@if (hasChanges()) {
|
|
4727
|
-
<div class="border border-
|
|
4728
|
-
<div class="flex items-center gap-2 mb-3">
|
|
4729
|
-
<i class="pi pi-info-circle text-primary"></i>
|
|
4730
|
-
<
|
|
4727
|
+
<div class="surface-border border-1 border-round p-3 mt-4">
|
|
4728
|
+
<div class="flex align-items-center gap-2 mb-3">
|
|
4729
|
+
<i class="pi pi-info-circle text-primary content-center"></i>
|
|
4730
|
+
<p class="m-0 mb-1 font-bold">Pending Changes</p>
|
|
4731
4731
|
</div>
|
|
4732
|
-
<div class="
|
|
4732
|
+
<div class="flex flex-col md:flex-row gap-4">
|
|
4733
4733
|
@if (pendingAdd().length > 0) {
|
|
4734
|
-
<div>
|
|
4735
|
-
<div class="flex items-center gap-2 mb-2">
|
|
4736
|
-
<i class="pi pi-plus-circle text-
|
|
4737
|
-
<strong class="text-sm"
|
|
4734
|
+
<div class="w-full md:w-1/2">
|
|
4735
|
+
<div class="flex align-items-center gap-2 mb-2">
|
|
4736
|
+
<i class="pi pi-plus-circle text-success"></i>
|
|
4737
|
+
<strong class="text-sm"
|
|
4738
|
+
>To Assign ({{ pendingAdd().length }})</strong
|
|
4739
|
+
>
|
|
4738
4740
|
</div>
|
|
4739
4741
|
<ul class="list-none p-0 m-0 pl-4">
|
|
4740
4742
|
@for (action of pendingAdd(); track action.id) {
|
|
@@ -4744,10 +4746,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4744
4746
|
</div>
|
|
4745
4747
|
}
|
|
4746
4748
|
@if (pendingRemove().length > 0) {
|
|
4747
|
-
<div>
|
|
4748
|
-
<div class="flex items-center gap-2 mb-2">
|
|
4749
|
-
<i class="pi pi-minus-circle text-
|
|
4750
|
-
<strong class="text-sm"
|
|
4749
|
+
<div class="w-full md:w-1/2">
|
|
4750
|
+
<div class="flex align-items-center gap-2 mb-2">
|
|
4751
|
+
<i class="pi pi-minus-circle text-danger"></i>
|
|
4752
|
+
<strong class="text-sm"
|
|
4753
|
+
>To Remove ({{ pendingRemove().length }})</strong
|
|
4754
|
+
>
|
|
4751
4755
|
</div>
|
|
4752
4756
|
<ul class="list-none p-0 m-0 pl-4">
|
|
4753
4757
|
@for (action of pendingRemove(); track action.id) {
|
|
@@ -4762,16 +4766,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4762
4766
|
}
|
|
4763
4767
|
|
|
4764
4768
|
@if (!loading() && actions().length === 0) {
|
|
4765
|
-
<div class="surface-card p-5
|
|
4766
|
-
<i
|
|
4767
|
-
|
|
4769
|
+
<div class="surface-card p-5 border-round shadow-1 text-center">
|
|
4770
|
+
<i
|
|
4771
|
+
class="pi pi-info-circle text-color-secondary mb-3"
|
|
4772
|
+
style="font-size: 3rem; display: block;"
|
|
4773
|
+
></i>
|
|
4774
|
+
<p class="text-color-secondary m-0">
|
|
4768
4775
|
No actions available for this user.
|
|
4769
4776
|
</p>
|
|
4770
4777
|
</div>
|
|
4771
4778
|
}
|
|
4772
4779
|
}
|
|
4773
4780
|
</div>
|
|
4774
|
-
`, styles: [":host{display:block}
|
|
4781
|
+
`, styles: [":host{display:block}\n"] }]
|
|
4775
4782
|
}], ctorParameters: () => [] });
|
|
4776
4783
|
|
|
4777
4784
|
// Logic Builder Component
|
|
@@ -4870,7 +4877,7 @@ function provideIamProviders() {
|
|
|
4870
4877
|
const IAM_ROUTES = [
|
|
4871
4878
|
{
|
|
4872
4879
|
path: '',
|
|
4873
|
-
loadComponent: () => import('./flusys-ng-iam-iam-container.component-
|
|
4880
|
+
loadComponent: () => import('./flusys-ng-iam-iam-container.component-BkhqmzLi.mjs').then((m) => m.IamContainerComponent),
|
|
4874
4881
|
children: [
|
|
4875
4882
|
// Actions Management
|
|
4876
4883
|
{
|
|
@@ -4878,15 +4885,15 @@ const IAM_ROUTES = [
|
|
|
4878
4885
|
children: [
|
|
4879
4886
|
{
|
|
4880
4887
|
path: '',
|
|
4881
|
-
loadComponent: () => import('./flusys-ng-iam-action-list-page.component-
|
|
4888
|
+
loadComponent: () => import('./flusys-ng-iam-action-list-page.component-BCzSardO.mjs').then((m) => m.ActionListPageComponent),
|
|
4882
4889
|
},
|
|
4883
4890
|
{
|
|
4884
4891
|
path: 'new',
|
|
4885
|
-
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-
|
|
4892
|
+
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-C1j10Qhw.mjs').then((m) => m.ActionFormPageComponent),
|
|
4886
4893
|
},
|
|
4887
4894
|
{
|
|
4888
4895
|
path: ':id',
|
|
4889
|
-
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-
|
|
4896
|
+
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-C1j10Qhw.mjs').then((m) => m.ActionFormPageComponent),
|
|
4890
4897
|
},
|
|
4891
4898
|
],
|
|
4892
4899
|
},
|
|
@@ -4896,22 +4903,22 @@ const IAM_ROUTES = [
|
|
|
4896
4903
|
children: [
|
|
4897
4904
|
{
|
|
4898
4905
|
path: '',
|
|
4899
|
-
loadComponent: () => import('./flusys-ng-iam-role-list-page.component-
|
|
4906
|
+
loadComponent: () => import('./flusys-ng-iam-role-list-page.component-BObCxHiB.mjs').then((m) => m.RoleListPageComponent),
|
|
4900
4907
|
},
|
|
4901
4908
|
{
|
|
4902
4909
|
path: 'new',
|
|
4903
|
-
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-
|
|
4910
|
+
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-Cqziu_BM.mjs').then((m) => m.RoleFormPageComponent),
|
|
4904
4911
|
},
|
|
4905
4912
|
{
|
|
4906
4913
|
path: ':id',
|
|
4907
|
-
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-
|
|
4914
|
+
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-Cqziu_BM.mjs').then((m) => m.RoleFormPageComponent),
|
|
4908
4915
|
},
|
|
4909
4916
|
],
|
|
4910
4917
|
},
|
|
4911
4918
|
// Permissions Management (User permission assignment)
|
|
4912
4919
|
{
|
|
4913
4920
|
path: 'permissions',
|
|
4914
|
-
loadComponent: () => import('./flusys-ng-iam-permission-page.component-
|
|
4921
|
+
loadComponent: () => import('./flusys-ng-iam-permission-page.component-BSQFPt_N.mjs').then((m) => m.PermissionPageComponent),
|
|
4915
4922
|
},
|
|
4916
4923
|
// Default redirect to actions
|
|
4917
4924
|
{
|
|
@@ -4930,4 +4937,4 @@ const IAM_ROUTES = [
|
|
|
4930
4937
|
*/
|
|
4931
4938
|
|
|
4932
4939
|
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-
|
|
4940
|
+
//# sourceMappingURL=flusys-ng-iam-flusys-ng-iam-DISrddPh.mjs.map
|