@masterteam/users-groups 0.0.14 → 0.0.16

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.
@@ -22,6 +22,7 @@ import { DynamicForm } from '@masterteam/forms/dynamic-form';
22
22
  import { ModalRef, DialogService } from '@masterteam/components/dialog';
23
23
  import { Tabs } from '@masterteam/components/tabs';
24
24
  import { finalize } from 'rxjs';
25
+ import { SecureImagePipe } from '@masterteam/components/upload-field';
25
26
 
26
27
  class GetUsers {
27
28
  static type = '[UsersGroups] Get Users';
@@ -63,6 +64,20 @@ class DeleteUser {
63
64
  this.userName = userName;
64
65
  }
65
66
  }
67
+ class LinkUserToApplication {
68
+ userName;
69
+ static type = '[UsersGroups] Link User To Application';
70
+ constructor(userName) {
71
+ this.userName = userName;
72
+ }
73
+ }
74
+ class GetUserSummary {
75
+ userName;
76
+ static type = '[UsersGroups] Get User Summary';
77
+ constructor(userName) {
78
+ this.userName = userName;
79
+ }
80
+ }
66
81
  class ResetUserPassword {
67
82
  id;
68
83
  payload;
@@ -123,6 +138,8 @@ var UsersGroupsActionKey;
123
138
  UsersGroupsActionKey["CreateUser"] = "createUser";
124
139
  UsersGroupsActionKey["UpdateUser"] = "updateUser";
125
140
  UsersGroupsActionKey["DeleteUser"] = "deleteUser";
141
+ UsersGroupsActionKey["LinkUserToApplication"] = "linkUserToApplication";
142
+ UsersGroupsActionKey["GetUserSummary"] = "getUserSummary";
126
143
  UsersGroupsActionKey["AddUserToGroup"] = "addUserToGroup";
127
144
  UsersGroupsActionKey["DeleteUserFromGroup"] = "deleteUserFromGroup";
128
145
  UsersGroupsActionKey["GetGroups"] = "getGroups";
@@ -144,6 +161,9 @@ let UsersGroupsState = class UsersGroupsState extends CrudStateBase {
144
161
  context = new HttpContext().set(REQUEST_CONTEXT, {
145
162
  useBaseUrl: false,
146
163
  });
164
+ encodeUserName(userName) {
165
+ return encodeURIComponent(String(userName));
166
+ }
147
167
  // ============================================================================
148
168
  // Data Selectors - Individual selectors for fine-grained reactivity
149
169
  // ============================================================================
@@ -153,6 +173,9 @@ let UsersGroupsState = class UsersGroupsState extends CrudStateBase {
153
173
  static getSelectedUser(state) {
154
174
  return state.selectedUser;
155
175
  }
176
+ static getUserSummary(state) {
177
+ return state.userSummary;
178
+ }
156
179
  static getGroups(state) {
157
180
  return state.groups;
158
181
  }
@@ -185,7 +208,8 @@ let UsersGroupsState = class UsersGroupsState extends CrudStateBase {
185
208
  });
186
209
  }
187
210
  getUser(ctx, { userName }) {
188
- const req$ = this.http.get(`Identity/users/${userName}?editMode=true`, { context: this.context });
211
+ const encodedUserName = this.encodeUserName(userName ?? '');
212
+ const req$ = this.http.get(`Identity/users/${encodedUserName}?editMode=true`, { context: this.context });
189
213
  return handleApiRequest({
190
214
  ctx,
191
215
  key: UsersGroupsActionKey.GetUser,
@@ -238,7 +262,8 @@ let UsersGroupsState = class UsersGroupsState extends CrudStateBase {
238
262
  });
239
263
  }
240
264
  updateUser(ctx, { changes, id }) {
241
- const req$ = this.http.put(`Identity/users/${id}`, changes, {
265
+ const encodedUserName = this.encodeUserName(id ?? '');
266
+ const req$ = this.http.put(`Identity/users/${encodedUserName}`, changes, {
242
267
  context: this.context,
243
268
  });
244
269
  return handleApiRequest({
@@ -260,19 +285,58 @@ let UsersGroupsState = class UsersGroupsState extends CrudStateBase {
260
285
  });
261
286
  }
262
287
  deleteUser(ctx, { userName }) {
263
- const req$ = this.http.delete(`Identity/users/${userName}`, {
288
+ const encodedUserName = this.encodeUserName(userName);
289
+ const userNameKey = String(userName);
290
+ const req$ = this.http.delete(`Identity/users/${encodedUserName}`, {
264
291
  context: this.context,
265
292
  });
266
- return this.delete(ctx, {
293
+ return handleApiRequest({
294
+ ctx,
267
295
  key: UsersGroupsActionKey.DeleteUser,
268
296
  request$: req$,
269
- stateProperty: (state) => state.users,
270
- uniqueKey: 'userName',
271
- id: userName,
297
+ onSuccess: (_response, state) => ({
298
+ users: this.adapter.updateOne(state.users, userNameKey, { isLinkedToCurrentApplication: false }, 'userName'),
299
+ }),
300
+ });
301
+ }
302
+ linkUserToApplication(ctx, { userName }) {
303
+ const encodedUserName = this.encodeUserName(userName);
304
+ const userNameKey = String(userName);
305
+ const req$ = this.http.put(`Identity/users/${encodedUserName}/application/link`, {}, {
306
+ context: this.context,
307
+ });
308
+ return handleApiRequest({
309
+ ctx,
310
+ key: UsersGroupsActionKey.LinkUserToApplication,
311
+ request$: req$,
312
+ onSuccess: (response, state) => {
313
+ const linkedUser = {
314
+ ...(state.users.find((user) => user.userName === userNameKey) ?? {}),
315
+ ...(response.data ?? {}),
316
+ userName: userNameKey,
317
+ isLinkedToCurrentApplication: true,
318
+ };
319
+ return {
320
+ users: this.adapter.upsertOne(state.users, linkedUser, 'userName'),
321
+ };
322
+ },
323
+ });
324
+ }
325
+ getUserSummary(ctx, { userName }) {
326
+ const encodedUserName = this.encodeUserName(userName);
327
+ const req$ = this.http.get(`identity/users/${encodedUserName}/summary`, { context: this.context });
328
+ return handleApiRequest({
329
+ ctx,
330
+ key: UsersGroupsActionKey.GetUserSummary,
331
+ request$: req$,
332
+ onSuccess: (response) => ({
333
+ userSummary: response.data ?? null,
334
+ }),
272
335
  });
273
336
  }
274
337
  resetUserPassword(ctx, { id, payload }) {
275
- const req$ = this.http.put(`Identity/users/${id}/password/reset`, payload, {
338
+ const encodedUserName = this.encodeUserName(id);
339
+ const req$ = this.http.put(`Identity/users/${encodedUserName}/password/reset`, payload, {
276
340
  context: this.context,
277
341
  });
278
342
  return handleApiRequest({
@@ -394,6 +458,12 @@ __decorate([
394
458
  __decorate([
395
459
  Action(DeleteUser)
396
460
  ], UsersGroupsState.prototype, "deleteUser", null);
461
+ __decorate([
462
+ Action(LinkUserToApplication)
463
+ ], UsersGroupsState.prototype, "linkUserToApplication", null);
464
+ __decorate([
465
+ Action(GetUserSummary)
466
+ ], UsersGroupsState.prototype, "getUserSummary", null);
397
467
  __decorate([
398
468
  Action(ResetUserPassword)
399
469
  ], UsersGroupsState.prototype, "resetUserPassword", null);
@@ -424,6 +494,9 @@ __decorate([
424
494
  __decorate([
425
495
  Selector()
426
496
  ], UsersGroupsState, "getSelectedUser", null);
497
+ __decorate([
498
+ Selector()
499
+ ], UsersGroupsState, "getUserSummary", null);
427
500
  __decorate([
428
501
  Selector()
429
502
  ], UsersGroupsState, "getGroups", null);
@@ -442,6 +515,7 @@ UsersGroupsState = __decorate([
442
515
  defaults: {
443
516
  users: [],
444
517
  selectedUser: null,
518
+ userSummary: null,
445
519
  groups: [],
446
520
  selectedGroup: null,
447
521
  loadingActive: [],
@@ -451,7 +525,7 @@ UsersGroupsState = __decorate([
451
525
  ], UsersGroupsState);
452
526
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: UsersGroupsState, decorators: [{
453
527
  type: Injectable
454
- }], propDecorators: { getUsers: [], getUser: [], createUser: [], addUserToGroup: [], updateUser: [], deleteUser: [], resetUserPassword: [], deleteUserFromGroup: [], getGroups: [], getGroup: [], createGroup: [], updateGroup: [], deleteGroup: [], clearSelectedGroup: [] } });
528
+ }], propDecorators: { getUsers: [], getUser: [], createUser: [], addUserToGroup: [], updateUser: [], deleteUser: [], linkUserToApplication: [], getUserSummary: [], resetUserPassword: [], deleteUserFromGroup: [], getGroups: [], getGroup: [], createGroup: [], updateGroup: [], deleteGroup: [], clearSelectedGroup: [] } });
455
529
 
456
530
  class UsersGroupsFacade {
457
531
  store = inject(Store);
@@ -460,6 +534,7 @@ class UsersGroupsFacade {
460
534
  // ============================================================================
461
535
  users = select(UsersGroupsState.getUsers);
462
536
  selectedUser = select(UsersGroupsState.getSelectedUser);
537
+ userSummary = select(UsersGroupsState.getUserSummary);
463
538
  groups = select(UsersGroupsState.getGroups);
464
539
  selectedGroup = select(UsersGroupsState.getSelectedGroup);
465
540
  // ============================================================================
@@ -474,6 +549,8 @@ class UsersGroupsFacade {
474
549
  isSavingUser = computed(() => this.loadingActive().includes(UsersGroupsActionKey.CreateUser) ||
475
550
  this.loadingActive().includes(UsersGroupsActionKey.UpdateUser), ...(ngDevMode ? [{ debugName: "isSavingUser" }] : /* istanbul ignore next */ []));
476
551
  isDeletingUser = computed(() => this.loadingActive().includes(UsersGroupsActionKey.DeleteUser), ...(ngDevMode ? [{ debugName: "isDeletingUser" }] : /* istanbul ignore next */ []));
552
+ isLinkingUserToApplication = computed(() => this.loadingActive().includes(UsersGroupsActionKey.LinkUserToApplication), ...(ngDevMode ? [{ debugName: "isLinkingUserToApplication" }] : /* istanbul ignore next */ []));
553
+ isLoadingUserSummary = computed(() => this.loadingActive().includes(UsersGroupsActionKey.GetUserSummary), ...(ngDevMode ? [{ debugName: "isLoadingUserSummary" }] : /* istanbul ignore next */ []));
477
554
  isLoadingGroups = computed(() => this.loadingActive().includes(UsersGroupsActionKey.GetGroups), ...(ngDevMode ? [{ debugName: "isLoadingGroups" }] : /* istanbul ignore next */ []));
478
555
  isLoadingGroup = computed(() => this.loadingActive().includes(UsersGroupsActionKey.GetGroup), ...(ngDevMode ? [{ debugName: "isLoadingGroup" }] : /* istanbul ignore next */ []));
479
556
  isSavingGroup = computed(() => this.loadingActive().includes(UsersGroupsActionKey.CreateGroup) ||
@@ -506,6 +583,12 @@ class UsersGroupsFacade {
506
583
  deleteUser(userName) {
507
584
  return this.store.dispatch(new DeleteUser(userName));
508
585
  }
586
+ linkUserToApplication(userName) {
587
+ return this.store.dispatch(new LinkUserToApplication(userName));
588
+ }
589
+ getUserSummary(userName) {
590
+ return this.store.dispatch(new GetUserSummary(userName));
591
+ }
509
592
  resetUserPassword(id, payload) {
510
593
  return this.store.dispatch(new ResetUserPassword(id, payload));
511
594
  }
@@ -878,6 +961,21 @@ class ResetPasswordForm {
878
961
  ValidatorConfig.pattern(this.passwordPattern, this.translocoService.translate('users-groups.password-rules')),
879
962
  ],
880
963
  },
964
+ {
965
+ key: 'confirmPassword',
966
+ inputType: 'password',
967
+ label: this.translocoService.translate('users-groups.confirm-password'),
968
+ placeholder: this.translocoService.translate('users-groups.confirm-password'),
969
+ hint: this.translocoService.translate('users-groups.password-rules'),
970
+ validators: [
971
+ ValidatorConfig.required(),
972
+ ValidatorConfig.pattern(this.passwordPattern, this.translocoService.translate('users-groups.password-rules')),
973
+ ValidatorConfig.custom((value, control) => {
974
+ const newPassword = control?.parent?.get('newPassword')?.value;
975
+ return !value || !newPassword || value === newPassword;
976
+ }, this.translocoService.translate('users-groups.passwords-must-match')),
977
+ ],
978
+ },
881
979
  ],
882
980
  },
883
981
  ],
@@ -886,6 +984,7 @@ class ResetPasswordForm {
886
984
  this.resetPasswordFormControl.setValue({
887
985
  displayName: this.user()?.displayName || '',
888
986
  newPassword: '',
987
+ confirmPassword: '',
889
988
  });
890
989
  }
891
990
  onSubmit() {
@@ -916,9 +1015,136 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
916
1015
  ], providers: [DialogService], template: "<ng-container *transloco=\"let t; prefix: 'users-groups'\">\r\n <form class=\"p-4\" [class]=\"modal.contentClass\">\r\n <mt-dynamic-form\r\n [formConfig]=\"resetPasswordFormConfig()\"\r\n [formControl]=\"resetPasswordFormControl\"\r\n />\r\n </form>\r\n\r\n <div [class]=\"modal.footerClass\">\r\n <mt-button\r\n [label]=\"t('cancel')\"\r\n variant=\"outlined\"\r\n [disabled]=\"passwordResetLoading()\"\r\n (click)=\"ref.close(false)\"\r\n />\r\n <mt-button\r\n [label]=\"'save' | transloco\"\r\n (click)=\"onSubmit()\"\r\n [loading]=\"passwordResetLoading()\"\r\n [disabled]=\"!this.resetPasswordFormControl.valid\"\r\n />\r\n </div>\r\n</ng-container>\r\n" }]
917
1016
  }], propDecorators: { user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }] } });
918
1017
 
1018
+ class UserLinkedGroups {
1019
+ facade = inject(UsersGroupsFacade);
1020
+ translocoService = inject(TranslocoService);
1021
+ ref = inject(ModalRef);
1022
+ user = input(null, ...(ngDevMode ? [{ debugName: "user" }] : /* istanbul ignore next */ []));
1023
+ context = new HttpContext().set(REQUEST_CONTEXT, {
1024
+ useBaseUrl: true,
1025
+ });
1026
+ loading = this.facade.isLoadingUserSummary;
1027
+ summary = this.facade.userSummary;
1028
+ activeTab = signal('groups', ...(ngDevMode ? [{ debugName: "activeTab" }] : /* istanbul ignore next */ []));
1029
+ groups = computed(() => {
1030
+ const lang = this.lang();
1031
+ return (this.summary()?.groups ?? []).map((group) => ({
1032
+ id: group.id,
1033
+ name: this.resolveTranslated(group.name, lang),
1034
+ isActivated: !!group.isActivated,
1035
+ membersCount: group.membersCount ?? 0,
1036
+ }));
1037
+ }, ...(ngDevMode ? [{ debugName: "groups" }] : /* istanbul ignore next */ []));
1038
+ roles = computed(() => {
1039
+ const lang = this.lang();
1040
+ return (this.summary()?.roles ?? []).map((role) => ({
1041
+ id: role.id,
1042
+ name: this.resolveTranslated(role.name, lang),
1043
+ moduleType: role.moduleType ?? '',
1044
+ }));
1045
+ }, ...(ngDevMode ? [{ debugName: "roles" }] : /* istanbul ignore next */ []));
1046
+ permissions = computed(() => {
1047
+ const lang = this.lang();
1048
+ return (this.summary()?.permissions ?? []).map((permission) => ({
1049
+ id: permission.id,
1050
+ name: this.resolveTranslated(permission.name, lang),
1051
+ command: permission.command ?? '',
1052
+ moduleType: permission.moduleType ?? '',
1053
+ }));
1054
+ }, ...(ngDevMode ? [{ debugName: "permissions" }] : /* istanbul ignore next */ []));
1055
+ accessibilities = computed(() => {
1056
+ const lang = this.lang();
1057
+ return (this.summary()?.accessibilities ?? []).map((accessibility) => ({
1058
+ id: accessibility.id,
1059
+ name: this.resolveTranslated(accessibility.moduleName, lang),
1060
+ category: this.resolveTranslated(accessibility.name, lang),
1061
+ }));
1062
+ }, ...(ngDevMode ? [{ debugName: "accessibilities" }] : /* istanbul ignore next */ []));
1063
+ userImage = computed(() => this.summary()?.user?.image ?? this.user()?.image ?? '', ...(ngDevMode ? [{ debugName: "userImage" }] : /* istanbul ignore next */ []));
1064
+ userDisplayName = computed(() => this.summary()?.user?.displayName ?? this.user()?.displayName ?? '', ...(ngDevMode ? [{ debugName: "userDisplayName" }] : /* istanbul ignore next */ []));
1065
+ tabs = computed(() => [
1066
+ {
1067
+ label: `${this.translocoService.translate('users-groups.groups')} (${this.groups().length})`,
1068
+ value: 'groups',
1069
+ },
1070
+ {
1071
+ label: `${this.translocoService.translate('users-groups.permissions')} (${this.permissions().length})`,
1072
+ value: 'permissions',
1073
+ },
1074
+ {
1075
+ label: `${this.translocoService.translate('users-groups.roles')} (${this.roles().length})`,
1076
+ value: 'roles',
1077
+ },
1078
+ {
1079
+ label: `${this.translocoService.translate('users-groups.accessibilities')} (${this.accessibilities().length})`,
1080
+ value: 'accessibilities',
1081
+ },
1082
+ ], ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
1083
+ isEmpty = computed(() => {
1084
+ if (this.loading())
1085
+ return false;
1086
+ switch (this.activeTab()) {
1087
+ case 'groups':
1088
+ return this.groups().length === 0;
1089
+ case 'permissions':
1090
+ return this.permissions().length === 0;
1091
+ case 'roles':
1092
+ return this.roles().length === 0;
1093
+ case 'accessibilities':
1094
+ return this.accessibilities().length === 0;
1095
+ }
1096
+ }, ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
1097
+ ngOnInit() {
1098
+ const userName = this.user()?.userName;
1099
+ if (!userName)
1100
+ return;
1101
+ this.facade.getUserSummary(userName);
1102
+ }
1103
+ lang() {
1104
+ return this.translocoService.getActiveLang();
1105
+ }
1106
+ resolveTranslated(value, lang) {
1107
+ if (!value)
1108
+ return '';
1109
+ if (typeof value === 'object') {
1110
+ const v = value;
1111
+ return v['display'] || v[lang] || v['en'] || v['ar'] || '';
1112
+ }
1113
+ if (typeof value === 'string') {
1114
+ const trimmed = value.trim();
1115
+ if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
1116
+ try {
1117
+ const parsed = JSON.parse(trimmed);
1118
+ return parsed[lang] || parsed['en'] || parsed['ar'] || '';
1119
+ }
1120
+ catch {
1121
+ return value;
1122
+ }
1123
+ }
1124
+ return value;
1125
+ }
1126
+ return '';
1127
+ }
1128
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: UserLinkedGroups, deps: [], target: i0.ɵɵFactoryTarget.Component });
1129
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: UserLinkedGroups, isStandalone: true, selector: "mt-user-linked-groups", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'users-groups'\">\r\n <div class=\"flex flex-col gap-4 p-4 max-h-[75vh] overflow-auto\">\r\n <div class=\"flex items-center min-w-0\">\r\n <mt-tabs\r\n class=\"flex-1 min-w-0\"\r\n [options]=\"tabs()\"\r\n [(active)]=\"activeTab\"\r\n mode=\"underline\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n ></mt-tabs>\r\n <div\r\n class=\"flex items-center gap-2 pb-2 px-2 shrink-0 border-b border-slate-200 dark:border-slate-700\"\r\n >\r\n <mt-avatar\r\n [image]=\"\r\n userImage()\r\n ? $any(userImage() | secureImage: true : context : 'avatar/')\r\n : ''\r\n \"\r\n [icon]=\"'user.user-01'\"\r\n styleClass=\"w-8! h-8! text-lg! text-gray-600! bg-surface-300!\"\r\n ></mt-avatar>\r\n <div class=\"flex flex-col min-w-0\">\r\n <span\r\n class=\"text-sm font-medium text-slate-600 dark:text-slate-100 truncate\"\r\n [title]=\"userDisplayName()\"\r\n >\r\n {{ userDisplayName() }}\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n @if (loading()) {\r\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\r\n <p-skeleton height=\"4rem\"></p-skeleton>\r\n <p-skeleton height=\"4rem\"></p-skeleton>\r\n <p-skeleton height=\"4rem\"></p-skeleton>\r\n <p-skeleton height=\"4rem\"></p-skeleton>\r\n </div>\r\n } @else if (isEmpty()) {\r\n <div\r\n class=\"flex flex-col items-center justify-center text-center py-10 gap-2 text-slate-500 dark:text-slate-400\"\r\n >\r\n <mt-icon icon=\"general.link-broken-01\" class=\"text-2xl\" />\r\n <span class=\"text-sm font-medium\">\r\n @switch (activeTab()) {\r\n @case (\"groups\") {\r\n {{ t(\"no-linked-groups\") }}\r\n }\r\n @case (\"permissions\") {\r\n {{ t(\"no-permissions\") }}\r\n }\r\n @case (\"roles\") {\r\n {{ t(\"no-roles\") }}\r\n }\r\n @case (\"accessibilities\") {\r\n {{ t(\"no-accessibilities\") }}\r\n }\r\n }\r\n </span>\r\n </div>\r\n } @else {\r\n @switch (activeTab()) {\r\n @case (\"groups\") {\r\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-3 pb-3\">\r\n @for (group of groups(); track group.id) {\r\n <div\r\n class=\"flex flex-col gap-2 rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 p-3 hover:shadow-sm transition-shadow\"\r\n >\r\n <div class=\"flex items-start justify-between gap-2\">\r\n <div class=\"flex items-center gap-3 min-w-0\">\r\n <span\r\n class=\"inline-flex items-center justify-center rounded-md bg-indigo-50 dark:bg-indigo-900/30 p-2\"\r\n >\r\n <mt-icon\r\n icon=\"user.users-01\"\r\n class=\"text-indigo-600 dark:text-indigo-300 text-xl\"\r\n />\r\n </span>\r\n <div class=\"flex flex-col min-w-0\">\r\n <span\r\n class=\"text-md text-slate-800 dark:text-slate-100 truncate\"\r\n [title]=\"group.name\"\r\n >\r\n {{ group.name }}\r\n </span>\r\n <span\r\n class=\"inline-flex items-center gap-1 text-xs text-slate-500 dark:text-slate-400\"\r\n >\r\n <mt-icon icon=\"user.users-01\" class=\"text-sm\" />\r\n {{ group.membersCount }} {{ t(\"users-number\") }}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n @if (group.isActivated) {\r\n <span\r\n class=\"inline-flex items-center gap-1 rounded-md bg-emerald-50 dark:bg-emerald-900/30 px-2 py-0.5 shrink-0\"\r\n >\r\n <span\r\n class=\"w-1.5 h-1.5 rounded-full bg-emerald-500\"\r\n ></span>\r\n <span\r\n class=\"text-emerald-700 dark:text-emerald-200 text-xs font-medium\"\r\n >\r\n {{ t(\"active\") }}\r\n </span>\r\n </span>\r\n } @else {\r\n <span\r\n class=\"inline-flex items-center gap-1 rounded-md bg-slate-100 dark:bg-slate-700 px-2 py-0.5 shrink-0\"\r\n >\r\n <span\r\n class=\"w-1.5 h-1.5 rounded-full bg-slate-400\"\r\n ></span>\r\n <span\r\n class=\"text-slate-600 dark:text-slate-200 text-xs font-medium\"\r\n >\r\n {{ t(\"inactive\") }}\r\n </span>\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @case (\"permissions\") {\r\n <div\r\n class=\"flex flex-col divide-y divide-slate-100 dark:divide-slate-700 rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800\"\r\n >\r\n @for (permission of permissions(); track permission.id) {\r\n <div class=\"flex items-center gap-3 p-3\">\r\n <span\r\n class=\"inline-flex items-center justify-center rounded-md bg-amber-50 dark:bg-amber-900/30 p-2 shrink-0\"\r\n >\r\n <mt-icon\r\n icon=\"security.shield-tick\"\r\n class=\"text-amber-600 dark:text-amber-300 text-lg\"\r\n />\r\n </span>\r\n <div class=\"flex flex-col min-w-0 flex-1\">\r\n <span\r\n class=\"text-sm font-medium text-slate-800 dark:text-slate-100 truncate\"\r\n [title]=\"permission.name\"\r\n >\r\n {{ permission.name }}\r\n </span>\r\n <span\r\n class=\"text-xs text-slate-500 dark:text-slate-400 truncate\"\r\n [title]=\"permission.command\"\r\n >\r\n {{ permission.command }}\r\n </span>\r\n </div>\r\n @if (permission.moduleType) {\r\n <span\r\n class=\"inline-flex items-center rounded-md bg-slate-50 dark:bg-slate-900/30 px-2 py-0.5 shrink-0 text-xs text-slate-600 dark:text-slate-300 font-medium\"\r\n >\r\n {{ permission.moduleType }}\r\n </span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @case (\"roles\") {\r\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-3 pb-3\">\r\n @for (role of roles(); track role.id) {\r\n <div\r\n class=\"flex items-center gap-3 rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 p-3\"\r\n >\r\n <span\r\n class=\"inline-flex items-center justify-center rounded-md bg-violet-50 dark:bg-violet-900/30 p-2 shrink-0\"\r\n >\r\n <mt-icon\r\n icon=\"user.user-check-01\"\r\n class=\"text-violet-600 dark:text-violet-300 text-xl\"\r\n />\r\n </span>\r\n <div class=\"flex flex-col min-w-0 flex-1\">\r\n <span\r\n class=\"text-md text-slate-800 dark:text-slate-100 truncate\"\r\n [title]=\"role.name\"\r\n >\r\n {{ role.name }}\r\n </span>\r\n @if (role.moduleType) {\r\n <span class=\"text-xs text-slate-500 dark:text-slate-400\">\r\n {{ role.moduleType }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @case (\"accessibilities\") {\r\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-3 pb-3\">\r\n @for (accessibility of accessibilities(); track accessibility.id) {\r\n <div\r\n class=\"flex items-center gap-3 rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 p-3\"\r\n >\r\n <span\r\n class=\"inline-flex items-center justify-center rounded-md bg-sky-50 dark:bg-sky-900/30 p-2 shrink-0\"\r\n >\r\n <mt-icon\r\n icon=\"security.lock-unlocked-01\"\r\n class=\"text-sky-600 dark:text-sky-300 text-xl\"\r\n />\r\n </span>\r\n <div class=\"flex flex-col min-w-0 flex-1\">\r\n <span\r\n class=\"text-md text-slate-800 dark:text-slate-100 truncate\"\r\n [title]=\"accessibility.name\"\r\n >\r\n {{ accessibility.name }}\r\n </span>\r\n @if (accessibility.category) {\r\n <span class=\"text-xs text-slate-500 dark:text-slate-400\">\r\n {{ accessibility.category }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n</ng-container>\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "mode", "moreLabel", "size", "fluid", "disabled"], outputs: ["activeChange", "onChange"] }, { kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { kind: "pipe", type: SecureImagePipe, name: "secureImage" }] });
1130
+ }
1131
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: UserLinkedGroups, decorators: [{
1132
+ type: Component,
1133
+ args: [{ selector: 'mt-user-linked-groups', standalone: true, imports: [
1134
+ CommonModule,
1135
+ TranslocoDirective,
1136
+ SkeletonModule,
1137
+ Icon,
1138
+ Tabs,
1139
+ Avatar,
1140
+ SecureImagePipe,
1141
+ ], template: "<ng-container *transloco=\"let t; prefix: 'users-groups'\">\r\n <div class=\"flex flex-col gap-4 p-4 max-h-[75vh] overflow-auto\">\r\n <div class=\"flex items-center min-w-0\">\r\n <mt-tabs\r\n class=\"flex-1 min-w-0\"\r\n [options]=\"tabs()\"\r\n [(active)]=\"activeTab\"\r\n mode=\"underline\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n ></mt-tabs>\r\n <div\r\n class=\"flex items-center gap-2 pb-2 px-2 shrink-0 border-b border-slate-200 dark:border-slate-700\"\r\n >\r\n <mt-avatar\r\n [image]=\"\r\n userImage()\r\n ? $any(userImage() | secureImage: true : context : 'avatar/')\r\n : ''\r\n \"\r\n [icon]=\"'user.user-01'\"\r\n styleClass=\"w-8! h-8! text-lg! text-gray-600! bg-surface-300!\"\r\n ></mt-avatar>\r\n <div class=\"flex flex-col min-w-0\">\r\n <span\r\n class=\"text-sm font-medium text-slate-600 dark:text-slate-100 truncate\"\r\n [title]=\"userDisplayName()\"\r\n >\r\n {{ userDisplayName() }}\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n @if (loading()) {\r\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\r\n <p-skeleton height=\"4rem\"></p-skeleton>\r\n <p-skeleton height=\"4rem\"></p-skeleton>\r\n <p-skeleton height=\"4rem\"></p-skeleton>\r\n <p-skeleton height=\"4rem\"></p-skeleton>\r\n </div>\r\n } @else if (isEmpty()) {\r\n <div\r\n class=\"flex flex-col items-center justify-center text-center py-10 gap-2 text-slate-500 dark:text-slate-400\"\r\n >\r\n <mt-icon icon=\"general.link-broken-01\" class=\"text-2xl\" />\r\n <span class=\"text-sm font-medium\">\r\n @switch (activeTab()) {\r\n @case (\"groups\") {\r\n {{ t(\"no-linked-groups\") }}\r\n }\r\n @case (\"permissions\") {\r\n {{ t(\"no-permissions\") }}\r\n }\r\n @case (\"roles\") {\r\n {{ t(\"no-roles\") }}\r\n }\r\n @case (\"accessibilities\") {\r\n {{ t(\"no-accessibilities\") }}\r\n }\r\n }\r\n </span>\r\n </div>\r\n } @else {\r\n @switch (activeTab()) {\r\n @case (\"groups\") {\r\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-3 pb-3\">\r\n @for (group of groups(); track group.id) {\r\n <div\r\n class=\"flex flex-col gap-2 rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 p-3 hover:shadow-sm transition-shadow\"\r\n >\r\n <div class=\"flex items-start justify-between gap-2\">\r\n <div class=\"flex items-center gap-3 min-w-0\">\r\n <span\r\n class=\"inline-flex items-center justify-center rounded-md bg-indigo-50 dark:bg-indigo-900/30 p-2\"\r\n >\r\n <mt-icon\r\n icon=\"user.users-01\"\r\n class=\"text-indigo-600 dark:text-indigo-300 text-xl\"\r\n />\r\n </span>\r\n <div class=\"flex flex-col min-w-0\">\r\n <span\r\n class=\"text-md text-slate-800 dark:text-slate-100 truncate\"\r\n [title]=\"group.name\"\r\n >\r\n {{ group.name }}\r\n </span>\r\n <span\r\n class=\"inline-flex items-center gap-1 text-xs text-slate-500 dark:text-slate-400\"\r\n >\r\n <mt-icon icon=\"user.users-01\" class=\"text-sm\" />\r\n {{ group.membersCount }} {{ t(\"users-number\") }}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n @if (group.isActivated) {\r\n <span\r\n class=\"inline-flex items-center gap-1 rounded-md bg-emerald-50 dark:bg-emerald-900/30 px-2 py-0.5 shrink-0\"\r\n >\r\n <span\r\n class=\"w-1.5 h-1.5 rounded-full bg-emerald-500\"\r\n ></span>\r\n <span\r\n class=\"text-emerald-700 dark:text-emerald-200 text-xs font-medium\"\r\n >\r\n {{ t(\"active\") }}\r\n </span>\r\n </span>\r\n } @else {\r\n <span\r\n class=\"inline-flex items-center gap-1 rounded-md bg-slate-100 dark:bg-slate-700 px-2 py-0.5 shrink-0\"\r\n >\r\n <span\r\n class=\"w-1.5 h-1.5 rounded-full bg-slate-400\"\r\n ></span>\r\n <span\r\n class=\"text-slate-600 dark:text-slate-200 text-xs font-medium\"\r\n >\r\n {{ t(\"inactive\") }}\r\n </span>\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @case (\"permissions\") {\r\n <div\r\n class=\"flex flex-col divide-y divide-slate-100 dark:divide-slate-700 rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800\"\r\n >\r\n @for (permission of permissions(); track permission.id) {\r\n <div class=\"flex items-center gap-3 p-3\">\r\n <span\r\n class=\"inline-flex items-center justify-center rounded-md bg-amber-50 dark:bg-amber-900/30 p-2 shrink-0\"\r\n >\r\n <mt-icon\r\n icon=\"security.shield-tick\"\r\n class=\"text-amber-600 dark:text-amber-300 text-lg\"\r\n />\r\n </span>\r\n <div class=\"flex flex-col min-w-0 flex-1\">\r\n <span\r\n class=\"text-sm font-medium text-slate-800 dark:text-slate-100 truncate\"\r\n [title]=\"permission.name\"\r\n >\r\n {{ permission.name }}\r\n </span>\r\n <span\r\n class=\"text-xs text-slate-500 dark:text-slate-400 truncate\"\r\n [title]=\"permission.command\"\r\n >\r\n {{ permission.command }}\r\n </span>\r\n </div>\r\n @if (permission.moduleType) {\r\n <span\r\n class=\"inline-flex items-center rounded-md bg-slate-50 dark:bg-slate-900/30 px-2 py-0.5 shrink-0 text-xs text-slate-600 dark:text-slate-300 font-medium\"\r\n >\r\n {{ permission.moduleType }}\r\n </span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @case (\"roles\") {\r\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-3 pb-3\">\r\n @for (role of roles(); track role.id) {\r\n <div\r\n class=\"flex items-center gap-3 rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 p-3\"\r\n >\r\n <span\r\n class=\"inline-flex items-center justify-center rounded-md bg-violet-50 dark:bg-violet-900/30 p-2 shrink-0\"\r\n >\r\n <mt-icon\r\n icon=\"user.user-check-01\"\r\n class=\"text-violet-600 dark:text-violet-300 text-xl\"\r\n />\r\n </span>\r\n <div class=\"flex flex-col min-w-0 flex-1\">\r\n <span\r\n class=\"text-md text-slate-800 dark:text-slate-100 truncate\"\r\n [title]=\"role.name\"\r\n >\r\n {{ role.name }}\r\n </span>\r\n @if (role.moduleType) {\r\n <span class=\"text-xs text-slate-500 dark:text-slate-400\">\r\n {{ role.moduleType }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @case (\"accessibilities\") {\r\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-3 pb-3\">\r\n @for (accessibility of accessibilities(); track accessibility.id) {\r\n <div\r\n class=\"flex items-center gap-3 rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 p-3\"\r\n >\r\n <span\r\n class=\"inline-flex items-center justify-center rounded-md bg-sky-50 dark:bg-sky-900/30 p-2 shrink-0\"\r\n >\r\n <mt-icon\r\n icon=\"security.lock-unlocked-01\"\r\n class=\"text-sky-600 dark:text-sky-300 text-xl\"\r\n />\r\n </span>\r\n <div class=\"flex flex-col min-w-0 flex-1\">\r\n <span\r\n class=\"text-md text-slate-800 dark:text-slate-100 truncate\"\r\n [title]=\"accessibility.name\"\r\n >\r\n {{ accessibility.name }}\r\n </span>\r\n @if (accessibility.category) {\r\n <span class=\"text-xs text-slate-500 dark:text-slate-400\">\r\n {{ accessibility.category }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n</ng-container>\r\n" }]
1142
+ }], propDecorators: { user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }] } });
1143
+
919
1144
  class Users {
920
1145
  typeCol = viewChild.required('typeCol');
921
1146
  userCol = viewChild.required('userCol');
1147
+ applicationLinkCol = viewChild.required('applicationLinkCol');
922
1148
  facade = inject(UsersGroupsFacade);
923
1149
  modal = inject(ModalService);
924
1150
  translocoService = inject(TranslocoService);
@@ -930,6 +1156,8 @@ class Users {
930
1156
  return all.filter((user) => !user.isExternal);
931
1157
  case 'external':
932
1158
  return all.filter((user) => user.isExternal);
1159
+ case 'notLinked':
1160
+ return all.filter((user) => !this.isLinkedToCurrentApplication(user));
933
1161
  case 'all':
934
1162
  default:
935
1163
  return all;
@@ -948,6 +1176,10 @@ class Users {
948
1176
  label: this.translocoService.translate('users-groups.external'),
949
1177
  value: 'external',
950
1178
  },
1179
+ {
1180
+ label: this.translocoService.translate('users-groups.not-linked'),
1181
+ value: 'notLinked',
1182
+ },
951
1183
  ], ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
952
1184
  activeTab = signal('all', ...(ngDevMode ? [{ debugName: "activeTab" }] : /* istanbul ignore next */ []));
953
1185
  tableActions = signal([
@@ -961,7 +1193,17 @@ class Users {
961
1193
  },
962
1194
  ], ...(ngDevMode ? [{ debugName: "tableActions" }] : /* istanbul ignore next */ []));
963
1195
  deletingRowIds = signal([], ...(ngDevMode ? [{ debugName: "deletingRowIds" }] : /* istanbul ignore next */ []));
1196
+ linkingRowIds = signal([], ...(ngDevMode ? [{ debugName: "linkingRowIds" }] : /* istanbul ignore next */ []));
964
1197
  rowActions = signal([
1198
+ {
1199
+ icon: 'general.link-01',
1200
+ tooltip: this.translocoService.translate('users-groups.linked-groups'),
1201
+ color: 'secondary',
1202
+ action: (row) => {
1203
+ this.openLinkedGroupsDialog(row);
1204
+ },
1205
+ hidden: (row) => !this.isLinkedToCurrentApplication(row),
1206
+ },
965
1207
  {
966
1208
  icon: 'security.key-01',
967
1209
  tooltip: this.translocoService.translate('users-groups.reset-password'),
@@ -969,7 +1211,7 @@ class Users {
969
1211
  action: (row) => {
970
1212
  this.addResetPasswordDialog(row);
971
1213
  },
972
- hidden: (row) => !row.isExternal,
1214
+ hidden: (row) => !row.isExternal || !this.isLinkedToCurrentApplication(row),
973
1215
  },
974
1216
  {
975
1217
  icon: 'custom.pencil',
@@ -978,25 +1220,49 @@ class Users {
978
1220
  action: (row) => {
979
1221
  this.addUserDialog(row);
980
1222
  },
981
- hidden: (row) => !row.isExternal,
1223
+ hidden: (row) => !row.isExternal || !this.isLinkedToCurrentApplication(row),
1224
+ },
1225
+ {
1226
+ icon: 'user.user-plus-01',
1227
+ tooltip: this.translocoService.translate('users-groups.link-to-application'),
1228
+ color: 'success',
1229
+ variant: 'outlined',
1230
+ action: (row) => {
1231
+ this.linkUserToApplication(row);
1232
+ },
1233
+ hidden: (row) => this.isLinkedToCurrentApplication(row),
1234
+ confirmation: {
1235
+ type: 'popup',
1236
+ header: this.translocoService.translate('users-groups.link-confirm-header'),
1237
+ message: this.translocoService.translate('users-groups.link-confirm-message'),
1238
+ icon: 'user.user-plus-01',
1239
+ acceptLabel: this.translocoService.translate('users-groups.link-to-application'),
1240
+ rejectLabel: this.translocoService.translate('users-groups.cancel'),
1241
+ acceptButton: {
1242
+ severity: 'success',
1243
+ },
1244
+ },
1245
+ loading: (row) => this.linkingRowIds().includes(row.userName),
982
1246
  },
983
1247
  {
984
1248
  icon: 'general.trash-01',
985
- tooltip: this.translocoService.translate('delete'),
1249
+ tooltip: this.translocoService.translate('users-groups.unlink-from-application'),
986
1250
  color: 'danger',
987
1251
  variant: 'outlined',
988
1252
  action: (row) => {
989
- this.deletingRowIds.update((ids) => [...ids, row.userName]);
990
- this.facade
991
- .deleteUser(row.userName)
992
- .pipe(finalize(() => {
993
- this.deletingRowIds.update((ids) => ids.filter((id) => id !== row.userName));
994
- }))
995
- .subscribe();
1253
+ this.unlinkUserFromApplication(row);
996
1254
  },
1255
+ hidden: (row) => !this.isLinkedToCurrentApplication(row),
997
1256
  confirmation: {
998
1257
  type: 'popup',
999
- confirmationType: 'delete',
1258
+ header: this.translocoService.translate('users-groups.unlink-confirm-header'),
1259
+ message: this.translocoService.translate('users-groups.unlink-confirm-message'),
1260
+ icon: 'general.link-broken-01',
1261
+ acceptLabel: this.translocoService.translate('users-groups.unlink-from-application'),
1262
+ rejectLabel: this.translocoService.translate('users-groups.cancel'),
1263
+ acceptButton: {
1264
+ severity: 'danger',
1265
+ },
1000
1266
  },
1001
1267
  loading: (row) => this.deletingRowIds().includes(row.userName),
1002
1268
  },
@@ -1018,6 +1284,26 @@ class Users {
1018
1284
  type: 'custom',
1019
1285
  customCellTpl: this.typeCol(),
1020
1286
  },
1287
+ {
1288
+ key: 'isLinkedToCurrentApplication',
1289
+ label: this.translocoService.translate('users-groups.application-link'),
1290
+ type: 'custom',
1291
+ customCellTpl: this.applicationLinkCol(),
1292
+ filterConfig: {
1293
+ type: 'select',
1294
+ label: this.translocoService.translate('users-groups.application-link'),
1295
+ options: [
1296
+ {
1297
+ label: this.translocoService.translate('users-groups.linked'),
1298
+ value: true,
1299
+ },
1300
+ {
1301
+ label: this.translocoService.translate('users-groups.not-linked'),
1302
+ value: false,
1303
+ },
1304
+ ],
1305
+ },
1306
+ },
1021
1307
  {
1022
1308
  key: 'email',
1023
1309
  label: this.translocoService.translate('users-groups.email'),
@@ -1037,6 +1323,35 @@ class Users {
1037
1323
  ngOnInit() {
1038
1324
  this.facade.getUsers();
1039
1325
  }
1326
+ isLinkedToCurrentApplication(user) {
1327
+ return user?.isLinkedToCurrentApplication !== false;
1328
+ }
1329
+ linkUserToApplication(user) {
1330
+ const userName = user?.userName;
1331
+ if (!userName) {
1332
+ return;
1333
+ }
1334
+ this.linkingRowIds.update((ids) => [...ids, userName]);
1335
+ this.facade
1336
+ .linkUserToApplication(userName)
1337
+ .pipe(finalize(() => {
1338
+ this.linkingRowIds.update((ids) => ids.filter((id) => id !== userName));
1339
+ }))
1340
+ .subscribe();
1341
+ }
1342
+ unlinkUserFromApplication(user) {
1343
+ const userName = user?.userName;
1344
+ if (!userName || !this.isLinkedToCurrentApplication(user)) {
1345
+ return;
1346
+ }
1347
+ this.deletingRowIds.update((ids) => [...ids, userName]);
1348
+ this.facade
1349
+ .deleteUser(userName)
1350
+ .pipe(finalize(() => {
1351
+ this.deletingRowIds.update((ids) => ids.filter((id) => id !== userName));
1352
+ }))
1353
+ .subscribe();
1354
+ }
1040
1355
  addUserDialog(user = null) {
1041
1356
  const modalType = user ? 'drawer' : 'dialog';
1042
1357
  this.modal.openModal(UserForm, modalType, {
@@ -1067,8 +1382,19 @@ class Users {
1067
1382
  },
1068
1383
  });
1069
1384
  }
1385
+ openLinkedGroupsDialog(user) {
1386
+ this.modal.openModal(UserLinkedGroups, 'dialog', {
1387
+ header: this.translocoService.translate('users-groups.summary'),
1388
+ styleClass: '!w-[50rem]',
1389
+ dismissableMask: true,
1390
+ dismissible: true,
1391
+ inputValues: {
1392
+ user,
1393
+ },
1394
+ });
1395
+ }
1070
1396
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: Users, deps: [], target: i0.ɵɵFactoryTarget.Component });
1071
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: Users, isStandalone: true, selector: "mt-users", viewQueries: [{ propertyName: "typeCol", first: true, predicate: ["typeCol"], descendants: true, isSignal: true }, { propertyName: "userCol", first: true, predicate: ["userCol"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'users-groups'\">\r\n <div class=\"mt-5\">\r\n <ng-template #typeCol let-row>\r\n @if (row.isExternal) {\r\n <span\r\n class=\"inline-flex items-center gap-1 rounded-md bg-slate-50 dark:bg-slate-900/30 p-2\"\r\n >\r\n <mt-icon\r\n icon=\"general.link-external-02\"\r\n class=\"text-slate-600 dark:text-slate-300 text-sm\"\r\n />\r\n <span class=\"text-slate-700 dark:text-slate-200 text-xs font-medium\">\r\n {{ t(\"external\") }}\r\n </span>\r\n </span>\r\n } @else {\r\n <span\r\n class=\"inline-flex items-center gap-1 rounded-md bg-emerald-50 dark:bg-emerald-900/30 p-2\"\r\n >\r\n <mt-icon\r\n icon=\"user.users-01\"\r\n class=\"text-emerald-600 dark:text-emerald-300 text-sm\"\r\n />\r\n <span\r\n class=\"text-emerald-700 dark:text-emerald-200 text-xs font-medium\"\r\n >\r\n {{ t(\"internal\") }}\r\n </span>\r\n </span>\r\n }\r\n </ng-template>\r\n <ng-template #userCol let-row>\r\n <div class=\"flex items-center gap-2\">\r\n <mt-avatar [icon]=\"'custom.user-pp'\"> </mt-avatar> {{ row.displayName }}\r\n </div>\r\n </ng-template>\r\n\r\n <mt-table\r\n [tabs]=\"tabs()\"\r\n [(activeTab)]=\"activeTab\"\r\n [data]=\"users()\"\r\n [columns]=\"tableColumns()\"\r\n [actions]=\"tableActions()\"\r\n [rowActions]=\"rowActions()\"\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n [loading]=\"loading()\"\r\n storageKey=\"users-groups-users-table\"\r\n >\r\n </mt-table>\r\n </div>\r\n</ng-container>\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "exportable", "printable", "groupable", "cellClickFilter", "freezeActions", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange"] }, { kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }] });
1397
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: Users, isStandalone: true, selector: "mt-users", viewQueries: [{ propertyName: "typeCol", first: true, predicate: ["typeCol"], descendants: true, isSignal: true }, { propertyName: "userCol", first: true, predicate: ["userCol"], descendants: true, isSignal: true }, { propertyName: "applicationLinkCol", first: true, predicate: ["applicationLinkCol"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'users-groups'\">\n <div class=\"mt-5\">\n <ng-template #typeCol let-row>\n @if (row.isExternal) {\n <span\n class=\"inline-flex items-center gap-1 rounded-md bg-slate-50 dark:bg-slate-900/30 p-2\"\n >\n <mt-icon\n icon=\"general.link-external-02\"\n class=\"text-slate-600 dark:text-slate-300 text-sm\"\n />\n <span class=\"text-slate-700 dark:text-slate-200 text-xs font-medium\">\n {{ t(\"external\") }}\n </span>\n </span>\n } @else {\n <span\n class=\"inline-flex items-center gap-1 rounded-md bg-emerald-50 dark:bg-emerald-900/30 p-2\"\n >\n <mt-icon\n icon=\"user.users-01\"\n class=\"text-emerald-600 dark:text-emerald-300 text-sm\"\n />\n <span\n class=\"text-emerald-700 dark:text-emerald-200 text-xs font-medium\"\n >\n {{ t(\"internal\") }}\n </span>\n </span>\n }\n </ng-template>\n <ng-template #userCol let-row>\n <div class=\"flex items-center gap-2\">\n <mt-avatar [icon]=\"'custom.user-pp'\"> </mt-avatar> {{ row.displayName }}\n </div>\n </ng-template>\n <ng-template #applicationLinkCol let-row>\n @if (isLinkedToCurrentApplication(row)) {\n <span\n class=\"inline-flex items-center gap-1 rounded-md bg-emerald-50 dark:bg-emerald-900/30 p-2\"\n >\n <mt-icon\n icon=\"user.user-check-01\"\n class=\"text-emerald-600 dark:text-emerald-300 text-sm\"\n />\n <span\n class=\"text-emerald-700 dark:text-emerald-200 text-xs font-medium\"\n >\n {{ t(\"linked\") }}\n </span>\n </span>\n } @else {\n <span\n class=\"inline-flex items-center gap-1 rounded-md bg-amber-50 dark:bg-amber-900/30 p-2\"\n >\n <mt-icon\n icon=\"general.link-broken-01\"\n class=\"text-amber-600 dark:text-amber-300 text-sm\"\n />\n <span class=\"text-amber-700 dark:text-amber-200 text-xs font-medium\">\n {{ t(\"not-linked\") }}\n </span>\n </span>\n }\n </ng-template>\n\n <mt-table\n [tabs]=\"tabs()\"\n [(activeTab)]=\"activeTab\"\n [data]=\"users()\"\n [columns]=\"tableColumns()\"\n [actions]=\"tableActions()\"\n [rowActions]=\"rowActions()\"\n [generalSearch]=\"true\"\n [showFilters]=\"true\"\n [loading]=\"loading()\"\n storageKey=\"users-groups-users-table\"\n >\n </mt-table>\n </div>\n</ng-container>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "exportable", "printable", "groupable", "cellClickFilter", "freezeActions", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange"] }, { kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }] });
1072
1398
  }
1073
1399
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: Users, decorators: [{
1074
1400
  type: Component,
@@ -1079,8 +1405,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
1079
1405
  Avatar,
1080
1406
  Icon,
1081
1407
  TranslocoDirective,
1082
- ], template: "<ng-container *transloco=\"let t; prefix: 'users-groups'\">\r\n <div class=\"mt-5\">\r\n <ng-template #typeCol let-row>\r\n @if (row.isExternal) {\r\n <span\r\n class=\"inline-flex items-center gap-1 rounded-md bg-slate-50 dark:bg-slate-900/30 p-2\"\r\n >\r\n <mt-icon\r\n icon=\"general.link-external-02\"\r\n class=\"text-slate-600 dark:text-slate-300 text-sm\"\r\n />\r\n <span class=\"text-slate-700 dark:text-slate-200 text-xs font-medium\">\r\n {{ t(\"external\") }}\r\n </span>\r\n </span>\r\n } @else {\r\n <span\r\n class=\"inline-flex items-center gap-1 rounded-md bg-emerald-50 dark:bg-emerald-900/30 p-2\"\r\n >\r\n <mt-icon\r\n icon=\"user.users-01\"\r\n class=\"text-emerald-600 dark:text-emerald-300 text-sm\"\r\n />\r\n <span\r\n class=\"text-emerald-700 dark:text-emerald-200 text-xs font-medium\"\r\n >\r\n {{ t(\"internal\") }}\r\n </span>\r\n </span>\r\n }\r\n </ng-template>\r\n <ng-template #userCol let-row>\r\n <div class=\"flex items-center gap-2\">\r\n <mt-avatar [icon]=\"'custom.user-pp'\"> </mt-avatar> {{ row.displayName }}\r\n </div>\r\n </ng-template>\r\n\r\n <mt-table\r\n [tabs]=\"tabs()\"\r\n [(activeTab)]=\"activeTab\"\r\n [data]=\"users()\"\r\n [columns]=\"tableColumns()\"\r\n [actions]=\"tableActions()\"\r\n [rowActions]=\"rowActions()\"\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n [loading]=\"loading()\"\r\n storageKey=\"users-groups-users-table\"\r\n >\r\n </mt-table>\r\n </div>\r\n</ng-container>\r\n" }]
1083
- }], propDecorators: { typeCol: [{ type: i0.ViewChild, args: ['typeCol', { isSignal: true }] }], userCol: [{ type: i0.ViewChild, args: ['userCol', { isSignal: true }] }] } });
1408
+ ], template: "<ng-container *transloco=\"let t; prefix: 'users-groups'\">\n <div class=\"mt-5\">\n <ng-template #typeCol let-row>\n @if (row.isExternal) {\n <span\n class=\"inline-flex items-center gap-1 rounded-md bg-slate-50 dark:bg-slate-900/30 p-2\"\n >\n <mt-icon\n icon=\"general.link-external-02\"\n class=\"text-slate-600 dark:text-slate-300 text-sm\"\n />\n <span class=\"text-slate-700 dark:text-slate-200 text-xs font-medium\">\n {{ t(\"external\") }}\n </span>\n </span>\n } @else {\n <span\n class=\"inline-flex items-center gap-1 rounded-md bg-emerald-50 dark:bg-emerald-900/30 p-2\"\n >\n <mt-icon\n icon=\"user.users-01\"\n class=\"text-emerald-600 dark:text-emerald-300 text-sm\"\n />\n <span\n class=\"text-emerald-700 dark:text-emerald-200 text-xs font-medium\"\n >\n {{ t(\"internal\") }}\n </span>\n </span>\n }\n </ng-template>\n <ng-template #userCol let-row>\n <div class=\"flex items-center gap-2\">\n <mt-avatar [icon]=\"'custom.user-pp'\"> </mt-avatar> {{ row.displayName }}\n </div>\n </ng-template>\n <ng-template #applicationLinkCol let-row>\n @if (isLinkedToCurrentApplication(row)) {\n <span\n class=\"inline-flex items-center gap-1 rounded-md bg-emerald-50 dark:bg-emerald-900/30 p-2\"\n >\n <mt-icon\n icon=\"user.user-check-01\"\n class=\"text-emerald-600 dark:text-emerald-300 text-sm\"\n />\n <span\n class=\"text-emerald-700 dark:text-emerald-200 text-xs font-medium\"\n >\n {{ t(\"linked\") }}\n </span>\n </span>\n } @else {\n <span\n class=\"inline-flex items-center gap-1 rounded-md bg-amber-50 dark:bg-amber-900/30 p-2\"\n >\n <mt-icon\n icon=\"general.link-broken-01\"\n class=\"text-amber-600 dark:text-amber-300 text-sm\"\n />\n <span class=\"text-amber-700 dark:text-amber-200 text-xs font-medium\">\n {{ t(\"not-linked\") }}\n </span>\n </span>\n }\n </ng-template>\n\n <mt-table\n [tabs]=\"tabs()\"\n [(activeTab)]=\"activeTab\"\n [data]=\"users()\"\n [columns]=\"tableColumns()\"\n [actions]=\"tableActions()\"\n [rowActions]=\"rowActions()\"\n [generalSearch]=\"true\"\n [showFilters]=\"true\"\n [loading]=\"loading()\"\n storageKey=\"users-groups-users-table\"\n >\n </mt-table>\n </div>\n</ng-container>\n" }]
1409
+ }], propDecorators: { typeCol: [{ type: i0.ViewChild, args: ['typeCol', { isSignal: true }] }], userCol: [{ type: i0.ViewChild, args: ['userCol', { isSignal: true }] }], applicationLinkCol: [{ type: i0.ViewChild, args: ['applicationLinkCol', { isSignal: true }] }] } });
1084
1410
 
1085
1411
  class GroupForm {
1086
1412
  modal = inject(ModalService);
@@ -1361,5 +1687,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
1361
1687
  * Generated bundle index. Do not edit.
1362
1688
  */
1363
1689
 
1364
- export { AddUserToGroup, ClearSelectedGroup, CreateGroup, CreateUser, DeleteGroup, DeleteUser, DeleteUserFromGroup, GetGroup, GetGroups, GetUser, GetUsers, GroupForm, Groups, ResetUserPassword, UpdateGroup, UpdateUser, UserForm, Users, UsersGroups, UsersGroupsActionKey, UsersGroupsFacade, UsersGroupsState };
1690
+ export { AddUserToGroup, ClearSelectedGroup, CreateGroup, CreateUser, DeleteGroup, DeleteUser, DeleteUserFromGroup, GetGroup, GetGroups, GetUser, GetUserSummary, GetUsers, GroupForm, Groups, LinkUserToApplication, ResetUserPassword, UpdateGroup, UpdateUser, UserForm, Users, UsersGroups, UsersGroupsActionKey, UsersGroupsFacade, UsersGroupsState };
1365
1691
  //# sourceMappingURL=masterteam-users-groups.mjs.map