@flusys/ng-iam 8.0.0-beta.1 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/{flusys-ng-iam-action-form-page.component-O7XSRAVo.mjs → flusys-ng-iam-action-form-page.component-CF_ixS-r.mjs} +2 -2
- package/fesm2022/{flusys-ng-iam-action-form-page.component-O7XSRAVo.mjs.map → flusys-ng-iam-action-form-page.component-CF_ixS-r.mjs.map} +1 -1
- package/fesm2022/{flusys-ng-iam-action-list-page.component-DfRFkfPh.mjs → flusys-ng-iam-action-list-page.component-BveT0xT6.mjs} +48 -4
- package/fesm2022/flusys-ng-iam-action-list-page.component-BveT0xT6.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-flusys-ng-iam--WniMPhV.mjs → flusys-ng-iam-flusys-ng-iam-CjLr3bfY.mjs} +548 -11
- package/fesm2022/flusys-ng-iam-flusys-ng-iam-CjLr3bfY.mjs.map +1 -0
- package/fesm2022/{flusys-ng-iam-permission-page.component-DhfA39qe.mjs → flusys-ng-iam-permission-page.component-CGtj1EiO.mjs} +2 -2
- package/fesm2022/{flusys-ng-iam-permission-page.component-DhfA39qe.mjs.map → flusys-ng-iam-permission-page.component-CGtj1EiO.mjs.map} +1 -1
- package/fesm2022/{flusys-ng-iam-role-form-page.component-BIgs_ePw.mjs → flusys-ng-iam-role-form-page.component-Cfc8za3D.mjs} +2 -2
- package/fesm2022/{flusys-ng-iam-role-form-page.component-BIgs_ePw.mjs.map → flusys-ng-iam-role-form-page.component-Cfc8za3D.mjs.map} +1 -1
- package/fesm2022/{flusys-ng-iam-role-list-page.component-CvJrtElJ.mjs → flusys-ng-iam-role-list-page.component-vFHxd3Q-.mjs} +48 -4
- package/fesm2022/flusys-ng-iam-role-list-page.component-vFHxd3Q-.mjs.map +1 -0
- package/fesm2022/flusys-ng-iam.mjs +1 -1
- package/package.json +5 -5
- package/types/flusys-ng-iam.d.ts +96 -2
- package/fesm2022/flusys-ng-iam-action-list-page.component-DfRFkfPh.mjs.map +0 -1
- package/fesm2022/flusys-ng-iam-flusys-ng-iam--WniMPhV.mjs.map +0 -1
- package/fesm2022/flusys-ng-iam-role-list-page.component-CvJrtElJ.mjs.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Service, inject, signal, input, output, effect, Component, computed, DestroyRef, viewChild } from '@angular/core';
|
|
3
|
-
import { ApiResourceService, TranslateService, PermissionValidatorService, TranslatePipe, ROLE_ACTION_PERMISSIONS, HasPermissionDirective, COMPANY_ACTION_PERMISSIONS, COMPANY_API_PROVIDER, USER_ROLE_PERMISSIONS, USER_PERMISSION_PROVIDER, UserSelectComponent, USER_ACTION_PERMISSIONS, PROFILE_PERMISSION_PROVIDER, ACTION_PERMISSIONS, ROLE_PERMISSIONS, permissionGuard, anyPermissionGuard, resolveTranslationModule, SHARED_MESSAGES } from '@flusys/ng-shared';
|
|
3
|
+
import { ApiResourceService, TranslateService, PermissionValidatorService, TranslatePipe, ROLE_ACTION_PERMISSIONS, HasPermissionDirective, COMPANY_ACTION_PERMISSIONS, COMPANY_API_PROVIDER, USER_ROLE_PERMISSIONS, USER_PERMISSION_PROVIDER, UserSelectComponent, USER_ACTION_PERMISSIONS, USER_PROVIDER, PROFILE_PERMISSION_PROVIDER, ACTION_PERMISSIONS, ROLE_PERMISSIONS, permissionGuard, anyPermissionGuard, resolveTranslationModule, SHARED_MESSAGES } from '@flusys/ng-shared';
|
|
4
4
|
import { APP_CONFIG, getServiceUrl, BaseApiService, isCompanyFeatureEnabled, getPermissionMode } from '@flusys/ng-core';
|
|
5
|
-
import { ConfirmationService, MessageService, FButtonDirective, SelectComponent, TooltipDirective, UiIconComponent, CheckboxComponent, TagComponent, TreeTableComponent, TreeTableTogglerComponent, TableComponent } from '@flusys/ng-ui';
|
|
5
|
+
import { ConfirmationService, MessageService, FButtonDirective, SelectComponent, TooltipDirective, UiIconComponent, CheckboxComponent, TagComponent, TreeTableComponent, TreeTableTogglerComponent, TableComponent, DialogComponent, ProgressSpinnerComponent } from '@flusys/ng-ui';
|
|
6
6
|
import { HttpClient } from '@angular/common/http';
|
|
7
7
|
import { of, firstValueFrom, map as map$1 } from 'rxjs';
|
|
8
8
|
import { tap, catchError, map } from 'rxjs/operators';
|
|
@@ -83,6 +83,11 @@ const IAM_MESSAGES = {
|
|
|
83
83
|
'iam.action.type.backend': 'Backend',
|
|
84
84
|
'iam.action.type.frontend': 'Frontend',
|
|
85
85
|
'iam.action.type.both': 'Both',
|
|
86
|
+
// components/role-permitted-users-dialog.component.ts, components/action-permitted-users-dialog.component.ts
|
|
87
|
+
'iam.permission.view.users': 'View Users',
|
|
88
|
+
'iam.permission.no.users': 'No users found',
|
|
89
|
+
'iam.permission.remove': 'Remove Access',
|
|
90
|
+
'iam.permission.remove.confirm': 'Remove access for {{name}}?',
|
|
86
91
|
'iam.action.delete.title': 'Delete Action',
|
|
87
92
|
// pages/action/action-form-page.component.ts
|
|
88
93
|
'iam.action.edit': 'Edit Action',
|
|
@@ -831,6 +836,14 @@ class PermissionApiService extends BaseApiService {
|
|
|
831
836
|
getRoleActions(roleId) {
|
|
832
837
|
return this.http.post(this.getUrl('permissions/get-role-actions'), { roleId });
|
|
833
838
|
}
|
|
839
|
+
/** Reverse lookup: users assigned to a role */
|
|
840
|
+
getRoleUsers(roleId) {
|
|
841
|
+
return this.http.post(this.getUrl('permissions/get-role-users'), { roleId });
|
|
842
|
+
}
|
|
843
|
+
/** Reverse lookup: users directly assigned an action */
|
|
844
|
+
getActionUsers(actionId) {
|
|
845
|
+
return this.http.post(this.getUrl('permissions/get-action-users'), { actionId });
|
|
846
|
+
}
|
|
834
847
|
assignCompanyActions(data) {
|
|
835
848
|
return this.http.post(this.getUrl('permissions/company-actions/assign'), data);
|
|
836
849
|
}
|
|
@@ -4696,6 +4709,530 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
4696
4709
|
`, styles: [":host{display:block}.empty-state-icon{width:3rem;height:3rem;border-radius:9999px;display:flex;align-items:center;justify-content:center;background-color:var(--f-primary-50, rgba(59, 130, 246, .08))}:host-context(.app-dark) .empty-state-icon{background-color:#60a5fa1f}.validation-warning{background-color:var(--f-yellow-50, #fefce8);border-left:4px solid var(--f-yellow-500, #eab308);color:var(--f-yellow-700, #a16207)}:host-context(.app-dark) .validation-warning{background-color:#eab3081a;color:var(--f-yellow-400, #facc15)}:host ::ng-deep tr.highlight-warning{background-color:var(--f-red-50, rgba(239, 68, 68, .1))!important}:host-context(.app-dark) ::ng-deep tr.highlight-warning{background-color:#ef444426!important}\n"] }]
|
|
4697
4710
|
}], ctorParameters: () => [], propDecorators: { userSelectRef: [{ type: i0.ViewChild, args: ['userSelect', { isSignal: true }] }] } });
|
|
4698
4711
|
|
|
4712
|
+
/**
|
|
4713
|
+
* Read-only viewer for users assigned to a role, with a remove-only action
|
|
4714
|
+
* per user. Assigning new users stays on the user-centric
|
|
4715
|
+
* `UserRoleSelectorComponent` flow — this dialog intentionally has no "add"
|
|
4716
|
+
* affordance. Since IAM stays independent of the auth package, user display
|
|
4717
|
+
* info is resolved via the `USER_PROVIDER` injection token rather than a
|
|
4718
|
+
* direct join.
|
|
4719
|
+
*/
|
|
4720
|
+
class RolePermittedUsersDialogComponent {
|
|
4721
|
+
USER_ROLE_PERMISSIONS = USER_ROLE_PERMISSIONS;
|
|
4722
|
+
permissionApi = inject(PermissionApiService);
|
|
4723
|
+
userProvider = inject(USER_PROVIDER);
|
|
4724
|
+
confirmationService = inject(ConfirmationService);
|
|
4725
|
+
messageService = inject(MessageService);
|
|
4726
|
+
translateAdapter = inject(TranslateService, {
|
|
4727
|
+
optional: true,
|
|
4728
|
+
});
|
|
4729
|
+
t(key, params) {
|
|
4730
|
+
return this.translateAdapter?.translate(key, params) ?? key;
|
|
4731
|
+
}
|
|
4732
|
+
visible = input.required(/* @ts-ignore */
|
|
4733
|
+
...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
4734
|
+
role = input.required(/* @ts-ignore */
|
|
4735
|
+
...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
|
|
4736
|
+
closed = output();
|
|
4737
|
+
isLoading = signal(false, /* @ts-ignore */
|
|
4738
|
+
...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
|
|
4739
|
+
isRemoving = signal(false, /* @ts-ignore */
|
|
4740
|
+
...(ngDevMode ? [{ debugName: "isRemoving" }] : /* istanbul ignore next */ []));
|
|
4741
|
+
users = signal([], /* @ts-ignore */
|
|
4742
|
+
...(ngDevMode ? [{ debugName: "users" }] : /* istanbul ignore next */ []));
|
|
4743
|
+
constructor() {
|
|
4744
|
+
effect(() => {
|
|
4745
|
+
if (this.visible() && this.role()) {
|
|
4746
|
+
this.loadUsers();
|
|
4747
|
+
}
|
|
4748
|
+
});
|
|
4749
|
+
}
|
|
4750
|
+
async loadUsers() {
|
|
4751
|
+
const role = this.role();
|
|
4752
|
+
if (!role)
|
|
4753
|
+
return;
|
|
4754
|
+
this.isLoading.set(true);
|
|
4755
|
+
try {
|
|
4756
|
+
const response = await firstValueFrom(this.permissionApi.getRoleUsers(role.id));
|
|
4757
|
+
const rows = response.data ?? [];
|
|
4758
|
+
const userIds = rows.map((r) => r.userId);
|
|
4759
|
+
const userInfoMap = await this.resolveUserInfo(userIds);
|
|
4760
|
+
this.users.set(rows.map((r) => {
|
|
4761
|
+
const info = userInfoMap.get(r.userId);
|
|
4762
|
+
return {
|
|
4763
|
+
id: r.id,
|
|
4764
|
+
userId: r.userId,
|
|
4765
|
+
userName: info?.name ?? r.userId,
|
|
4766
|
+
userEmail: info?.email ?? '',
|
|
4767
|
+
};
|
|
4768
|
+
}));
|
|
4769
|
+
}
|
|
4770
|
+
catch {
|
|
4771
|
+
// Error toast handled by global interceptor
|
|
4772
|
+
}
|
|
4773
|
+
finally {
|
|
4774
|
+
this.isLoading.set(false);
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
async resolveUserInfo(userIds) {
|
|
4778
|
+
if (userIds.length === 0 || !this.userProvider.getUsersByIds) {
|
|
4779
|
+
return new Map();
|
|
4780
|
+
}
|
|
4781
|
+
try {
|
|
4782
|
+
const response = await firstValueFrom(this.userProvider.getUsersByIds(userIds));
|
|
4783
|
+
return new Map((response.data ?? []).map((u) => [u.id, u]));
|
|
4784
|
+
}
|
|
4785
|
+
catch {
|
|
4786
|
+
return new Map();
|
|
4787
|
+
}
|
|
4788
|
+
}
|
|
4789
|
+
onRemove(user) {
|
|
4790
|
+
const role = this.role();
|
|
4791
|
+
if (!role)
|
|
4792
|
+
return;
|
|
4793
|
+
this.confirmationService.confirm({
|
|
4794
|
+
message: this.t('iam.permission.remove.confirm', { name: user.userName }),
|
|
4795
|
+
header: this.t('iam.permission.remove'),
|
|
4796
|
+
icon: 'exclamation-triangle',
|
|
4797
|
+
acceptButtonStyleClass: 'p-button-danger',
|
|
4798
|
+
accept: async () => {
|
|
4799
|
+
this.isRemoving.set(true);
|
|
4800
|
+
try {
|
|
4801
|
+
const response = await firstValueFrom(this.permissionApi.assignUserRoles({
|
|
4802
|
+
userId: user.userId,
|
|
4803
|
+
items: [{ id: role.id, action: 'remove' }],
|
|
4804
|
+
}));
|
|
4805
|
+
if (response.success) {
|
|
4806
|
+
this.users.update((list) => list.filter((u) => u.id !== user.id));
|
|
4807
|
+
this.messageService.add({
|
|
4808
|
+
severity: 'success',
|
|
4809
|
+
summary: this.t('shared.success'),
|
|
4810
|
+
detail: response?.messageKey
|
|
4811
|
+
? this.t(response.messageKey, response.messageVariables)
|
|
4812
|
+
: (response?.message ?? ''),
|
|
4813
|
+
});
|
|
4814
|
+
}
|
|
4815
|
+
}
|
|
4816
|
+
catch {
|
|
4817
|
+
// Error toast handled by global interceptor
|
|
4818
|
+
}
|
|
4819
|
+
finally {
|
|
4820
|
+
this.isRemoving.set(false);
|
|
4821
|
+
}
|
|
4822
|
+
},
|
|
4823
|
+
});
|
|
4824
|
+
}
|
|
4825
|
+
onClose() {
|
|
4826
|
+
this.closed.emit();
|
|
4827
|
+
}
|
|
4828
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: RolePermittedUsersDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4829
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: RolePermittedUsersDialogComponent, isStandalone: true, selector: "flusys-role-permitted-users-dialog", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: true, transformFunction: null }, role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { closed: "closed" }, ngImport: i0, template: `
|
|
4830
|
+
<f-dialog
|
|
4831
|
+
[visible]="visible()"
|
|
4832
|
+
[modal]="true"
|
|
4833
|
+
[closable]="true"
|
|
4834
|
+
[breakpoints]="{ '960px': '90vw', '640px': '95vw' }"
|
|
4835
|
+
[style]="{ width: '50vw', maxWidth: '600px' }"
|
|
4836
|
+
[header]="'iam.permission.view.users' | translate"
|
|
4837
|
+
(onHide)="onClose()"
|
|
4838
|
+
>
|
|
4839
|
+
<div class="mb-4">
|
|
4840
|
+
<p class="text-sm text-muted-color">
|
|
4841
|
+
{{ 'iam.role.name' | translate }}:
|
|
4842
|
+
<strong>{{ role()?.name }}</strong>
|
|
4843
|
+
</p>
|
|
4844
|
+
</div>
|
|
4845
|
+
|
|
4846
|
+
@if (isLoading()) {
|
|
4847
|
+
<div class="flex justify-center items-center py-8">
|
|
4848
|
+
<f-progressspinner />
|
|
4849
|
+
</div>
|
|
4850
|
+
} @else {
|
|
4851
|
+
<div class="flex flex-col gap-3">
|
|
4852
|
+
@for (user of users(); track user.id) {
|
|
4853
|
+
<div
|
|
4854
|
+
class="flex items-center justify-between p-3 border border-surface rounded"
|
|
4855
|
+
>
|
|
4856
|
+
<div>
|
|
4857
|
+
<div class="font-semibold">{{ user.userName }}</div>
|
|
4858
|
+
<div class="text-sm text-muted-color">{{ user.userEmail }}</div>
|
|
4859
|
+
</div>
|
|
4860
|
+
<button
|
|
4861
|
+
fButton
|
|
4862
|
+
*hasPermission="USER_ROLE_PERMISSIONS.ASSIGN"
|
|
4863
|
+
[text]="true"
|
|
4864
|
+
severity="danger"
|
|
4865
|
+
size="small"
|
|
4866
|
+
[disabled]="isRemoving()"
|
|
4867
|
+
[fTooltip]="'shared.remove' | translate"
|
|
4868
|
+
(click)="onRemove(user)"
|
|
4869
|
+
>
|
|
4870
|
+
<ui-icon pButtonIcon name="trash"></ui-icon>
|
|
4871
|
+
</button>
|
|
4872
|
+
</div>
|
|
4873
|
+
} @empty {
|
|
4874
|
+
<div class="text-center py-4 text-muted-color">
|
|
4875
|
+
{{ 'iam.permission.no.users' | translate }}
|
|
4876
|
+
</div>
|
|
4877
|
+
}
|
|
4878
|
+
</div>
|
|
4879
|
+
}
|
|
4880
|
+
|
|
4881
|
+
<ng-template #footer>
|
|
4882
|
+
<button
|
|
4883
|
+
fButton
|
|
4884
|
+
severity="secondary"
|
|
4885
|
+
[outlined]="true"
|
|
4886
|
+
(click)="onClose()"
|
|
4887
|
+
>
|
|
4888
|
+
<span pButtonLabel>{{ 'shared.close' | translate }}</span>
|
|
4889
|
+
</button>
|
|
4890
|
+
</ng-template>
|
|
4891
|
+
</f-dialog>
|
|
4892
|
+
`, isInline: true, dependencies: [{ kind: "component", type: DialogComponent, selector: "f-dialog", inputs: ["header", "visible", "modal", "closable", "closeOnEscape", "dismissableMask", "resizable", "draggable", "maximizable", "blockScroll", "returnFocusOnClose", "position", "style", "styleClass", "breakpoints"], outputs: ["visibleChange", "onShow", "onHide", "onMaximize"] }, { kind: "directive", type: FButtonDirective, selector: "[fButton]", inputs: ["severity", "text", "outlined", "link", "variant", "rounded", "raised", "fluid", "disabled", "loading", "loadingIcon", "size", "label", "icon", "iconPos", "ariaLabel", "plain"] }, { kind: "component", type: ProgressSpinnerComponent, selector: "f-progressspinner", inputs: ["size", "strokeWidth", "animationDuration", "styleClass", "ariaLabel"] }, { kind: "directive", type: TooltipDirective, selector: "[fTooltip]", inputs: ["fTooltip", "tooltipPosition", "positionStyle", "tooltipStyleClass", "disabled", "tooltipEvent", "showDelay", "hideDelay", "life", "autoHide", "positionTop", "positionLeft", "escape"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
4893
|
+
}
|
|
4894
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: RolePermittedUsersDialogComponent, decorators: [{
|
|
4895
|
+
type: Component,
|
|
4896
|
+
args: [{
|
|
4897
|
+
selector: 'flusys-role-permitted-users-dialog',
|
|
4898
|
+
imports: [
|
|
4899
|
+
DialogComponent,
|
|
4900
|
+
FButtonDirective,
|
|
4901
|
+
ProgressSpinnerComponent,
|
|
4902
|
+
TooltipDirective,
|
|
4903
|
+
UiIconComponent,
|
|
4904
|
+
HasPermissionDirective,
|
|
4905
|
+
TranslatePipe,
|
|
4906
|
+
],
|
|
4907
|
+
template: `
|
|
4908
|
+
<f-dialog
|
|
4909
|
+
[visible]="visible()"
|
|
4910
|
+
[modal]="true"
|
|
4911
|
+
[closable]="true"
|
|
4912
|
+
[breakpoints]="{ '960px': '90vw', '640px': '95vw' }"
|
|
4913
|
+
[style]="{ width: '50vw', maxWidth: '600px' }"
|
|
4914
|
+
[header]="'iam.permission.view.users' | translate"
|
|
4915
|
+
(onHide)="onClose()"
|
|
4916
|
+
>
|
|
4917
|
+
<div class="mb-4">
|
|
4918
|
+
<p class="text-sm text-muted-color">
|
|
4919
|
+
{{ 'iam.role.name' | translate }}:
|
|
4920
|
+
<strong>{{ role()?.name }}</strong>
|
|
4921
|
+
</p>
|
|
4922
|
+
</div>
|
|
4923
|
+
|
|
4924
|
+
@if (isLoading()) {
|
|
4925
|
+
<div class="flex justify-center items-center py-8">
|
|
4926
|
+
<f-progressspinner />
|
|
4927
|
+
</div>
|
|
4928
|
+
} @else {
|
|
4929
|
+
<div class="flex flex-col gap-3">
|
|
4930
|
+
@for (user of users(); track user.id) {
|
|
4931
|
+
<div
|
|
4932
|
+
class="flex items-center justify-between p-3 border border-surface rounded"
|
|
4933
|
+
>
|
|
4934
|
+
<div>
|
|
4935
|
+
<div class="font-semibold">{{ user.userName }}</div>
|
|
4936
|
+
<div class="text-sm text-muted-color">{{ user.userEmail }}</div>
|
|
4937
|
+
</div>
|
|
4938
|
+
<button
|
|
4939
|
+
fButton
|
|
4940
|
+
*hasPermission="USER_ROLE_PERMISSIONS.ASSIGN"
|
|
4941
|
+
[text]="true"
|
|
4942
|
+
severity="danger"
|
|
4943
|
+
size="small"
|
|
4944
|
+
[disabled]="isRemoving()"
|
|
4945
|
+
[fTooltip]="'shared.remove' | translate"
|
|
4946
|
+
(click)="onRemove(user)"
|
|
4947
|
+
>
|
|
4948
|
+
<ui-icon pButtonIcon name="trash"></ui-icon>
|
|
4949
|
+
</button>
|
|
4950
|
+
</div>
|
|
4951
|
+
} @empty {
|
|
4952
|
+
<div class="text-center py-4 text-muted-color">
|
|
4953
|
+
{{ 'iam.permission.no.users' | translate }}
|
|
4954
|
+
</div>
|
|
4955
|
+
}
|
|
4956
|
+
</div>
|
|
4957
|
+
}
|
|
4958
|
+
|
|
4959
|
+
<ng-template #footer>
|
|
4960
|
+
<button
|
|
4961
|
+
fButton
|
|
4962
|
+
severity="secondary"
|
|
4963
|
+
[outlined]="true"
|
|
4964
|
+
(click)="onClose()"
|
|
4965
|
+
>
|
|
4966
|
+
<span pButtonLabel>{{ 'shared.close' | translate }}</span>
|
|
4967
|
+
</button>
|
|
4968
|
+
</ng-template>
|
|
4969
|
+
</f-dialog>
|
|
4970
|
+
`,
|
|
4971
|
+
}]
|
|
4972
|
+
}], ctorParameters: () => [], propDecorators: { visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: true }] }], role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: true }] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
|
|
4973
|
+
|
|
4974
|
+
/**
|
|
4975
|
+
* Read-only viewer for users directly assigned an action, with a remove-only
|
|
4976
|
+
* action per user. Assigning new users stays on the user-centric
|
|
4977
|
+
* `UserActionSelectorComponent` flow — this dialog intentionally has no
|
|
4978
|
+
* "add" affordance. Since IAM stays independent of the auth package, user
|
|
4979
|
+
* display info is resolved via the `USER_PROVIDER` injection token rather
|
|
4980
|
+
* than a direct join.
|
|
4981
|
+
*/
|
|
4982
|
+
class ActionPermittedUsersDialogComponent {
|
|
4983
|
+
USER_ACTION_PERMISSIONS = USER_ACTION_PERMISSIONS;
|
|
4984
|
+
permissionApi = inject(PermissionApiService);
|
|
4985
|
+
userProvider = inject(USER_PROVIDER);
|
|
4986
|
+
confirmationService = inject(ConfirmationService);
|
|
4987
|
+
messageService = inject(MessageService);
|
|
4988
|
+
translateAdapter = inject(TranslateService, {
|
|
4989
|
+
optional: true,
|
|
4990
|
+
});
|
|
4991
|
+
t(key, params) {
|
|
4992
|
+
return this.translateAdapter?.translate(key, params) ?? key;
|
|
4993
|
+
}
|
|
4994
|
+
visible = input.required(/* @ts-ignore */
|
|
4995
|
+
...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
4996
|
+
action = input.required(/* @ts-ignore */
|
|
4997
|
+
...(ngDevMode ? [{ debugName: "action" }] : /* istanbul ignore next */ []));
|
|
4998
|
+
closed = output();
|
|
4999
|
+
isLoading = signal(false, /* @ts-ignore */
|
|
5000
|
+
...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
|
|
5001
|
+
isRemoving = signal(false, /* @ts-ignore */
|
|
5002
|
+
...(ngDevMode ? [{ debugName: "isRemoving" }] : /* istanbul ignore next */ []));
|
|
5003
|
+
users = signal([], /* @ts-ignore */
|
|
5004
|
+
...(ngDevMode ? [{ debugName: "users" }] : /* istanbul ignore next */ []));
|
|
5005
|
+
constructor() {
|
|
5006
|
+
effect(() => {
|
|
5007
|
+
if (this.visible() && this.action()) {
|
|
5008
|
+
this.loadUsers();
|
|
5009
|
+
}
|
|
5010
|
+
});
|
|
5011
|
+
}
|
|
5012
|
+
async loadUsers() {
|
|
5013
|
+
const action = this.action();
|
|
5014
|
+
if (!action)
|
|
5015
|
+
return;
|
|
5016
|
+
this.isLoading.set(true);
|
|
5017
|
+
try {
|
|
5018
|
+
const response = await firstValueFrom(this.permissionApi.getActionUsers(action.id));
|
|
5019
|
+
const rows = response.data ?? [];
|
|
5020
|
+
const userIds = rows.map((r) => r.userId);
|
|
5021
|
+
const userInfoMap = await this.resolveUserInfo(userIds);
|
|
5022
|
+
this.users.set(rows.map((r) => {
|
|
5023
|
+
const info = userInfoMap.get(r.userId);
|
|
5024
|
+
return {
|
|
5025
|
+
id: r.id,
|
|
5026
|
+
userId: r.userId,
|
|
5027
|
+
userName: info?.name ?? r.userId,
|
|
5028
|
+
userEmail: info?.email ?? '',
|
|
5029
|
+
};
|
|
5030
|
+
}));
|
|
5031
|
+
}
|
|
5032
|
+
catch {
|
|
5033
|
+
// Error toast handled by global interceptor
|
|
5034
|
+
}
|
|
5035
|
+
finally {
|
|
5036
|
+
this.isLoading.set(false);
|
|
5037
|
+
}
|
|
5038
|
+
}
|
|
5039
|
+
async resolveUserInfo(userIds) {
|
|
5040
|
+
if (userIds.length === 0 || !this.userProvider.getUsersByIds) {
|
|
5041
|
+
return new Map();
|
|
5042
|
+
}
|
|
5043
|
+
try {
|
|
5044
|
+
const response = await firstValueFrom(this.userProvider.getUsersByIds(userIds));
|
|
5045
|
+
return new Map((response.data ?? []).map((u) => [u.id, u]));
|
|
5046
|
+
}
|
|
5047
|
+
catch {
|
|
5048
|
+
return new Map();
|
|
5049
|
+
}
|
|
5050
|
+
}
|
|
5051
|
+
onRemove(user) {
|
|
5052
|
+
const action = this.action();
|
|
5053
|
+
if (!action)
|
|
5054
|
+
return;
|
|
5055
|
+
this.confirmationService.confirm({
|
|
5056
|
+
message: this.t('iam.permission.remove.confirm', { name: user.userName }),
|
|
5057
|
+
header: this.t('iam.permission.remove'),
|
|
5058
|
+
icon: 'exclamation-triangle',
|
|
5059
|
+
acceptButtonStyleClass: 'p-button-danger',
|
|
5060
|
+
accept: async () => {
|
|
5061
|
+
this.isRemoving.set(true);
|
|
5062
|
+
try {
|
|
5063
|
+
const response = await firstValueFrom(this.permissionApi.assignUserActions({
|
|
5064
|
+
userId: user.userId,
|
|
5065
|
+
items: [{ id: action.id, action: 'remove' }],
|
|
5066
|
+
}));
|
|
5067
|
+
if (response.success) {
|
|
5068
|
+
this.users.update((list) => list.filter((u) => u.id !== user.id));
|
|
5069
|
+
this.messageService.add({
|
|
5070
|
+
severity: 'success',
|
|
5071
|
+
summary: this.t('shared.success'),
|
|
5072
|
+
detail: response?.messageKey
|
|
5073
|
+
? this.t(response.messageKey, response.messageVariables)
|
|
5074
|
+
: (response?.message ?? ''),
|
|
5075
|
+
});
|
|
5076
|
+
}
|
|
5077
|
+
}
|
|
5078
|
+
catch {
|
|
5079
|
+
// Error toast handled by global interceptor
|
|
5080
|
+
}
|
|
5081
|
+
finally {
|
|
5082
|
+
this.isRemoving.set(false);
|
|
5083
|
+
}
|
|
5084
|
+
},
|
|
5085
|
+
});
|
|
5086
|
+
}
|
|
5087
|
+
onClose() {
|
|
5088
|
+
this.closed.emit();
|
|
5089
|
+
}
|
|
5090
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ActionPermittedUsersDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5091
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: ActionPermittedUsersDialogComponent, isStandalone: true, selector: "flusys-action-permitted-users-dialog", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: true, transformFunction: null }, action: { classPropertyName: "action", publicName: "action", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { closed: "closed" }, ngImport: i0, template: `
|
|
5092
|
+
<f-dialog
|
|
5093
|
+
[visible]="visible()"
|
|
5094
|
+
[modal]="true"
|
|
5095
|
+
[closable]="true"
|
|
5096
|
+
[breakpoints]="{ '960px': '90vw', '640px': '95vw' }"
|
|
5097
|
+
[style]="{ width: '50vw', maxWidth: '600px' }"
|
|
5098
|
+
[header]="'iam.permission.view.users' | translate"
|
|
5099
|
+
(onHide)="onClose()"
|
|
5100
|
+
>
|
|
5101
|
+
<div class="mb-4">
|
|
5102
|
+
<p class="text-sm text-muted-color">
|
|
5103
|
+
{{ 'iam.action.name' | translate }}:
|
|
5104
|
+
<strong>{{ action()?.name }}</strong>
|
|
5105
|
+
</p>
|
|
5106
|
+
</div>
|
|
5107
|
+
|
|
5108
|
+
@if (isLoading()) {
|
|
5109
|
+
<div class="flex justify-center items-center py-8">
|
|
5110
|
+
<f-progressspinner />
|
|
5111
|
+
</div>
|
|
5112
|
+
} @else {
|
|
5113
|
+
<div class="flex flex-col gap-3">
|
|
5114
|
+
@for (user of users(); track user.id) {
|
|
5115
|
+
<div
|
|
5116
|
+
class="flex items-center justify-between p-3 border border-surface rounded"
|
|
5117
|
+
>
|
|
5118
|
+
<div>
|
|
5119
|
+
<div class="font-semibold">{{ user.userName }}</div>
|
|
5120
|
+
<div class="text-sm text-muted-color">{{ user.userEmail }}</div>
|
|
5121
|
+
</div>
|
|
5122
|
+
<button
|
|
5123
|
+
fButton
|
|
5124
|
+
*hasPermission="USER_ACTION_PERMISSIONS.ASSIGN"
|
|
5125
|
+
[text]="true"
|
|
5126
|
+
severity="danger"
|
|
5127
|
+
size="small"
|
|
5128
|
+
[disabled]="isRemoving()"
|
|
5129
|
+
[fTooltip]="'shared.remove' | translate"
|
|
5130
|
+
(click)="onRemove(user)"
|
|
5131
|
+
>
|
|
5132
|
+
<ui-icon pButtonIcon name="trash"></ui-icon>
|
|
5133
|
+
</button>
|
|
5134
|
+
</div>
|
|
5135
|
+
} @empty {
|
|
5136
|
+
<div class="text-center py-4 text-muted-color">
|
|
5137
|
+
{{ 'iam.permission.no.users' | translate }}
|
|
5138
|
+
</div>
|
|
5139
|
+
}
|
|
5140
|
+
</div>
|
|
5141
|
+
}
|
|
5142
|
+
|
|
5143
|
+
<ng-template #footer>
|
|
5144
|
+
<button
|
|
5145
|
+
fButton
|
|
5146
|
+
severity="secondary"
|
|
5147
|
+
[outlined]="true"
|
|
5148
|
+
(click)="onClose()"
|
|
5149
|
+
>
|
|
5150
|
+
<span pButtonLabel>{{ 'shared.close' | translate }}</span>
|
|
5151
|
+
</button>
|
|
5152
|
+
</ng-template>
|
|
5153
|
+
</f-dialog>
|
|
5154
|
+
`, isInline: true, dependencies: [{ kind: "component", type: DialogComponent, selector: "f-dialog", inputs: ["header", "visible", "modal", "closable", "closeOnEscape", "dismissableMask", "resizable", "draggable", "maximizable", "blockScroll", "returnFocusOnClose", "position", "style", "styleClass", "breakpoints"], outputs: ["visibleChange", "onShow", "onHide", "onMaximize"] }, { kind: "directive", type: FButtonDirective, selector: "[fButton]", inputs: ["severity", "text", "outlined", "link", "variant", "rounded", "raised", "fluid", "disabled", "loading", "loadingIcon", "size", "label", "icon", "iconPos", "ariaLabel", "plain"] }, { kind: "component", type: ProgressSpinnerComponent, selector: "f-progressspinner", inputs: ["size", "strokeWidth", "animationDuration", "styleClass", "ariaLabel"] }, { kind: "directive", type: TooltipDirective, selector: "[fTooltip]", inputs: ["fTooltip", "tooltipPosition", "positionStyle", "tooltipStyleClass", "disabled", "tooltipEvent", "showDelay", "hideDelay", "life", "autoHide", "positionTop", "positionLeft", "escape"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
5155
|
+
}
|
|
5156
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ActionPermittedUsersDialogComponent, decorators: [{
|
|
5157
|
+
type: Component,
|
|
5158
|
+
args: [{
|
|
5159
|
+
selector: 'flusys-action-permitted-users-dialog',
|
|
5160
|
+
imports: [
|
|
5161
|
+
DialogComponent,
|
|
5162
|
+
FButtonDirective,
|
|
5163
|
+
ProgressSpinnerComponent,
|
|
5164
|
+
TooltipDirective,
|
|
5165
|
+
UiIconComponent,
|
|
5166
|
+
HasPermissionDirective,
|
|
5167
|
+
TranslatePipe,
|
|
5168
|
+
],
|
|
5169
|
+
template: `
|
|
5170
|
+
<f-dialog
|
|
5171
|
+
[visible]="visible()"
|
|
5172
|
+
[modal]="true"
|
|
5173
|
+
[closable]="true"
|
|
5174
|
+
[breakpoints]="{ '960px': '90vw', '640px': '95vw' }"
|
|
5175
|
+
[style]="{ width: '50vw', maxWidth: '600px' }"
|
|
5176
|
+
[header]="'iam.permission.view.users' | translate"
|
|
5177
|
+
(onHide)="onClose()"
|
|
5178
|
+
>
|
|
5179
|
+
<div class="mb-4">
|
|
5180
|
+
<p class="text-sm text-muted-color">
|
|
5181
|
+
{{ 'iam.action.name' | translate }}:
|
|
5182
|
+
<strong>{{ action()?.name }}</strong>
|
|
5183
|
+
</p>
|
|
5184
|
+
</div>
|
|
5185
|
+
|
|
5186
|
+
@if (isLoading()) {
|
|
5187
|
+
<div class="flex justify-center items-center py-8">
|
|
5188
|
+
<f-progressspinner />
|
|
5189
|
+
</div>
|
|
5190
|
+
} @else {
|
|
5191
|
+
<div class="flex flex-col gap-3">
|
|
5192
|
+
@for (user of users(); track user.id) {
|
|
5193
|
+
<div
|
|
5194
|
+
class="flex items-center justify-between p-3 border border-surface rounded"
|
|
5195
|
+
>
|
|
5196
|
+
<div>
|
|
5197
|
+
<div class="font-semibold">{{ user.userName }}</div>
|
|
5198
|
+
<div class="text-sm text-muted-color">{{ user.userEmail }}</div>
|
|
5199
|
+
</div>
|
|
5200
|
+
<button
|
|
5201
|
+
fButton
|
|
5202
|
+
*hasPermission="USER_ACTION_PERMISSIONS.ASSIGN"
|
|
5203
|
+
[text]="true"
|
|
5204
|
+
severity="danger"
|
|
5205
|
+
size="small"
|
|
5206
|
+
[disabled]="isRemoving()"
|
|
5207
|
+
[fTooltip]="'shared.remove' | translate"
|
|
5208
|
+
(click)="onRemove(user)"
|
|
5209
|
+
>
|
|
5210
|
+
<ui-icon pButtonIcon name="trash"></ui-icon>
|
|
5211
|
+
</button>
|
|
5212
|
+
</div>
|
|
5213
|
+
} @empty {
|
|
5214
|
+
<div class="text-center py-4 text-muted-color">
|
|
5215
|
+
{{ 'iam.permission.no.users' | translate }}
|
|
5216
|
+
</div>
|
|
5217
|
+
}
|
|
5218
|
+
</div>
|
|
5219
|
+
}
|
|
5220
|
+
|
|
5221
|
+
<ng-template #footer>
|
|
5222
|
+
<button
|
|
5223
|
+
fButton
|
|
5224
|
+
severity="secondary"
|
|
5225
|
+
[outlined]="true"
|
|
5226
|
+
(click)="onClose()"
|
|
5227
|
+
>
|
|
5228
|
+
<span pButtonLabel>{{ 'shared.close' | translate }}</span>
|
|
5229
|
+
</button>
|
|
5230
|
+
</ng-template>
|
|
5231
|
+
</f-dialog>
|
|
5232
|
+
`,
|
|
5233
|
+
}]
|
|
5234
|
+
}], ctorParameters: () => [], propDecorators: { visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: true }] }], action: [{ type: i0.Input, args: [{ isSignal: true, alias: "action", required: true }] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
|
|
5235
|
+
|
|
4699
5236
|
// Logic Builder Component
|
|
4700
5237
|
|
|
4701
5238
|
/**
|
|
@@ -4809,15 +5346,15 @@ const IAM_ROUTES = [
|
|
|
4809
5346
|
children: [
|
|
4810
5347
|
{
|
|
4811
5348
|
path: '',
|
|
4812
|
-
loadComponent: () => import('./flusys-ng-iam-action-list-page.component-
|
|
5349
|
+
loadComponent: () => import('./flusys-ng-iam-action-list-page.component-BveT0xT6.mjs').then((m) => m.ActionListPageComponent),
|
|
4813
5350
|
},
|
|
4814
5351
|
{
|
|
4815
5352
|
path: 'new',
|
|
4816
|
-
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-
|
|
5353
|
+
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-CF_ixS-r.mjs').then((m) => m.ActionFormPageComponent),
|
|
4817
5354
|
},
|
|
4818
5355
|
{
|
|
4819
5356
|
path: ':id',
|
|
4820
|
-
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-
|
|
5357
|
+
loadComponent: () => import('./flusys-ng-iam-action-form-page.component-CF_ixS-r.mjs').then((m) => m.ActionFormPageComponent),
|
|
4821
5358
|
},
|
|
4822
5359
|
],
|
|
4823
5360
|
},
|
|
@@ -4828,15 +5365,15 @@ const IAM_ROUTES = [
|
|
|
4828
5365
|
children: [
|
|
4829
5366
|
{
|
|
4830
5367
|
path: '',
|
|
4831
|
-
loadComponent: () => import('./flusys-ng-iam-role-list-page.component-
|
|
5368
|
+
loadComponent: () => import('./flusys-ng-iam-role-list-page.component-vFHxd3Q-.mjs').then((m) => m.RoleListPageComponent),
|
|
4832
5369
|
},
|
|
4833
5370
|
{
|
|
4834
5371
|
path: 'new',
|
|
4835
|
-
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-
|
|
5372
|
+
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-Cfc8za3D.mjs').then((m) => m.RoleFormPageComponent),
|
|
4836
5373
|
},
|
|
4837
5374
|
{
|
|
4838
5375
|
path: ':id',
|
|
4839
|
-
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-
|
|
5376
|
+
loadComponent: () => import('./flusys-ng-iam-role-form-page.component-Cfc8za3D.mjs').then((m) => m.RoleFormPageComponent),
|
|
4840
5377
|
},
|
|
4841
5378
|
],
|
|
4842
5379
|
},
|
|
@@ -4851,7 +5388,7 @@ const IAM_ROUTES = [
|
|
|
4851
5388
|
COMPANY_ACTION_PERMISSIONS.READ,
|
|
4852
5389
|
]),
|
|
4853
5390
|
],
|
|
4854
|
-
loadComponent: () => import('./flusys-ng-iam-permission-page.component-
|
|
5391
|
+
loadComponent: () => import('./flusys-ng-iam-permission-page.component-CGtj1EiO.mjs').then((m) => m.PermissionPageComponent),
|
|
4855
5392
|
},
|
|
4856
5393
|
// Default redirect to the first tab the user has permission to see
|
|
4857
5394
|
{
|
|
@@ -4869,5 +5406,5 @@ const IAM_ROUTES = [
|
|
|
4869
5406
|
* Generated bundle index. Do not edit.
|
|
4870
5407
|
*/
|
|
4871
5408
|
|
|
4872
|
-
export { ActionApiService as A, CompanyActionSelectorComponent as C, IAM_MESSAGES as I, LogicBuilderComponent as L, MAX_DROPDOWN_ITEMS as M, PermissionApiService as P, RoleApiService as R, UserRoleSelectorComponent as U, ActionType as a,
|
|
4873
|
-
//# sourceMappingURL=flusys-ng-iam-flusys-ng-iam
|
|
5409
|
+
export { ActionApiService as A, CompanyActionSelectorComponent as C, IAM_MESSAGES as I, LogicBuilderComponent as L, MAX_DROPDOWN_ITEMS as M, PermissionApiService as P, RoleApiService as R, UserRoleSelectorComponent as U, ActionType as a, ActionPermittedUsersDialogComponent as b, convertActionToTreeNode as c, buildActionParentTree as d, RolePermittedUsersDialogComponent as e, RoleActionSelectorComponent as f, UserActionSelectorComponent as g, ActionPermissionLogicService as h, IAM_ROUTES as i, MyPermissionsApiService as j, PermissionStateService as k, ProfilePermissionProviderAdapter as l, provideIamProviders as p };
|
|
5410
|
+
//# sourceMappingURL=flusys-ng-iam-flusys-ng-iam-CjLr3bfY.mjs.map
|