@memberjunction/ng-explorer-settings 6.2.0-edge.0 → 6.2.0-edge.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/account-info/account-info.component.d.ts +1 -1
- package/dist/lib/account-info/account-info.component.js +2 -2
- package/dist/lib/account-info/account-info.component.js.map +1 -1
- package/dist/lib/application-management/application-dialog/application-dialog.component.d.ts +92 -10
- package/dist/lib/application-management/application-dialog/application-dialog.component.d.ts.map +1 -1
- package/dist/lib/application-management/application-dialog/application-dialog.component.js +222 -76
- package/dist/lib/application-management/application-dialog/application-dialog.component.js.map +1 -1
- package/dist/lib/application-management/application-management.component.d.ts +92 -10
- package/dist/lib/application-management/application-management.component.d.ts.map +1 -1
- package/dist/lib/application-management/application-management.component.js +229 -65
- package/dist/lib/application-management/application-management.component.js.map +1 -1
- package/dist/lib/application-settings/application-settings.component.d.ts +4 -4
- package/dist/lib/application-settings/application-settings.component.js +10 -10
- package/dist/lib/application-settings/application-settings.component.js.map +1 -1
- package/dist/lib/entity-permissions/entity-permissions.component.d.ts +88 -10
- package/dist/lib/entity-permissions/entity-permissions.component.d.ts.map +1 -1
- package/dist/lib/entity-permissions/entity-permissions.component.js +219 -63
- package/dist/lib/entity-permissions/entity-permissions.component.js.map +1 -1
- package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.d.ts +44 -6
- package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.d.ts.map +1 -1
- package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.js +117 -43
- package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.js.map +1 -1
- package/dist/lib/notification-preferences/notification-preferences.component.d.ts +25 -3
- package/dist/lib/notification-preferences/notification-preferences.component.d.ts.map +1 -1
- package/dist/lib/notification-preferences/notification-preferences.component.js +62 -18
- package/dist/lib/notification-preferences/notification-preferences.component.js.map +1 -1
- package/dist/lib/role-management/role-dialog/role-dialog.component.d.ts +32 -4
- package/dist/lib/role-management/role-dialog/role-dialog.component.d.ts.map +1 -1
- package/dist/lib/role-management/role-dialog/role-dialog.component.js +84 -30
- package/dist/lib/role-management/role-dialog/role-dialog.component.js.map +1 -1
- package/dist/lib/role-management/role-management-agent-context.d.ts +12 -4
- package/dist/lib/role-management/role-management-agent-context.d.ts.map +1 -1
- package/dist/lib/role-management/role-management-agent-context.js +26 -10
- package/dist/lib/role-management/role-management-agent-context.js.map +1 -1
- package/dist/lib/role-management/role-management.component.d.ts +95 -11
- package/dist/lib/role-management/role-management.component.d.ts.map +1 -1
- package/dist/lib/role-management/role-management.component.js +259 -91
- package/dist/lib/role-management/role-management.component.js.map +1 -1
- package/dist/lib/settings/settings.component.d.ts +87 -11
- package/dist/lib/settings/settings.component.d.ts.map +1 -1
- package/dist/lib/settings/settings.component.js +211 -65
- package/dist/lib/settings/settings.component.js.map +1 -1
- package/dist/lib/shared/settings-card.component.d.ts +24 -4
- package/dist/lib/shared/settings-card.component.d.ts.map +1 -1
- package/dist/lib/shared/settings-card.component.js +46 -6
- package/dist/lib/shared/settings-card.component.js.map +1 -1
- package/dist/lib/sql-logging/sql-logging.component.d.ts +172 -18
- package/dist/lib/sql-logging/sql-logging.component.d.ts.map +1 -1
- package/dist/lib/sql-logging/sql-logging.component.js +320 -108
- package/dist/lib/sql-logging/sql-logging.component.js.map +1 -1
- package/dist/lib/user-app-config/user-app-config-content.component.js +3 -3
- package/dist/lib/user-app-config/user-app-config-content.component.js.map +1 -1
- package/dist/lib/user-app-config/user-app-config-reorder.d.ts +4 -0
- package/dist/lib/user-app-config/user-app-config-reorder.d.ts.map +1 -1
- package/dist/lib/user-app-config/user-app-config-reorder.js +11 -3
- package/dist/lib/user-app-config/user-app-config-reorder.js.map +1 -1
- package/dist/lib/user-management/transaction-group-refusals.d.ts +4 -2
- package/dist/lib/user-management/transaction-group-refusals.d.ts.map +1 -1
- package/dist/lib/user-management/transaction-group-refusals.js +7 -3
- package/dist/lib/user-management/transaction-group-refusals.js.map +1 -1
- package/dist/lib/user-management/user-dialog/user-dialog.component.d.ts +44 -6
- package/dist/lib/user-management/user-dialog/user-dialog.component.d.ts.map +1 -1
- package/dist/lib/user-management/user-dialog/user-dialog.component.js +121 -47
- package/dist/lib/user-management/user-dialog/user-dialog.component.js.map +1 -1
- package/dist/lib/user-management/user-management-agent-context.d.ts +10 -0
- package/dist/lib/user-management/user-management-agent-context.d.ts.map +1 -1
- package/dist/lib/user-management/user-management-agent-context.js +26 -6
- package/dist/lib/user-management/user-management-agent-context.js.map +1 -1
- package/dist/lib/user-management/user-management.component.d.ts +174 -16
- package/dist/lib/user-management/user-management.component.d.ts.map +1 -1
- package/dist/lib/user-management/user-management.component.js +480 -171
- package/dist/lib/user-management/user-management.component.js.map +1 -1
- package/dist/lib/user-profile-settings/user-profile-settings.component.d.ts +97 -15
- package/dist/lib/user-profile-settings/user-profile-settings.component.d.ts.map +1 -1
- package/dist/lib/user-profile-settings/user-profile-settings.component.js +280 -116
- package/dist/lib/user-profile-settings/user-profile-settings.component.js.map +1 -1
- package/package.json +20 -20
|
@@ -9,9 +9,9 @@ import { Subject, BehaviorSubject } from 'rxjs';
|
|
|
9
9
|
import { takeUntil, debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
|
10
10
|
import { RunView, CompositeKey } from '@memberjunction/core';
|
|
11
11
|
import { BaseDashboard } from '@memberjunction/ng-shared';
|
|
12
|
-
import { RegisterClass, UUIDsEqual } from '@memberjunction/global';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
12
|
+
import { NormalizeUUID, RegisterClass, UUIDsEqual } from '@memberjunction/global';
|
|
13
|
+
import { ServerRefusalReasons } from './transaction-group-refusals';
|
|
14
|
+
import { BuildUserManagementAgentContext, IsValidUserStatusFilter, ResolveUserByIDOrName, ResolveRoleByIDOrName, } from './user-management-agent-context';
|
|
15
15
|
import * as i0 from "@angular/core";
|
|
16
16
|
import * as i1 from "@angular/forms";
|
|
17
17
|
import * as i2 from "@memberjunction/ng-shared-generic";
|
|
@@ -514,49 +514,177 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
514
514
|
cdr;
|
|
515
515
|
ngZone;
|
|
516
516
|
// State management
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
517
|
+
Users = [];
|
|
518
|
+
/** @deprecated Use {@link Users}. */
|
|
519
|
+
get users() {
|
|
520
|
+
return this.Users;
|
|
521
|
+
}
|
|
522
|
+
/** @deprecated Use {@link Users}. */
|
|
523
|
+
set users(value) {
|
|
524
|
+
this.Users = value;
|
|
525
|
+
}
|
|
526
|
+
FilteredUsers = [];
|
|
527
|
+
/** @deprecated Use {@link FilteredUsers}. */
|
|
528
|
+
get filteredUsers() {
|
|
529
|
+
return this.FilteredUsers;
|
|
530
|
+
}
|
|
531
|
+
/** @deprecated Use {@link FilteredUsers}. */
|
|
532
|
+
set filteredUsers(value) {
|
|
533
|
+
this.FilteredUsers = value;
|
|
534
|
+
}
|
|
535
|
+
Roles = [];
|
|
536
|
+
/** @deprecated Use {@link Roles}. */
|
|
537
|
+
get roles() {
|
|
538
|
+
return this.Roles;
|
|
539
|
+
}
|
|
540
|
+
/** @deprecated Use {@link Roles}. */
|
|
541
|
+
set roles(value) {
|
|
542
|
+
this.Roles = value;
|
|
543
|
+
}
|
|
544
|
+
SelectedUser = null;
|
|
545
|
+
/** @deprecated Use {@link SelectedUser}. */
|
|
546
|
+
get selectedUser() {
|
|
547
|
+
return this.SelectedUser;
|
|
548
|
+
}
|
|
549
|
+
/** @deprecated Use {@link SelectedUser}. */
|
|
550
|
+
set selectedUser(value) {
|
|
551
|
+
this.SelectedUser = value;
|
|
552
|
+
}
|
|
521
553
|
isLoading = false;
|
|
522
554
|
error = null;
|
|
523
555
|
// Selection state for bulk actions
|
|
524
|
-
|
|
556
|
+
SelectedUserIds = new Set();
|
|
557
|
+
/** @deprecated Use {@link SelectedUserIds}. */
|
|
558
|
+
get selectedUserIds() {
|
|
559
|
+
return this.SelectedUserIds;
|
|
560
|
+
}
|
|
561
|
+
/** @deprecated Use {@link SelectedUserIds}. */
|
|
562
|
+
set selectedUserIds(value) {
|
|
563
|
+
this.SelectedUserIds = value;
|
|
564
|
+
}
|
|
525
565
|
// Dialog state
|
|
526
|
-
|
|
527
|
-
|
|
566
|
+
ShowUserDialog = false;
|
|
567
|
+
/** @deprecated Use {@link ShowUserDialog}. */
|
|
568
|
+
get showUserDialog() {
|
|
569
|
+
return this.ShowUserDialog;
|
|
570
|
+
}
|
|
571
|
+
/** @deprecated Use {@link ShowUserDialog}. */
|
|
572
|
+
set showUserDialog(value) {
|
|
573
|
+
this.ShowUserDialog = value;
|
|
574
|
+
}
|
|
575
|
+
UserDialogData = null;
|
|
576
|
+
/** @deprecated Use {@link UserDialogData}. */
|
|
577
|
+
get userDialogData() {
|
|
578
|
+
return this.UserDialogData;
|
|
579
|
+
}
|
|
580
|
+
/** @deprecated Use {@link UserDialogData}. */
|
|
581
|
+
set userDialogData(value) {
|
|
582
|
+
this.UserDialogData = value;
|
|
583
|
+
}
|
|
528
584
|
// Bulk action dialog state
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
585
|
+
ShowBulkActionConfirm = false;
|
|
586
|
+
/** @deprecated Use {@link ShowBulkActionConfirm}. */
|
|
587
|
+
get showBulkActionConfirm() {
|
|
588
|
+
return this.ShowBulkActionConfirm;
|
|
589
|
+
}
|
|
590
|
+
/** @deprecated Use {@link ShowBulkActionConfirm}. */
|
|
591
|
+
set showBulkActionConfirm(value) {
|
|
592
|
+
this.ShowBulkActionConfirm = value;
|
|
593
|
+
}
|
|
594
|
+
BulkActionType = null;
|
|
595
|
+
/** @deprecated Use {@link BulkActionType}. */
|
|
596
|
+
get bulkActionType() {
|
|
597
|
+
return this.BulkActionType;
|
|
598
|
+
}
|
|
599
|
+
/** @deprecated Use {@link BulkActionType}. */
|
|
600
|
+
set bulkActionType(value) {
|
|
601
|
+
this.BulkActionType = value;
|
|
602
|
+
}
|
|
603
|
+
ShowBulkRoleAssign = false;
|
|
604
|
+
/** @deprecated Use {@link ShowBulkRoleAssign}. */
|
|
605
|
+
get showBulkRoleAssign() {
|
|
606
|
+
return this.ShowBulkRoleAssign;
|
|
607
|
+
}
|
|
608
|
+
/** @deprecated Use {@link ShowBulkRoleAssign}. */
|
|
609
|
+
set showBulkRoleAssign(value) {
|
|
610
|
+
this.ShowBulkRoleAssign = value;
|
|
611
|
+
}
|
|
612
|
+
BulkRoleId = '';
|
|
613
|
+
/** @deprecated Use {@link BulkRoleId}. */
|
|
614
|
+
get bulkRoleId() {
|
|
615
|
+
return this.BulkRoleId;
|
|
616
|
+
}
|
|
617
|
+
/** @deprecated Use {@link BulkRoleId}. */
|
|
618
|
+
set bulkRoleId(value) {
|
|
619
|
+
this.BulkRoleId = value;
|
|
620
|
+
}
|
|
533
621
|
// Stats
|
|
534
|
-
|
|
622
|
+
Stats = {
|
|
535
623
|
totalUsers: 0,
|
|
536
624
|
activeUsers: 0,
|
|
537
625
|
inactiveUsers: 0,
|
|
538
626
|
adminUsers: 0 // This will be based on roles, not Type
|
|
539
627
|
};
|
|
628
|
+
/** @deprecated Use {@link Stats}. */
|
|
629
|
+
get stats() {
|
|
630
|
+
return this.Stats;
|
|
631
|
+
}
|
|
632
|
+
/** @deprecated Use {@link Stats}. */
|
|
633
|
+
set stats(value) {
|
|
634
|
+
this.Stats = value;
|
|
635
|
+
}
|
|
540
636
|
// Filters
|
|
541
|
-
|
|
637
|
+
Filters$ = new BehaviorSubject({
|
|
542
638
|
status: 'all',
|
|
543
639
|
role: '',
|
|
544
640
|
search: ''
|
|
545
641
|
});
|
|
642
|
+
/** @deprecated Use {@link Filters$}. */
|
|
643
|
+
get filters$() {
|
|
644
|
+
return this.Filters$;
|
|
645
|
+
}
|
|
646
|
+
/** @deprecated Use {@link Filters$}. */
|
|
647
|
+
set filters$(value) {
|
|
648
|
+
this.Filters$ = value;
|
|
649
|
+
}
|
|
546
650
|
// UI State
|
|
547
651
|
showCreateDialog = false;
|
|
548
|
-
|
|
549
|
-
|
|
652
|
+
ShowEditDialog = false;
|
|
653
|
+
/** @deprecated Use {@link ShowEditDialog}. */
|
|
654
|
+
get showEditDialog() {
|
|
655
|
+
return this.ShowEditDialog;
|
|
656
|
+
}
|
|
657
|
+
/** @deprecated Use {@link ShowEditDialog}. */
|
|
658
|
+
set showEditDialog(value) {
|
|
659
|
+
this.ShowEditDialog = value;
|
|
660
|
+
}
|
|
661
|
+
ShowDeleteConfirm = false;
|
|
662
|
+
/** @deprecated Use {@link ShowDeleteConfirm}. */
|
|
663
|
+
get showDeleteConfirm() {
|
|
664
|
+
return this.ShowDeleteConfirm;
|
|
665
|
+
}
|
|
666
|
+
/** @deprecated Use {@link ShowDeleteConfirm}. */
|
|
667
|
+
set showDeleteConfirm(value) {
|
|
668
|
+
this.ShowDeleteConfirm = value;
|
|
669
|
+
}
|
|
550
670
|
// Mobile expansion state
|
|
551
671
|
expandedUserIds = new Set();
|
|
552
672
|
// User-Role mapping
|
|
553
673
|
userRoleMap = new Map(); // userId -> roleIds[]
|
|
554
674
|
// Grid configuration
|
|
555
|
-
|
|
675
|
+
GridConfig = {
|
|
556
676
|
pageSize: 20,
|
|
557
677
|
sortField: 'Name',
|
|
558
678
|
sortDirection: 'asc'
|
|
559
679
|
};
|
|
680
|
+
/** @deprecated Use {@link GridConfig}. */
|
|
681
|
+
get gridConfig() {
|
|
682
|
+
return this.GridConfig;
|
|
683
|
+
}
|
|
684
|
+
/** @deprecated Use {@link GridConfig}. */
|
|
685
|
+
set gridConfig(value) {
|
|
686
|
+
this.GridConfig = value;
|
|
687
|
+
}
|
|
560
688
|
destroy$ = new Subject();
|
|
561
689
|
get metadata() { return this.ProviderToUse; }
|
|
562
690
|
constructor(cdr, ngZone) {
|
|
@@ -572,7 +700,7 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
572
700
|
this.registerAgentClientTools();
|
|
573
701
|
}
|
|
574
702
|
loadData() {
|
|
575
|
-
this.
|
|
703
|
+
this.LoadInitialData();
|
|
576
704
|
}
|
|
577
705
|
// ================================================================
|
|
578
706
|
// AI Agent Context & Client Tools
|
|
@@ -599,25 +727,25 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
599
727
|
* {@link buildUserManagementAgentContext} helper (unit-tested in isolation).
|
|
600
728
|
*/
|
|
601
729
|
publishAgentContext() {
|
|
602
|
-
const filters = this.
|
|
603
|
-
const roleFilter = filters.role ? this.
|
|
730
|
+
const filters = this.Filters$.value;
|
|
731
|
+
const roleFilter = filters.role ? this.Roles.find(r => UUIDsEqual(r.ID, filters.role)) : null;
|
|
604
732
|
const input = {
|
|
605
|
-
TotalUserCount: this.
|
|
606
|
-
FilteredUserCount: this.
|
|
607
|
-
ActiveUserCount: this.
|
|
608
|
-
InactiveUserCount: this.
|
|
733
|
+
TotalUserCount: this.Users.length,
|
|
734
|
+
FilteredUserCount: this.FilteredUsers.length,
|
|
735
|
+
ActiveUserCount: this.Stats.activeUsers,
|
|
736
|
+
InactiveUserCount: this.Stats.inactiveUsers,
|
|
609
737
|
StatusFilter: filters.status,
|
|
610
738
|
RoleFilterId: filters.role || null,
|
|
611
739
|
RoleFilterName: roleFilter?.Name ?? null,
|
|
612
740
|
SearchText: filters.search,
|
|
613
|
-
SelectedUserId: this.
|
|
614
|
-
SelectedUserName: this.
|
|
615
|
-
VisibleUserNames: this.
|
|
616
|
-
VisibleUserEmails: this.
|
|
617
|
-
AvailableRoleNames: this.
|
|
741
|
+
SelectedUserId: this.SelectedUser?.ID ?? null,
|
|
742
|
+
SelectedUserName: this.SelectedUser?.Name ?? null,
|
|
743
|
+
VisibleUserNames: this.FilteredUsers.map(u => u.Name ?? '').filter(n => n !== ''),
|
|
744
|
+
VisibleUserEmails: this.FilteredUsers.map(u => u.Email ?? '').filter(e => e !== ''),
|
|
745
|
+
AvailableRoleNames: this.Roles.map(r => r.Name ?? '').filter(n => n !== ''),
|
|
618
746
|
VisibleColumns: ['Name', 'Email', 'Type', 'Status', 'Roles'],
|
|
619
747
|
};
|
|
620
|
-
this.navigationService.SetAgentContext(this,
|
|
748
|
+
this.navigationService.SetAgentContext(this, BuildUserManagementAgentContext(input));
|
|
621
749
|
}
|
|
622
750
|
/**
|
|
623
751
|
* Register the read-only / navigational client tools the agent may invoke.
|
|
@@ -684,10 +812,10 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
684
812
|
}
|
|
685
813
|
handleSwitchStatusFilterTool(params) {
|
|
686
814
|
const status = params?.['status'];
|
|
687
|
-
if (!
|
|
815
|
+
if (!IsValidUserStatusFilter(status)) {
|
|
688
816
|
return { Success: false, ErrorMessage: `Invalid status "${String(status)}". Expected one of: all, active, inactive.` };
|
|
689
817
|
}
|
|
690
|
-
this.
|
|
818
|
+
this.OnStatusFilterChange(status);
|
|
691
819
|
this.publishAgentContext();
|
|
692
820
|
return { Success: true };
|
|
693
821
|
}
|
|
@@ -695,15 +823,15 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
695
823
|
const raw = String(params?.['role'] ?? '');
|
|
696
824
|
if (!raw.trim()) {
|
|
697
825
|
// Empty string explicitly clears the role filter.
|
|
698
|
-
this.
|
|
826
|
+
this.UpdateFilter({ role: '' });
|
|
699
827
|
this.publishAgentContext();
|
|
700
828
|
return { Success: true };
|
|
701
829
|
}
|
|
702
|
-
const resolved =
|
|
830
|
+
const resolved = ResolveRoleByIDOrName(raw, this.Roles.map(r => ({ ID: r.ID, Name: r.Name ?? '' })));
|
|
703
831
|
if (!resolved.ok) {
|
|
704
832
|
return { Success: false, ErrorMessage: resolved.error };
|
|
705
833
|
}
|
|
706
|
-
this.
|
|
834
|
+
this.UpdateFilter({ role: resolved.match.ID });
|
|
707
835
|
this.publishAgentContext();
|
|
708
836
|
return { Success: true };
|
|
709
837
|
}
|
|
@@ -712,20 +840,20 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
712
840
|
if (typeof searchText !== 'string') {
|
|
713
841
|
return { Success: false, ErrorMessage: 'searchText must be a string.' };
|
|
714
842
|
}
|
|
715
|
-
this.
|
|
843
|
+
this.UpdateFilter({ search: searchText });
|
|
716
844
|
this.publishAgentContext();
|
|
717
845
|
return { Success: true };
|
|
718
846
|
}
|
|
719
847
|
handleSelectUserTool(params) {
|
|
720
848
|
const raw = String(params?.['user'] ?? '');
|
|
721
|
-
const resolved =
|
|
849
|
+
const resolved = ResolveUserByIDOrName(raw, this.Users.map(u => ({
|
|
722
850
|
ID: u.ID, Name: u.Name ?? '', Email: u.Email, FirstName: u.FirstName, LastName: u.LastName,
|
|
723
851
|
})));
|
|
724
852
|
if (!resolved.ok) {
|
|
725
853
|
return { Success: false, ErrorMessage: resolved.error };
|
|
726
854
|
}
|
|
727
|
-
const match = this.
|
|
728
|
-
this.
|
|
855
|
+
const match = this.Users.find(u => UUIDsEqual(u.ID, resolved.match.ID)) ?? null;
|
|
856
|
+
this.SelectedUser = match;
|
|
729
857
|
this.cdr.markForCheck();
|
|
730
858
|
this.publishAgentContext();
|
|
731
859
|
return { Success: true };
|
|
@@ -746,7 +874,7 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
746
874
|
}
|
|
747
875
|
handleNavigateToUserRecordTool(params) {
|
|
748
876
|
const raw = String(params?.['user'] ?? '');
|
|
749
|
-
const resolved =
|
|
877
|
+
const resolved = ResolveUserByIDOrName(raw, this.Users.map(u => ({
|
|
750
878
|
ID: u.ID, Name: u.Name ?? '', Email: u.Email, FirstName: u.FirstName, LastName: u.LastName,
|
|
751
879
|
})));
|
|
752
880
|
if (!resolved.ok) {
|
|
@@ -759,7 +887,7 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
759
887
|
applySortToVisibleUsers() {
|
|
760
888
|
const dir = this.agentSortDirection === 'desc' ? -1 : 1;
|
|
761
889
|
const field = this.agentSortField;
|
|
762
|
-
this.
|
|
890
|
+
this.FilteredUsers = [...this.FilteredUsers].sort((a, b) => {
|
|
763
891
|
const av = this.sortKeyForUser(a, field);
|
|
764
892
|
const bv = this.sortKeyForUser(b, field);
|
|
765
893
|
return av.localeCompare(bv) * dir;
|
|
@@ -775,13 +903,13 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
775
903
|
}
|
|
776
904
|
}
|
|
777
905
|
handleClearFiltersTool() {
|
|
778
|
-
this.
|
|
906
|
+
this.ResetAllFiltersAndSearch();
|
|
779
907
|
this.publishAgentContext();
|
|
780
908
|
return { Success: true };
|
|
781
909
|
}
|
|
782
910
|
async handleRefreshTool() {
|
|
783
911
|
try {
|
|
784
|
-
await this.
|
|
912
|
+
await this.LoadInitialData();
|
|
785
913
|
this.publishAgentContext();
|
|
786
914
|
return { Success: true };
|
|
787
915
|
}
|
|
@@ -790,10 +918,10 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
790
918
|
}
|
|
791
919
|
}
|
|
792
920
|
handleExportTool() {
|
|
793
|
-
if (this.
|
|
921
|
+
if (this.FilteredUsers.length === 0) {
|
|
794
922
|
return { Success: false, ErrorMessage: 'No users to export.' };
|
|
795
923
|
}
|
|
796
|
-
this.
|
|
924
|
+
this.ExportUsers();
|
|
797
925
|
return { Success: true };
|
|
798
926
|
}
|
|
799
927
|
ngOnDestroy() {
|
|
@@ -801,7 +929,7 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
801
929
|
this.destroy$.complete();
|
|
802
930
|
super.ngOnDestroy();
|
|
803
931
|
}
|
|
804
|
-
async
|
|
932
|
+
async LoadInitialData() {
|
|
805
933
|
try {
|
|
806
934
|
this.isLoading = true;
|
|
807
935
|
this.error = null;
|
|
@@ -811,8 +939,8 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
811
939
|
this.loadRoles(),
|
|
812
940
|
this.loadUserRoles()
|
|
813
941
|
]);
|
|
814
|
-
this.
|
|
815
|
-
this.
|
|
942
|
+
this.Users = users;
|
|
943
|
+
this.Roles = roles;
|
|
816
944
|
// Build user-role mapping
|
|
817
945
|
this.buildUserRoleMapping(userRoles);
|
|
818
946
|
this.calculateStats();
|
|
@@ -830,6 +958,10 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
830
958
|
});
|
|
831
959
|
}
|
|
832
960
|
}
|
|
961
|
+
/** @deprecated Use {@link LoadInitialData}. */
|
|
962
|
+
async loadInitialData() {
|
|
963
|
+
return this.LoadInitialData();
|
|
964
|
+
}
|
|
833
965
|
async loadUsers() {
|
|
834
966
|
const rv = RunView.FromMetadataProvider(this.ProviderToUse);
|
|
835
967
|
const result = await rv.RunView({
|
|
@@ -867,7 +999,7 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
867
999
|
});
|
|
868
1000
|
}
|
|
869
1001
|
setupFilterSubscription() {
|
|
870
|
-
this.
|
|
1002
|
+
this.Filters$
|
|
871
1003
|
.pipe(debounceTime(300), distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)), takeUntil(this.destroy$))
|
|
872
1004
|
.subscribe(() => {
|
|
873
1005
|
this.applyFilters();
|
|
@@ -875,8 +1007,8 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
875
1007
|
});
|
|
876
1008
|
}
|
|
877
1009
|
applyFilters() {
|
|
878
|
-
const filters = this.
|
|
879
|
-
let filtered = [...this.
|
|
1010
|
+
const filters = this.Filters$.value;
|
|
1011
|
+
let filtered = [...this.Users];
|
|
880
1012
|
// Apply status filter
|
|
881
1013
|
if (filters.status !== 'all') {
|
|
882
1014
|
filtered = filtered.filter(user => filters.status === 'active' ? user.IsActive : !user.IsActive);
|
|
@@ -896,25 +1028,29 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
896
1028
|
user.FirstName?.toLowerCase().includes(searchLower) ||
|
|
897
1029
|
user.LastName?.toLowerCase().includes(searchLower));
|
|
898
1030
|
}
|
|
899
|
-
this.
|
|
1031
|
+
this.FilteredUsers = filtered;
|
|
900
1032
|
// Preserve any agent-requested sort across filter changes (read-only display ordering).
|
|
901
1033
|
this.applySortToVisibleUsers();
|
|
902
1034
|
}
|
|
903
1035
|
calculateStats() {
|
|
904
|
-
this.
|
|
905
|
-
totalUsers: this.
|
|
906
|
-
activeUsers: this.
|
|
907
|
-
inactiveUsers: this.
|
|
908
|
-
adminUsers: this.
|
|
1036
|
+
this.Stats = {
|
|
1037
|
+
totalUsers: this.Users.length,
|
|
1038
|
+
activeUsers: this.Users.filter(u => u.IsActive).length,
|
|
1039
|
+
inactiveUsers: this.Users.filter(u => !u.IsActive).length,
|
|
1040
|
+
adminUsers: this.Users.filter(u => u.Type === 'Owner').length // Using Owner as admin type
|
|
909
1041
|
};
|
|
910
1042
|
}
|
|
911
1043
|
// Public methods for template
|
|
1044
|
+
OnStatusFilterChange(status) {
|
|
1045
|
+
this.UpdateFilter({ status });
|
|
1046
|
+
}
|
|
1047
|
+
/** @deprecated Use {@link OnStatusFilterChange}. */
|
|
912
1048
|
onStatusFilterChange(status) {
|
|
913
|
-
this.
|
|
1049
|
+
return this.OnStatusFilterChange(status);
|
|
914
1050
|
}
|
|
915
|
-
|
|
916
|
-
this.
|
|
917
|
-
...this.
|
|
1051
|
+
UpdateFilter(partial) {
|
|
1052
|
+
this.Filters$.next({
|
|
1053
|
+
...this.Filters$.value,
|
|
918
1054
|
...partial
|
|
919
1055
|
});
|
|
920
1056
|
// Discrete changes (chips, popover dropdowns) apply immediately. Text search
|
|
@@ -925,42 +1061,58 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
925
1061
|
this.cdr.markForCheck();
|
|
926
1062
|
}
|
|
927
1063
|
}
|
|
1064
|
+
/** @deprecated Use {@link UpdateFilter}. */
|
|
1065
|
+
updateFilter(partial) {
|
|
1066
|
+
return this.UpdateFilter(partial);
|
|
1067
|
+
}
|
|
1068
|
+
SelectUser(user) {
|
|
1069
|
+
this.SelectedUser = user;
|
|
1070
|
+
this.ShowEditDialog = true;
|
|
1071
|
+
}
|
|
1072
|
+
/** @deprecated Use {@link SelectUser}. */
|
|
928
1073
|
selectUser(user) {
|
|
929
|
-
this.
|
|
930
|
-
this.showEditDialog = true;
|
|
1074
|
+
return this.SelectUser(user);
|
|
931
1075
|
}
|
|
932
1076
|
createNewUser() {
|
|
933
|
-
this.
|
|
1077
|
+
this.UserDialogData = {
|
|
934
1078
|
mode: 'create',
|
|
935
|
-
availableRoles: this.
|
|
1079
|
+
availableRoles: this.Roles
|
|
936
1080
|
};
|
|
937
|
-
this.
|
|
1081
|
+
this.ShowUserDialog = true;
|
|
938
1082
|
}
|
|
939
|
-
|
|
940
|
-
this.
|
|
1083
|
+
EditUser(user) {
|
|
1084
|
+
this.UserDialogData = {
|
|
941
1085
|
user: user,
|
|
942
1086
|
mode: 'edit',
|
|
943
|
-
availableRoles: this.
|
|
1087
|
+
availableRoles: this.Roles
|
|
944
1088
|
};
|
|
945
|
-
this.
|
|
1089
|
+
this.ShowUserDialog = true;
|
|
1090
|
+
}
|
|
1091
|
+
/** @deprecated Use {@link EditUser}. */
|
|
1092
|
+
editUser(user) {
|
|
1093
|
+
return this.EditUser(user);
|
|
1094
|
+
}
|
|
1095
|
+
ConfirmDeleteUser(user) {
|
|
1096
|
+
this.SelectedUser = user;
|
|
1097
|
+
this.ShowDeleteConfirm = true;
|
|
946
1098
|
}
|
|
1099
|
+
/** @deprecated Use {@link ConfirmDeleteUser}. */
|
|
947
1100
|
confirmDeleteUser(user) {
|
|
948
|
-
this.
|
|
949
|
-
this.showDeleteConfirm = true;
|
|
1101
|
+
return this.ConfirmDeleteUser(user);
|
|
950
1102
|
}
|
|
951
|
-
async
|
|
952
|
-
if (!this.
|
|
1103
|
+
async DeleteUser() {
|
|
1104
|
+
if (!this.SelectedUser)
|
|
953
1105
|
return;
|
|
954
1106
|
try {
|
|
955
1107
|
// Load user entity to delete
|
|
956
1108
|
const user = await this.metadata.GetEntityObject('MJ: Users');
|
|
957
|
-
const loadResult = await user.Load(this.
|
|
1109
|
+
const loadResult = await user.Load(this.SelectedUser.ID);
|
|
958
1110
|
if (loadResult) {
|
|
959
1111
|
const deleteResult = await user.Delete();
|
|
960
1112
|
if (deleteResult) {
|
|
961
|
-
this.
|
|
962
|
-
this.
|
|
963
|
-
await this.
|
|
1113
|
+
this.ShowDeleteConfirm = false;
|
|
1114
|
+
this.SelectedUser = null;
|
|
1115
|
+
await this.LoadInitialData();
|
|
964
1116
|
}
|
|
965
1117
|
else {
|
|
966
1118
|
throw new Error(user.LatestResult?.Message || 'Failed to delete user');
|
|
@@ -978,7 +1130,11 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
978
1130
|
});
|
|
979
1131
|
}
|
|
980
1132
|
}
|
|
981
|
-
|
|
1133
|
+
/** @deprecated Use {@link DeleteUser}. */
|
|
1134
|
+
async deleteUser() {
|
|
1135
|
+
return this.DeleteUser();
|
|
1136
|
+
}
|
|
1137
|
+
async ToggleUserStatus(user) {
|
|
982
1138
|
try {
|
|
983
1139
|
user.IsActive = !user.IsActive;
|
|
984
1140
|
// BaseEntity.Save() returns false on a validation failure — it does NOT throw. Discarding
|
|
@@ -1009,8 +1165,12 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1009
1165
|
});
|
|
1010
1166
|
}
|
|
1011
1167
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1168
|
+
/** @deprecated Use {@link ToggleUserStatus}. */
|
|
1169
|
+
async toggleUserStatus(user) {
|
|
1170
|
+
return this.ToggleUserStatus(user);
|
|
1171
|
+
}
|
|
1172
|
+
ExportUsers() {
|
|
1173
|
+
if (this.FilteredUsers.length === 0) {
|
|
1014
1174
|
this.error = 'No users to export';
|
|
1015
1175
|
return;
|
|
1016
1176
|
}
|
|
@@ -1019,7 +1179,7 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1019
1179
|
const headers = ['Name', 'First Name', 'Last Name', 'Email', 'Type', 'Status', 'Created', 'Updated'];
|
|
1020
1180
|
const csvRows = [headers.join(',')];
|
|
1021
1181
|
// Add user data
|
|
1022
|
-
this.
|
|
1182
|
+
this.FilteredUsers.forEach(user => {
|
|
1023
1183
|
const row = [
|
|
1024
1184
|
this.escapeCSV(user.Name || ''),
|
|
1025
1185
|
this.escapeCSV(user.FirstName || ''),
|
|
@@ -1051,22 +1211,38 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1051
1211
|
this.error = 'Failed to export users';
|
|
1052
1212
|
}
|
|
1053
1213
|
}
|
|
1214
|
+
/** @deprecated Use {@link ExportUsers}. */
|
|
1215
|
+
exportUsers() {
|
|
1216
|
+
return this.ExportUsers();
|
|
1217
|
+
}
|
|
1054
1218
|
escapeCSV(value) {
|
|
1055
1219
|
if (value.includes(',') || value.includes('"') || value.includes('\n')) {
|
|
1056
1220
|
return `"${value.replace(/"/g, '""')}"`;
|
|
1057
1221
|
}
|
|
1058
1222
|
return value;
|
|
1059
1223
|
}
|
|
1224
|
+
RefreshData() {
|
|
1225
|
+
this.LoadInitialData();
|
|
1226
|
+
}
|
|
1227
|
+
/** @deprecated Use {@link RefreshData}. */
|
|
1060
1228
|
refreshData() {
|
|
1061
|
-
this.
|
|
1229
|
+
return this.RefreshData();
|
|
1062
1230
|
}
|
|
1063
|
-
|
|
1231
|
+
GetStatusIcon(user) {
|
|
1064
1232
|
return user.IsActive ? 'fa-check-circle' : 'fa-times-circle';
|
|
1065
1233
|
}
|
|
1066
|
-
|
|
1234
|
+
/** @deprecated Use {@link GetStatusIcon}. */
|
|
1235
|
+
getStatusIcon(user) {
|
|
1236
|
+
return this.GetStatusIcon(user);
|
|
1237
|
+
}
|
|
1238
|
+
GetStatusClass(user) {
|
|
1067
1239
|
return user.IsActive ? 'status-active' : 'status-inactive';
|
|
1068
1240
|
}
|
|
1069
|
-
|
|
1241
|
+
/** @deprecated Use {@link GetStatusClass}. */
|
|
1242
|
+
getStatusClass(user) {
|
|
1243
|
+
return this.GetStatusClass(user);
|
|
1244
|
+
}
|
|
1245
|
+
GetUserTypeIcon(user) {
|
|
1070
1246
|
switch (user.Type) {
|
|
1071
1247
|
case 'Owner':
|
|
1072
1248
|
return 'fa-shield-halved';
|
|
@@ -1076,56 +1252,96 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1076
1252
|
return 'fa-user';
|
|
1077
1253
|
}
|
|
1078
1254
|
}
|
|
1079
|
-
|
|
1255
|
+
/** @deprecated Use {@link GetUserTypeIcon}. */
|
|
1256
|
+
getUserTypeIcon(user) {
|
|
1257
|
+
return this.GetUserTypeIcon(user);
|
|
1258
|
+
}
|
|
1259
|
+
GetUserInitials(user) {
|
|
1080
1260
|
const first = user.FirstName?.charAt(0) || '';
|
|
1081
1261
|
const last = user.LastName?.charAt(0) || '';
|
|
1082
1262
|
return (first + last).toUpperCase() || user.Name?.charAt(0).toUpperCase() || 'U';
|
|
1083
1263
|
}
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
this.
|
|
1264
|
+
/** @deprecated Use {@link GetUserInitials}. */
|
|
1265
|
+
getUserInitials(user) {
|
|
1266
|
+
return this.GetUserInitials(user);
|
|
1267
|
+
}
|
|
1268
|
+
OnUserDialogResult(result) {
|
|
1269
|
+
this.ShowUserDialog = false;
|
|
1270
|
+
this.UserDialogData = null;
|
|
1087
1271
|
if (result.action === 'save') {
|
|
1088
1272
|
// Refresh the user list to show changes
|
|
1089
|
-
this.
|
|
1273
|
+
this.LoadInitialData();
|
|
1090
1274
|
}
|
|
1091
1275
|
}
|
|
1276
|
+
/** @deprecated Use {@link OnUserDialogResult}. */
|
|
1277
|
+
onUserDialogResult(result) {
|
|
1278
|
+
return this.OnUserDialogResult(result);
|
|
1279
|
+
}
|
|
1092
1280
|
// Selection methods for bulk actions
|
|
1281
|
+
get IsAllSelected() {
|
|
1282
|
+
return this.FilteredUsers.length > 0 &&
|
|
1283
|
+
this.FilteredUsers.every(user => this.SelectedUserIds.has(user.ID));
|
|
1284
|
+
}
|
|
1285
|
+
/** @deprecated Use {@link IsAllSelected}. */
|
|
1093
1286
|
get isAllSelected() {
|
|
1094
|
-
return this.
|
|
1095
|
-
this.filteredUsers.every(user => this.selectedUserIds.has(user.ID));
|
|
1287
|
+
return this.IsAllSelected;
|
|
1096
1288
|
}
|
|
1289
|
+
get IsIndeterminate() {
|
|
1290
|
+
const selectedCount = this.FilteredUsers.filter(user => this.SelectedUserIds.has(user.ID)).length;
|
|
1291
|
+
return selectedCount > 0 && selectedCount < this.FilteredUsers.length;
|
|
1292
|
+
}
|
|
1293
|
+
/** @deprecated Use {@link IsIndeterminate}. */
|
|
1097
1294
|
get isIndeterminate() {
|
|
1098
|
-
|
|
1099
|
-
|
|
1295
|
+
return this.IsIndeterminate;
|
|
1296
|
+
}
|
|
1297
|
+
get HasSelection() {
|
|
1298
|
+
return this.SelectedUserIds.size > 0;
|
|
1100
1299
|
}
|
|
1300
|
+
/** @deprecated Use {@link HasSelection}. */
|
|
1101
1301
|
get hasSelection() {
|
|
1102
|
-
return this.
|
|
1302
|
+
return this.HasSelection;
|
|
1303
|
+
}
|
|
1304
|
+
get SelectedCount() {
|
|
1305
|
+
return this.SelectedUserIds.size;
|
|
1103
1306
|
}
|
|
1307
|
+
/** @deprecated Use {@link SelectedCount}. */
|
|
1104
1308
|
get selectedCount() {
|
|
1105
|
-
return this.
|
|
1309
|
+
return this.SelectedCount;
|
|
1106
1310
|
}
|
|
1107
|
-
get
|
|
1108
|
-
const filters = this.
|
|
1311
|
+
get HasActiveFilters() {
|
|
1312
|
+
const filters = this.Filters$.value;
|
|
1109
1313
|
return filters.status !== 'all' || filters.role !== '';
|
|
1110
1314
|
}
|
|
1111
|
-
|
|
1315
|
+
/** @deprecated Use {@link HasActiveFilters}. */
|
|
1316
|
+
get hasActiveFilters() {
|
|
1317
|
+
return this.HasActiveFilters;
|
|
1318
|
+
}
|
|
1319
|
+
get ActiveFilterCount() {
|
|
1112
1320
|
let count = 0;
|
|
1113
|
-
const filters = this.
|
|
1321
|
+
const filters = this.Filters$.value;
|
|
1114
1322
|
if (filters.status !== 'all')
|
|
1115
1323
|
count++;
|
|
1116
1324
|
if (filters.role !== '')
|
|
1117
1325
|
count++;
|
|
1118
1326
|
return count;
|
|
1119
1327
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1328
|
+
/** @deprecated Use {@link ActiveFilterCount}. */
|
|
1329
|
+
get activeFilterCount() {
|
|
1330
|
+
return this.ActiveFilterCount;
|
|
1331
|
+
}
|
|
1332
|
+
ClearFilters() {
|
|
1333
|
+
this.Filters$.next({
|
|
1334
|
+
...this.Filters$.value,
|
|
1123
1335
|
status: 'all',
|
|
1124
1336
|
role: ''
|
|
1125
1337
|
});
|
|
1126
1338
|
this.applyFilters();
|
|
1127
1339
|
this.cdr.markForCheck();
|
|
1128
1340
|
}
|
|
1341
|
+
/** @deprecated Use {@link ClearFilters}. */
|
|
1342
|
+
clearFilters() {
|
|
1343
|
+
return this.ClearFilters();
|
|
1344
|
+
}
|
|
1129
1345
|
// -- Filter panel binding (mj-filter-panel inside the popover) -------------
|
|
1130
1346
|
//
|
|
1131
1347
|
// Only Role lives inside the popover — Status is exposed as visible chips
|
|
@@ -1137,7 +1353,7 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1137
1353
|
* FilterFieldConfig[] describing the popover's Role field. Driven off
|
|
1138
1354
|
* `roles[]` so the dropdown stays in sync as roles load.
|
|
1139
1355
|
*/
|
|
1140
|
-
get
|
|
1356
|
+
get FilterFields() {
|
|
1141
1357
|
return [
|
|
1142
1358
|
{
|
|
1143
1359
|
key: 'status',
|
|
@@ -1155,25 +1371,33 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1155
1371
|
label: 'Role',
|
|
1156
1372
|
icon: 'fa-solid fa-user-shield',
|
|
1157
1373
|
placeholder: 'All Roles',
|
|
1158
|
-
filterable: this.
|
|
1374
|
+
filterable: this.Roles.length > 10,
|
|
1159
1375
|
options: [
|
|
1160
1376
|
{ text: 'All Roles', value: '' },
|
|
1161
|
-
...this.
|
|
1377
|
+
...this.Roles.map(r => ({ text: r.Name ?? '', value: r.ID }))
|
|
1162
1378
|
]
|
|
1163
1379
|
}
|
|
1164
1380
|
];
|
|
1165
1381
|
}
|
|
1382
|
+
/** @deprecated Use {@link FilterFields}. */
|
|
1383
|
+
get filterFields() {
|
|
1384
|
+
return this.FilterFields;
|
|
1385
|
+
}
|
|
1166
1386
|
/** Current popover field values keyed by FilterFieldConfig.key. */
|
|
1387
|
+
get FilterValues() {
|
|
1388
|
+
return { status: this.Filters$.value.status, role: this.Filters$.value.role };
|
|
1389
|
+
}
|
|
1390
|
+
/** @deprecated Use {@link FilterValues}. */
|
|
1167
1391
|
get filterValues() {
|
|
1168
|
-
return
|
|
1392
|
+
return this.FilterValues;
|
|
1169
1393
|
}
|
|
1170
1394
|
/** Total active filters (Status + Role) — drives the Filter button badge. */
|
|
1171
1395
|
get TotalActiveFilterCount() {
|
|
1172
|
-
const f = this.
|
|
1396
|
+
const f = this.Filters$.value;
|
|
1173
1397
|
return (f.status !== 'all' ? 1 : 0) + (f.role !== '' ? 1 : 0);
|
|
1174
1398
|
}
|
|
1175
1399
|
/** Apply a value change from <mj-filter-panel>. */
|
|
1176
|
-
|
|
1400
|
+
OnFilterPanelChange(values) {
|
|
1177
1401
|
const partial = {};
|
|
1178
1402
|
if ('status' in values) {
|
|
1179
1403
|
partial.status = values['status'] || 'all';
|
|
@@ -1181,70 +1405,106 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1181
1405
|
if ('role' in values) {
|
|
1182
1406
|
partial.role = values['role'] ?? '';
|
|
1183
1407
|
}
|
|
1184
|
-
this.
|
|
1408
|
+
this.UpdateFilter(partial);
|
|
1409
|
+
}
|
|
1410
|
+
/** @deprecated Use {@link OnFilterPanelChange}. */
|
|
1411
|
+
onFilterPanelChange(values) {
|
|
1412
|
+
return this.OnFilterPanelChange(values);
|
|
1185
1413
|
}
|
|
1186
1414
|
/** Clear all filters (Status + Role); search persists. */
|
|
1415
|
+
ClearAllAppliedFilters() {
|
|
1416
|
+
this.UpdateFilter({ status: 'all', role: '' });
|
|
1417
|
+
}
|
|
1418
|
+
/** @deprecated Use {@link ClearAllAppliedFilters}. */
|
|
1187
1419
|
clearAllAppliedFilters() {
|
|
1188
|
-
this.
|
|
1420
|
+
return this.ClearAllAppliedFilters();
|
|
1189
1421
|
}
|
|
1190
1422
|
/** True when search and/or panel filters are narrowing the list — gates the
|
|
1191
1423
|
* no-results empty-state "Reset filters" CTA. */
|
|
1192
1424
|
get IsListNarrowed() {
|
|
1193
|
-
return this.
|
|
1425
|
+
return this.Filters$.value.search !== '' || this.TotalActiveFilterCount > 0;
|
|
1194
1426
|
}
|
|
1195
1427
|
/** Reset everything narrowing the list (search + Status + Role) and refresh
|
|
1196
1428
|
* immediately. Wired to the no-results empty-state CTA. Unlike
|
|
1197
1429
|
* clearAllAppliedFilters(), this also clears the search box. */
|
|
1198
|
-
|
|
1199
|
-
this.
|
|
1430
|
+
ResetAllFiltersAndSearch() {
|
|
1431
|
+
this.Filters$.next({ status: 'all', role: '', search: '' });
|
|
1200
1432
|
this.applyFilters();
|
|
1201
1433
|
this.cdr.markForCheck();
|
|
1202
1434
|
}
|
|
1203
|
-
|
|
1204
|
-
|
|
1435
|
+
/** @deprecated Use {@link ResetAllFiltersAndSearch}. */
|
|
1436
|
+
resetAllFiltersAndSearch() {
|
|
1437
|
+
return this.ResetAllFiltersAndSearch();
|
|
1438
|
+
}
|
|
1439
|
+
ToggleSelectAll() {
|
|
1440
|
+
if (this.IsAllSelected) {
|
|
1205
1441
|
// Deselect all filtered users
|
|
1206
|
-
this.
|
|
1442
|
+
this.FilteredUsers.forEach(user => this.SelectedUserIds.delete(user.ID));
|
|
1207
1443
|
}
|
|
1208
1444
|
else {
|
|
1209
1445
|
// Select all filtered users
|
|
1210
|
-
this.
|
|
1446
|
+
this.FilteredUsers.forEach(user => this.SelectedUserIds.add(user.ID));
|
|
1211
1447
|
}
|
|
1212
1448
|
}
|
|
1213
|
-
|
|
1449
|
+
/** @deprecated Use {@link ToggleSelectAll}. */
|
|
1450
|
+
toggleSelectAll() {
|
|
1451
|
+
return this.ToggleSelectAll();
|
|
1452
|
+
}
|
|
1453
|
+
ToggleUserSelection(userId, event) {
|
|
1214
1454
|
if (event) {
|
|
1215
1455
|
event.stopPropagation();
|
|
1216
1456
|
}
|
|
1217
|
-
if (this.
|
|
1218
|
-
this.
|
|
1457
|
+
if (this.SelectedUserIds.has(userId)) {
|
|
1458
|
+
this.SelectedUserIds.delete(userId);
|
|
1219
1459
|
}
|
|
1220
1460
|
else {
|
|
1221
|
-
this.
|
|
1461
|
+
this.SelectedUserIds.add(userId);
|
|
1222
1462
|
}
|
|
1223
1463
|
}
|
|
1464
|
+
/** @deprecated Use {@link ToggleUserSelection}. */
|
|
1465
|
+
toggleUserSelection(userId, event) {
|
|
1466
|
+
return this.ToggleUserSelection(userId, event);
|
|
1467
|
+
}
|
|
1468
|
+
IsUserSelected(userId) {
|
|
1469
|
+
return this.SelectedUserIds.has(userId);
|
|
1470
|
+
}
|
|
1471
|
+
/** @deprecated Use {@link IsUserSelected}. */
|
|
1224
1472
|
isUserSelected(userId) {
|
|
1225
|
-
return this.
|
|
1473
|
+
return this.IsUserSelected(userId);
|
|
1474
|
+
}
|
|
1475
|
+
ClearSelection() {
|
|
1476
|
+
this.SelectedUserIds.clear();
|
|
1226
1477
|
}
|
|
1478
|
+
/** @deprecated Use {@link ClearSelection}. */
|
|
1227
1479
|
clearSelection() {
|
|
1228
|
-
this.
|
|
1480
|
+
return this.ClearSelection();
|
|
1229
1481
|
}
|
|
1230
1482
|
// Bulk action methods
|
|
1231
|
-
|
|
1232
|
-
if (!this.
|
|
1483
|
+
ConfirmBulkAction(action) {
|
|
1484
|
+
if (!this.HasSelection)
|
|
1233
1485
|
return;
|
|
1234
|
-
this.
|
|
1235
|
-
this.
|
|
1486
|
+
this.BulkActionType = action;
|
|
1487
|
+
this.ShowBulkActionConfirm = true;
|
|
1236
1488
|
}
|
|
1489
|
+
/** @deprecated Use {@link ConfirmBulkAction}. */
|
|
1490
|
+
confirmBulkAction(action) {
|
|
1491
|
+
return this.ConfirmBulkAction(action);
|
|
1492
|
+
}
|
|
1493
|
+
CancelBulkAction() {
|
|
1494
|
+
this.ShowBulkActionConfirm = false;
|
|
1495
|
+
this.BulkActionType = null;
|
|
1496
|
+
}
|
|
1497
|
+
/** @deprecated Use {@link CancelBulkAction}. */
|
|
1237
1498
|
cancelBulkAction() {
|
|
1238
|
-
this.
|
|
1239
|
-
this.bulkActionType = null;
|
|
1499
|
+
return this.CancelBulkAction();
|
|
1240
1500
|
}
|
|
1241
|
-
async
|
|
1242
|
-
if (!this.
|
|
1501
|
+
async ExecuteBulkAction() {
|
|
1502
|
+
if (!this.BulkActionType || !this.HasSelection)
|
|
1243
1503
|
return;
|
|
1244
1504
|
try {
|
|
1245
1505
|
this.isLoading = true;
|
|
1246
|
-
const selectedUsers = this.
|
|
1247
|
-
switch (this.
|
|
1506
|
+
const selectedUsers = this.Users.filter(user => this.SelectedUserIds.has(user.ID));
|
|
1507
|
+
switch (this.BulkActionType) {
|
|
1248
1508
|
case 'enable':
|
|
1249
1509
|
await this.bulkSetUserStatus(selectedUsers, true);
|
|
1250
1510
|
break;
|
|
@@ -1255,10 +1515,10 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1255
1515
|
await this.bulkDeleteUsers(selectedUsers);
|
|
1256
1516
|
break;
|
|
1257
1517
|
}
|
|
1258
|
-
this.
|
|
1259
|
-
this.
|
|
1260
|
-
this.
|
|
1261
|
-
await this.
|
|
1518
|
+
this.ClearSelection();
|
|
1519
|
+
this.ShowBulkActionConfirm = false;
|
|
1520
|
+
this.BulkActionType = null;
|
|
1521
|
+
await this.LoadInitialData();
|
|
1262
1522
|
}
|
|
1263
1523
|
catch (error) {
|
|
1264
1524
|
console.error('Bulk action failed:', error);
|
|
@@ -1274,6 +1534,10 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1274
1534
|
});
|
|
1275
1535
|
}
|
|
1276
1536
|
}
|
|
1537
|
+
/** @deprecated Use {@link ExecuteBulkAction}. */
|
|
1538
|
+
async executeBulkAction() {
|
|
1539
|
+
return this.ExecuteBulkAction();
|
|
1540
|
+
}
|
|
1277
1541
|
async bulkSetUserStatus(users, isActive) {
|
|
1278
1542
|
if (users.length === 0)
|
|
1279
1543
|
return;
|
|
@@ -1306,26 +1570,34 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1306
1570
|
}
|
|
1307
1571
|
}
|
|
1308
1572
|
// Bulk role assignment
|
|
1309
|
-
|
|
1310
|
-
if (!this.
|
|
1573
|
+
OpenBulkRoleAssign() {
|
|
1574
|
+
if (!this.HasSelection)
|
|
1311
1575
|
return;
|
|
1312
|
-
this.
|
|
1313
|
-
this.
|
|
1576
|
+
this.BulkRoleId = '';
|
|
1577
|
+
this.ShowBulkRoleAssign = true;
|
|
1314
1578
|
}
|
|
1579
|
+
/** @deprecated Use {@link OpenBulkRoleAssign}. */
|
|
1580
|
+
openBulkRoleAssign() {
|
|
1581
|
+
return this.OpenBulkRoleAssign();
|
|
1582
|
+
}
|
|
1583
|
+
CancelBulkRoleAssign() {
|
|
1584
|
+
this.ShowBulkRoleAssign = false;
|
|
1585
|
+
this.BulkRoleId = '';
|
|
1586
|
+
}
|
|
1587
|
+
/** @deprecated Use {@link CancelBulkRoleAssign}. */
|
|
1315
1588
|
cancelBulkRoleAssign() {
|
|
1316
|
-
this.
|
|
1317
|
-
this.bulkRoleId = '';
|
|
1589
|
+
return this.CancelBulkRoleAssign();
|
|
1318
1590
|
}
|
|
1319
|
-
async
|
|
1320
|
-
if (!this.
|
|
1591
|
+
async ExecuteBulkRoleAssign() {
|
|
1592
|
+
if (!this.BulkRoleId || !this.HasSelection)
|
|
1321
1593
|
return;
|
|
1322
1594
|
try {
|
|
1323
1595
|
this.isLoading = true;
|
|
1324
|
-
const selectedUserIds = Array.from(this.
|
|
1596
|
+
const selectedUserIds = Array.from(this.SelectedUserIds);
|
|
1325
1597
|
// Collect only the users that don't already have the role
|
|
1326
1598
|
const usersNeedingRole = selectedUserIds.filter(userId => {
|
|
1327
1599
|
const existingRoles = this.userRoleMap.get(userId) || [];
|
|
1328
|
-
return !existingRoles.includes(this.
|
|
1600
|
+
return !existingRoles.includes(this.BulkRoleId);
|
|
1329
1601
|
});
|
|
1330
1602
|
if (usersNeedingRole.length > 0) {
|
|
1331
1603
|
const tg = await this.metadata.CreateTransactionGroup();
|
|
@@ -1359,7 +1631,7 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1359
1631
|
const userRole = await this.metadata.GetEntityObject('MJ: User Roles');
|
|
1360
1632
|
userRole.NewRecord();
|
|
1361
1633
|
userRole.UserID = userId;
|
|
1362
|
-
userRole.RoleID = this.
|
|
1634
|
+
userRole.RoleID = this.BulkRoleId;
|
|
1363
1635
|
userRole.TransactionGroup = tg;
|
|
1364
1636
|
if (!await userRole.Save()) {
|
|
1365
1637
|
refusals.push(`${this.describeUser(userId)}: ${userRole.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
@@ -1368,7 +1640,7 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1368
1640
|
// Kept so the SERVER's reason can be read back off it below. Without this the entity
|
|
1369
1641
|
// goes out of scope at the end of the iteration and the reason #4309 puts on
|
|
1370
1642
|
// LatestResult has nobody left to read it.
|
|
1371
|
-
enrolled.push({
|
|
1643
|
+
enrolled.push({ Label: this.describeUser(userId), Entity: userRole });
|
|
1372
1644
|
}
|
|
1373
1645
|
}
|
|
1374
1646
|
if (refusals.length > 0) {
|
|
@@ -1379,16 +1651,16 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1379
1651
|
if (!await tg.Submit()) {
|
|
1380
1652
|
// Every row enrolled, so this is a SERVER-side refusal (or a rollback). Since #4309 the
|
|
1381
1653
|
// server says which row and why, and that reason is now on each entity's LatestResult.
|
|
1382
|
-
const reasons =
|
|
1654
|
+
const reasons = ServerRefusalReasons(enrolled);
|
|
1383
1655
|
throw new Error(reasons.length > 0
|
|
1384
1656
|
? `Failed to assign roles — all changes have been rolled back.\n${reasons.join('\n')}`
|
|
1385
1657
|
: 'Failed to assign roles — all changes have been rolled back');
|
|
1386
1658
|
}
|
|
1387
1659
|
}
|
|
1388
|
-
this.
|
|
1389
|
-
this.
|
|
1390
|
-
this.
|
|
1391
|
-
await this.
|
|
1660
|
+
this.ClearSelection();
|
|
1661
|
+
this.ShowBulkRoleAssign = false;
|
|
1662
|
+
this.BulkRoleId = '';
|
|
1663
|
+
await this.LoadInitialData();
|
|
1392
1664
|
}
|
|
1393
1665
|
catch (error) {
|
|
1394
1666
|
console.error('Bulk role assignment failed:', error);
|
|
@@ -1404,9 +1676,13 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1404
1676
|
});
|
|
1405
1677
|
}
|
|
1406
1678
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1679
|
+
/** @deprecated Use {@link ExecuteBulkRoleAssign}. */
|
|
1680
|
+
async executeBulkRoleAssign() {
|
|
1681
|
+
return this.ExecuteBulkRoleAssign();
|
|
1682
|
+
}
|
|
1683
|
+
GetBulkActionMessage() {
|
|
1684
|
+
const count = this.SelectedCount;
|
|
1685
|
+
switch (this.BulkActionType) {
|
|
1410
1686
|
case 'enable':
|
|
1411
1687
|
return `Are you sure you want to enable ${count} user${count > 1 ? 's' : ''}?`;
|
|
1412
1688
|
case 'disable':
|
|
@@ -1417,8 +1693,12 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1417
1693
|
return '';
|
|
1418
1694
|
}
|
|
1419
1695
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1696
|
+
/** @deprecated Use {@link GetBulkActionMessage}. */
|
|
1697
|
+
getBulkActionMessage() {
|
|
1698
|
+
return this.GetBulkActionMessage();
|
|
1699
|
+
}
|
|
1700
|
+
GetBulkActionTitle() {
|
|
1701
|
+
switch (this.BulkActionType) {
|
|
1422
1702
|
case 'enable':
|
|
1423
1703
|
return 'Enable Users';
|
|
1424
1704
|
case 'disable':
|
|
@@ -1429,9 +1709,13 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1429
1709
|
return 'Confirm Action';
|
|
1430
1710
|
}
|
|
1431
1711
|
}
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1712
|
+
/** @deprecated Use {@link GetBulkActionTitle}. */
|
|
1713
|
+
getBulkActionTitle() {
|
|
1714
|
+
return this.GetBulkActionTitle();
|
|
1715
|
+
}
|
|
1716
|
+
GetBulkActionButtonText() {
|
|
1717
|
+
const count = this.SelectedCount;
|
|
1718
|
+
switch (this.BulkActionType) {
|
|
1435
1719
|
case 'enable':
|
|
1436
1720
|
return `Enable ${count} User${count > 1 ? 's' : ''}`;
|
|
1437
1721
|
case 'disable':
|
|
@@ -1442,8 +1726,12 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1442
1726
|
return 'Confirm';
|
|
1443
1727
|
}
|
|
1444
1728
|
}
|
|
1729
|
+
/** @deprecated Use {@link GetBulkActionButtonText}. */
|
|
1730
|
+
getBulkActionButtonText() {
|
|
1731
|
+
return this.GetBulkActionButtonText();
|
|
1732
|
+
}
|
|
1445
1733
|
// Expansion methods
|
|
1446
|
-
|
|
1734
|
+
ToggleUserExpansion(userId) {
|
|
1447
1735
|
if (this.expandedUserIds.has(userId)) {
|
|
1448
1736
|
this.expandedUserIds.delete(userId);
|
|
1449
1737
|
}
|
|
@@ -1451,20 +1739,41 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1451
1739
|
this.expandedUserIds.add(userId);
|
|
1452
1740
|
}
|
|
1453
1741
|
}
|
|
1454
|
-
|
|
1742
|
+
/** @deprecated Use {@link ToggleUserExpansion}. */
|
|
1743
|
+
toggleUserExpansion(userId) {
|
|
1744
|
+
return this.ToggleUserExpansion(userId);
|
|
1745
|
+
}
|
|
1746
|
+
IsUserExpanded(userId) {
|
|
1455
1747
|
return this.expandedUserIds.has(userId);
|
|
1456
1748
|
}
|
|
1749
|
+
/** @deprecated Use {@link IsUserExpanded}. */
|
|
1750
|
+
isUserExpanded(userId) {
|
|
1751
|
+
return this.IsUserExpanded(userId);
|
|
1752
|
+
}
|
|
1457
1753
|
// Get roles for a specific user
|
|
1754
|
+
GetUserRoles(userId) {
|
|
1755
|
+
const roleIds = this.userRoleMap.get(userId);
|
|
1756
|
+
if (!roleIds || roleIds.length === 0) {
|
|
1757
|
+
return [];
|
|
1758
|
+
}
|
|
1759
|
+
// Normalize the user's role IDs into a Set rather than running a nested `.some(UUIDsEqual(...))`
|
|
1760
|
+
// per role: UUIDsEqual's `===` fast path misses whenever the IDs differ, so every miss allocated
|
|
1761
|
+
// two lowercased strings and the nested scan paid that roles x userRoles times. The template
|
|
1762
|
+
// calls this TWICE per user row (the @if and the @for), on every change-detection pass. Filtering
|
|
1763
|
+
// `this.Roles` still drives the result, so the rendered order is unchanged.
|
|
1764
|
+
const assignedIds = new Set(roleIds.map(id => NormalizeUUID(id)));
|
|
1765
|
+
return this.Roles.filter(role => assignedIds.has(NormalizeUUID(role.ID)));
|
|
1766
|
+
}
|
|
1767
|
+
/** @deprecated Use {@link GetUserRoles}. */
|
|
1458
1768
|
getUserRoles(userId) {
|
|
1459
|
-
|
|
1460
|
-
return this.roles.filter(role => roleIds.some(id => UUIDsEqual(id, role.ID)));
|
|
1769
|
+
return this.GetUserRoles(userId);
|
|
1461
1770
|
}
|
|
1462
1771
|
/**
|
|
1463
1772
|
* Names a user for an error message. Falls back to the raw ID rather than to a placeholder so a
|
|
1464
1773
|
* refusal for a user who has dropped out of the loaded page is still traceable.
|
|
1465
1774
|
*/
|
|
1466
1775
|
describeUser(userId) {
|
|
1467
|
-
const user = this.
|
|
1776
|
+
const user = this.Users.find(u => UUIDsEqual(u.ID, userId));
|
|
1468
1777
|
return user?.Email ?? user?.Name ?? userId;
|
|
1469
1778
|
}
|
|
1470
1779
|
static ɵfac = function UserManagementComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || UserManagementComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i0.NgZone)); };
|