@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.
Files changed (18) hide show
  1. package/fesm2022/{flusys-ng-iam-action-form-page.component-O7XSRAVo.mjs → flusys-ng-iam-action-form-page.component-CF_ixS-r.mjs} +2 -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
  3. package/fesm2022/{flusys-ng-iam-action-list-page.component-DfRFkfPh.mjs → flusys-ng-iam-action-list-page.component-BveT0xT6.mjs} +48 -4
  4. package/fesm2022/flusys-ng-iam-action-list-page.component-BveT0xT6.mjs.map +1 -0
  5. package/fesm2022/{flusys-ng-iam-flusys-ng-iam--WniMPhV.mjs → flusys-ng-iam-flusys-ng-iam-CjLr3bfY.mjs} +548 -11
  6. package/fesm2022/flusys-ng-iam-flusys-ng-iam-CjLr3bfY.mjs.map +1 -0
  7. package/fesm2022/{flusys-ng-iam-permission-page.component-DhfA39qe.mjs → flusys-ng-iam-permission-page.component-CGtj1EiO.mjs} +2 -2
  8. package/fesm2022/{flusys-ng-iam-permission-page.component-DhfA39qe.mjs.map → flusys-ng-iam-permission-page.component-CGtj1EiO.mjs.map} +1 -1
  9. package/fesm2022/{flusys-ng-iam-role-form-page.component-BIgs_ePw.mjs → flusys-ng-iam-role-form-page.component-Cfc8za3D.mjs} +2 -2
  10. 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
  11. package/fesm2022/{flusys-ng-iam-role-list-page.component-CvJrtElJ.mjs → flusys-ng-iam-role-list-page.component-vFHxd3Q-.mjs} +48 -4
  12. package/fesm2022/flusys-ng-iam-role-list-page.component-vFHxd3Q-.mjs.map +1 -0
  13. package/fesm2022/flusys-ng-iam.mjs +1 -1
  14. package/package.json +5 -5
  15. package/types/flusys-ng-iam.d.ts +96 -2
  16. package/fesm2022/flusys-ng-iam-action-list-page.component-DfRFkfPh.mjs.map +0 -1
  17. package/fesm2022/flusys-ng-iam-flusys-ng-iam--WniMPhV.mjs.map +0 -1
  18. 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-DfRFkfPh.mjs').then((m) => m.ActionListPageComponent),
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-O7XSRAVo.mjs').then((m) => m.ActionFormPageComponent),
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-O7XSRAVo.mjs').then((m) => m.ActionFormPageComponent),
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-CvJrtElJ.mjs').then((m) => m.RoleListPageComponent),
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-BIgs_ePw.mjs').then((m) => m.RoleFormPageComponent),
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-BIgs_ePw.mjs').then((m) => m.RoleFormPageComponent),
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-DhfA39qe.mjs').then((m) => m.PermissionPageComponent),
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, buildActionParentTree as b, convertActionToTreeNode as c, RoleActionSelectorComponent as d, UserActionSelectorComponent as e, ActionPermissionLogicService as f, IAM_ROUTES as g, MyPermissionsApiService as h, PermissionStateService as i, ProfilePermissionProviderAdapter as j, provideIamProviders as p };
4873
- //# sourceMappingURL=flusys-ng-iam-flusys-ng-iam--WniMPhV.mjs.map
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