@memberjunction/ng-explorer-settings 3.3.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/account-info/account-info.component.d.ts +23 -0
- package/dist/lib/account-info/account-info.component.d.ts.map +1 -0
- package/dist/lib/account-info/account-info.component.js +157 -0
- package/dist/lib/account-info/account-info.component.js.map +1 -0
- package/dist/lib/appearance-settings/appearance-settings.component.d.ts +15 -0
- package/dist/lib/appearance-settings/appearance-settings.component.d.ts.map +1 -0
- package/dist/lib/appearance-settings/appearance-settings.component.js +79 -0
- package/dist/lib/appearance-settings/appearance-settings.component.js.map +1 -0
- 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 +20 -18
- package/dist/lib/application-management/application-dialog/application-dialog.component.js.map +1 -1
- package/dist/lib/application-management/application-management.component.d.ts +3 -2
- package/dist/lib/application-management/application-management.component.d.ts.map +1 -1
- package/dist/lib/application-management/application-management.component.js +20 -15
- package/dist/lib/application-management/application-management.component.js.map +1 -1
- package/dist/lib/application-settings/application-settings.component.d.ts +113 -0
- package/dist/lib/application-settings/application-settings.component.d.ts.map +1 -0
- package/dist/lib/application-settings/application-settings.component.js +523 -0
- package/dist/lib/application-settings/application-settings.component.js.map +1 -0
- package/dist/lib/entity-permissions/entity-permissions.component.d.ts.map +1 -1
- package/dist/lib/entity-permissions/entity-permissions.component.js +13 -10
- package/dist/lib/entity-permissions/entity-permissions.component.js.map +1 -1
- package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.d.ts +1 -1
- 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 +44 -43
- package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.js.map +1 -1
- package/dist/lib/general-settings/general-settings.component.d.ts +14 -0
- package/dist/lib/general-settings/general-settings.component.d.ts.map +1 -0
- package/dist/lib/general-settings/general-settings.component.js +50 -0
- package/dist/lib/general-settings/general-settings.component.js.map +1 -0
- package/dist/lib/module.d.ts +30 -28
- package/dist/lib/module.d.ts.map +1 -1
- package/dist/lib/module.js +64 -37
- package/dist/lib/module.js.map +1 -1
- package/dist/lib/notification-preferences/notification-preferences.component.d.ts.map +1 -1
- package/dist/lib/notification-preferences/notification-preferences.component.js +9 -8
- package/dist/lib/notification-preferences/notification-preferences.component.js.map +1 -1
- package/dist/lib/role-management/role-dialog/role-dialog.component.d.ts +1 -1
- 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 +32 -32
- package/dist/lib/role-management/role-dialog/role-dialog.component.js.map +1 -1
- package/dist/lib/role-management/role-management.component.d.ts.map +1 -1
- package/dist/lib/role-management/role-management.component.js +11 -8
- package/dist/lib/role-management/role-management.component.js.map +1 -1
- package/dist/lib/settings/settings.component.d.ts +5 -14
- package/dist/lib/settings/settings.component.d.ts.map +1 -1
- package/dist/lib/settings/settings.component.js +149 -419
- package/dist/lib/settings/settings.component.js.map +1 -1
- package/dist/lib/shared/components/settings-card/settings-card.component.d.ts +27 -0
- package/dist/lib/shared/components/settings-card/settings-card.component.d.ts.map +1 -0
- package/dist/lib/shared/components/settings-card/settings-card.component.js +170 -0
- package/dist/lib/shared/components/settings-card/settings-card.component.js.map +1 -0
- package/dist/lib/shared/settings-card.component.d.ts.map +1 -1
- package/dist/lib/shared/settings-card.component.js +4 -4
- package/dist/lib/shared/settings-card.component.js.map +1 -1
- package/dist/lib/sql-logging/sql-logging.component.d.ts +2 -2
- package/dist/lib/sql-logging/sql-logging.component.d.ts.map +1 -1
- package/dist/lib/sql-logging/sql-logging.component.js +21 -16
- package/dist/lib/sql-logging/sql-logging.component.js.map +1 -1
- package/dist/lib/user-app-config/user-app-config.component.d.ts.map +1 -1
- package/dist/lib/user-app-config/user-app-config.component.js +14 -11
- package/dist/lib/user-app-config/user-app-config.component.js.map +1 -1
- package/dist/lib/user-management/user-dialog/user-dialog.component.d.ts +1 -1
- 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 +37 -36
- package/dist/lib/user-management/user-dialog/user-dialog.component.js.map +1 -1
- package/dist/lib/user-management/user-management.component.d.ts.map +1 -1
- package/dist/lib/user-management/user-management.component.js +23 -19
- package/dist/lib/user-management/user-management.component.js.map +1 -1
- 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 +16 -12
- package/dist/lib/user-profile-settings/user-profile-settings.component.js.map +1 -1
- package/dist/public-api.d.ts +5 -12
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +6 -38
- package/dist/public-api.js.map +1 -1
- package/package.json +35 -37
|
@@ -120,7 +120,7 @@ function UserManagementComponent_Conditional_75_Template(rf, ctx) { if (rf & 1)
|
|
|
120
120
|
i0.ɵɵelementEnd();
|
|
121
121
|
i0.ɵɵrepeaterCreate(18, UserManagementComponent_Conditional_75_For_19_Template, 2, 2, "option", 42, _forTrack0);
|
|
122
122
|
i0.ɵɵelementEnd()();
|
|
123
|
-
i0.ɵɵ
|
|
123
|
+
i0.ɵɵconditionalCreate(20, UserManagementComponent_Conditional_75_Conditional_20_Template, 3, 0, "button", 74);
|
|
124
124
|
i0.ɵɵelementEnd()();
|
|
125
125
|
} if (rf & 2) {
|
|
126
126
|
const ctx_r1 = i0.ɵɵnextContext();
|
|
@@ -252,7 +252,7 @@ function UserManagementComponent_Conditional_79_For_9_Conditional_25_Template(rf
|
|
|
252
252
|
i0.ɵɵelement(48, "i", 122);
|
|
253
253
|
i0.ɵɵtext(49, " Assigned Roles ");
|
|
254
254
|
i0.ɵɵelementEnd();
|
|
255
|
-
i0.ɵɵ
|
|
255
|
+
i0.ɵɵconditionalCreate(50, UserManagementComponent_Conditional_79_For_9_Conditional_25_Conditional_50_Template, 3, 0, "div", 123)(51, UserManagementComponent_Conditional_79_For_9_Conditional_25_Conditional_51_Template, 2, 0, "p", 124);
|
|
256
256
|
i0.ɵɵelementEnd()();
|
|
257
257
|
} if (rf & 2) {
|
|
258
258
|
const user_r10 = i0.ɵɵnextContext().$implicit;
|
|
@@ -278,7 +278,7 @@ function UserManagementComponent_Conditional_79_For_9_Conditional_25_Template(rf
|
|
|
278
278
|
i0.ɵɵadvance();
|
|
279
279
|
i0.ɵɵtextInterpolate1(" ", user_r10.Type, " ");
|
|
280
280
|
i0.ɵɵadvance(6);
|
|
281
|
-
i0.ɵɵtextInterpolate2("", user_r10.FirstName, " ", user_r10.LastName
|
|
281
|
+
i0.ɵɵtextInterpolate2("", user_r10.FirstName, " ", user_r10.LastName);
|
|
282
282
|
i0.ɵɵadvance(6);
|
|
283
283
|
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(38, 19, user_r10.__mj_CreatedAt, "short"));
|
|
284
284
|
i0.ɵɵadvance(7);
|
|
@@ -325,7 +325,7 @@ function UserManagementComponent_Conditional_79_For_9_Template(rf, ctx) { if (rf
|
|
|
325
325
|
i0.ɵɵelementStart(23, "button", 106);
|
|
326
326
|
i0.ɵɵelement(24, "i", 107);
|
|
327
327
|
i0.ɵɵelementEnd()()();
|
|
328
|
-
i0.ɵɵ
|
|
328
|
+
i0.ɵɵconditionalCreate(25, UserManagementComponent_Conditional_79_For_9_Conditional_25_Template, 52, 25, "div", 108);
|
|
329
329
|
i0.ɵɵelementEnd();
|
|
330
330
|
} if (rf & 2) {
|
|
331
331
|
const user_r10 = ctx.$implicit;
|
|
@@ -378,10 +378,10 @@ function UserManagementComponent_Conditional_79_Template(rf, ctx) { if (rf & 1)
|
|
|
378
378
|
i0.ɵɵelementStart(5, "span", 85);
|
|
379
379
|
i0.ɵɵtext(6);
|
|
380
380
|
i0.ɵɵelementEnd()();
|
|
381
|
-
i0.ɵɵ
|
|
381
|
+
i0.ɵɵconditionalCreate(7, UserManagementComponent_Conditional_79_Conditional_7_Template, 2, 1, "span", 86);
|
|
382
382
|
i0.ɵɵelementEnd();
|
|
383
383
|
i0.ɵɵrepeaterCreate(8, UserManagementComponent_Conditional_79_For_9_Template, 26, 22, "div", 87, _forTrack0);
|
|
384
|
-
i0.ɵɵ
|
|
384
|
+
i0.ɵɵconditionalCreate(10, UserManagementComponent_Conditional_79_Conditional_10_Template, 6, 0, "div", 88);
|
|
385
385
|
i0.ɵɵelementEnd()();
|
|
386
386
|
} if (rf & 2) {
|
|
387
387
|
const ctx_r1 = i0.ɵɵnextContext();
|
|
@@ -462,7 +462,7 @@ function UserManagementComponent_Conditional_82_Conditional_17_Conditional_2_Tem
|
|
|
462
462
|
i0.ɵɵelement(0, "i", 60);
|
|
463
463
|
} }
|
|
464
464
|
function UserManagementComponent_Conditional_82_Conditional_17_Template(rf, ctx) { if (rf & 1) {
|
|
465
|
-
i0.ɵɵ
|
|
465
|
+
i0.ɵɵconditionalCreate(0, UserManagementComponent_Conditional_82_Conditional_17_Conditional_0_Template, 1, 0, "i", 64)(1, UserManagementComponent_Conditional_82_Conditional_17_Conditional_1_Template, 1, 0, "i", 58)(2, UserManagementComponent_Conditional_82_Conditional_17_Conditional_2_Template, 1, 0, "i", 60);
|
|
466
466
|
i0.ɵɵtext(3);
|
|
467
467
|
} if (rf & 2) {
|
|
468
468
|
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
@@ -477,7 +477,7 @@ function UserManagementComponent_Conditional_82_Template(rf, ctx) { if (rf & 1)
|
|
|
477
477
|
i0.ɵɵelementStart(1, "div", 131);
|
|
478
478
|
i0.ɵɵlistener("click", function UserManagementComponent_Conditional_82_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r14); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
479
479
|
i0.ɵɵelementStart(2, "div", 132)(3, "h3", 142);
|
|
480
|
-
i0.ɵɵ
|
|
480
|
+
i0.ɵɵconditionalCreate(4, UserManagementComponent_Conditional_82_Conditional_4_Template, 1, 0, "i", 134)(5, UserManagementComponent_Conditional_82_Conditional_5_Template, 1, 0, "i", 143)(6, UserManagementComponent_Conditional_82_Conditional_6_Template, 1, 0, "i", 144);
|
|
481
481
|
i0.ɵɵtext(7);
|
|
482
482
|
i0.ɵɵelementEnd();
|
|
483
483
|
i0.ɵɵelementStart(8, "button", 135);
|
|
@@ -487,11 +487,11 @@ function UserManagementComponent_Conditional_82_Template(rf, ctx) { if (rf & 1)
|
|
|
487
487
|
i0.ɵɵelementStart(10, "div", 145)(11, "p");
|
|
488
488
|
i0.ɵɵtext(12);
|
|
489
489
|
i0.ɵɵelementEnd();
|
|
490
|
-
i0.ɵɵ
|
|
490
|
+
i0.ɵɵconditionalCreate(13, UserManagementComponent_Conditional_82_Conditional_13_Template, 2, 0, "p", 137);
|
|
491
491
|
i0.ɵɵelementEnd();
|
|
492
492
|
i0.ɵɵelementStart(14, "div", 138)(15, "button", 146);
|
|
493
493
|
i0.ɵɵlistener("click", function UserManagementComponent_Conditional_82_Template_button_click_15_listener() { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.executeBulkAction()); });
|
|
494
|
-
i0.ɵɵ
|
|
494
|
+
i0.ɵɵconditionalCreate(16, UserManagementComponent_Conditional_82_Conditional_16_Template, 2, 0)(17, UserManagementComponent_Conditional_82_Conditional_17_Template, 4, 2);
|
|
495
495
|
i0.ɵɵelementEnd();
|
|
496
496
|
i0.ɵɵelementStart(18, "button", 147);
|
|
497
497
|
i0.ɵɵlistener("click", function UserManagementComponent_Conditional_82_Template_button_click_18_listener() { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.cancelBulkAction()); });
|
|
@@ -562,7 +562,7 @@ function UserManagementComponent_Conditional_83_Template(rf, ctx) { if (rf & 1)
|
|
|
562
562
|
i0.ɵɵelementEnd()()();
|
|
563
563
|
i0.ɵɵelementStart(19, "div", 138)(20, "button", 155);
|
|
564
564
|
i0.ɵɵlistener("click", function UserManagementComponent_Conditional_83_Template_button_click_20_listener() { i0.ɵɵrestoreView(_r15); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.executeBulkRoleAssign()); });
|
|
565
|
-
i0.ɵɵ
|
|
565
|
+
i0.ɵɵconditionalCreate(21, UserManagementComponent_Conditional_83_Conditional_21_Template, 2, 0)(22, UserManagementComponent_Conditional_83_Conditional_22_Template, 2, 0);
|
|
566
566
|
i0.ɵɵelementEnd();
|
|
567
567
|
i0.ɵɵelementStart(23, "button", 147);
|
|
568
568
|
i0.ɵɵlistener("click", function UserManagementComponent_Conditional_83_Template_button_click_23_listener() { i0.ɵɵrestoreView(_r15); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.cancelBulkRoleAssign()); });
|
|
@@ -1127,7 +1127,7 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1127
1127
|
return this.roles.filter(role => roleIds.includes(role.ID));
|
|
1128
1128
|
}
|
|
1129
1129
|
static ɵfac = function UserManagementComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || UserManagementComponent)(); };
|
|
1130
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: UserManagementComponent, selectors: [["mj-user-management"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 84, vars: 31, consts: [[1, "user-management-container"], [1, "sticky-header"], ["role", "toolbar", "aria-label", "User management actions", 1, "action-buttons"], ["aria-label", "Refresh user list", 1, "mj-btn", "mj-btn-secondary", "mj-btn-icon-mobile", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-refresh"], [1, "btn-text"], ["aria-label", "Export users to file", 1, "mj-btn", "mj-btn-secondary", "mj-btn-icon-mobile", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-download"], ["aria-label", "Add new user", 1, "mj-btn", "mj-btn-primary", "mj-btn-icon-mobile", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-plus"], ["role", "toolbar", "aria-label", "Bulk actions", 1, "bulk-action-toolbar"], ["role", "region", "aria-label", "User statistics", 1, "mj-grid-4"], [1, "mj-card"], ["aria-hidden", "true", 1, "stat-icon", "stat-icon-total"], [1, "fa-solid", "fa-users"], [1, "stat-content"], ["aria-label", "Total users count", 1, "stat-value"], [1, "stat-label"], ["aria-hidden", "true", 1, "stat-icon", "stat-icon-active"], [1, "fa-solid", "fa-user-check"], ["aria-label", "Active users count", 1, "stat-value"], ["aria-hidden", "true", 1, "stat-icon", "stat-icon-inactive"], [1, "fa-solid", "fa-user-xmark"], ["aria-label", "Inactive users count", 1, "stat-value"], ["aria-hidden", "true", 1, "stat-icon", "stat-icon-admin"], [1, "fa-solid", "fa-shield-halved"], ["aria-label", "Owner users count", 1, "stat-value"], ["role", "search", "aria-label", "Filter users", 1, "filters-section"], [1, "filters-row"], [1, "mj-search"], ["aria-hidden", "true", 1, "fa-solid", "fa-search", "mj-search-icon"], ["type", "text", "placeholder", "Search users by name or email...", "aria-label", "Search users by name or email", 1, "mj-search-input", 3, "input", "value"], ["aria-label", "Toggle filters", 1, "mobile-filter-toggle", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-filter"], [1, "filter-badge"], [1, "mj-filter-group", "desktop-filter-group"], ["id", "status-filter-label", 1, "mj-filter-label"], ["role", "group", "aria-labelledby", "status-filter-label", 1, "mj-filter-buttons"], [1, "mj-btn", "mj-btn-ghost", 3, "click"], ["for", "role-filter", 1, "mj-filter-label"], ["id", "role-filter", "aria-label", "Filter by role", 1, "mj-filter-select", 3, "change"], ["value", ""], [3, "value"], [1, "mobile-filter-dropdown"], [1, "scrollable-content"], ["role", "status", "aria-live", "polite", "aria-busy", "true", 1, "loading-container"], ["role", "alert", "aria-live", "assertive", 1, "error-container"], [1, "content-area"], [3, "result", "data", "visible"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "delete-dialog-title", "aria-describedby", "delete-dialog-desc", 1, "modal-backdrop"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "bulk-action-dialog-title", "aria-describedby", "bulk-action-dialog-desc", 1, "modal-backdrop"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "bulk-role-dialog-title", "aria-describedby", "bulk-role-dialog-desc", 1, "modal-backdrop"], [1, "bulk-selection-info"], ["aria-hidden", "true", 1, "fa-solid", "fa-check-square"], ["aria-label", "Clear selection", 1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-times"], [1, "bulk-action-buttons"], ["aria-label", "Enable selected users", 1, "mj-btn", "mj-btn-secondary", "mj-btn-sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-toggle-on"], ["aria-label", "Disable selected users", 1, "mj-btn", "mj-btn-secondary", "mj-btn-sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-toggle-off"], ["aria-label", "Assign role to selected users", 1, "mj-btn", "mj-btn-secondary", "mj-btn-sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-user-tag"], ["aria-label", "Delete selected users", 1, "mj-btn", "mj-btn-danger", "mj-btn-sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-trash"], [1, "mobile-filter-content"], [1, "mj-filter-group"], ["id", "status-filter-label-mobile", 1, "mj-filter-label"], ["aria-labelledby", "status-filter-label-mobile", 1, "mj-filter-select", 3, "change", "value"], ["value", "all"], ["value", "active"], ["value", "inactive"], ["for", "role-filter-mobile", 1, "mj-filter-label"], ["id", "role-filter-mobile", "aria-label", "Filter by role", 1, "mj-filter-select", 3, "change"], [1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", "clear-filters-btn"], [1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", "clear-filters-btn", 3, "click"], ["text", "Loading users...", "size", "medium"], [1, "error-content"], ["aria-hidden", "true", 1, "fa-solid", "fa-exclamation-triangle", "error-icon"], [1, "error-message"], ["aria-label", "Retry loading users", 1, "retry-button", 3, "click"], ["role", "list", "aria-label", "Users list", 1, "users-list"], [1, "list-header"], [1, "select-all-label"], ["type", "checkbox", "aria-label", "Select all users", 1, "checkbox", 3, "change", "checked", "indeterminate"], [1, "select-all-text"], [1, "selection-count"], ["role", "listitem", 1, "user-card", 3, "expanded", "selected"], ["role", "status", 1, "empty-state"], ["role", "listitem", 1, "user-card"], ["role", "button", "tabindex", "0", 1, "user-header", 3, "click", "keydown.enter"], [1, "user-selection", 3, "click"], ["type", "checkbox", 1, "checkbox", 3, "change", "checked"], [1, "user-info"], ["aria-hidden", "true", 1, "user-avatar"], [1, "user-details"], [1, "user-name"], [1, "user-email"], [1, "user-meta"], [1, "status-badge"], ["aria-hidden", "true"], [1, "user-actions", "desktop-only", 3, "click"], ["title", "Edit", 1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-edit"], [1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", 3, "click", "title"], ["title", "Delete", 1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", "mj-btn-danger", 3, "click"], ["aria-label", "Toggle user details", 1, "expand-btn"], ["aria-hidden", "true", 1, "fa-solid", "fa-chevron-down"], [1, "user-content"], [1, "mobile-actions-bar", 3, "click"], [1, "mobile-action-buttons"], [1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", 3, "click"], [1, "btn-label"], [1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", "mj-btn-danger", 3, "click"], [1, "user-stats"], [1, "stat-item"], [1, "stat-value"], ["aria-hidden", "true", 1, "fa-solid", "fa-id-card"], ["aria-hidden", "true", 1, "fa-solid", "fa-calendar"], ["aria-hidden", "true", 1, "fa-solid", "fa-clock"], [1, "roles-section"], [1, "section-title"], ["aria-hidden", "true", 1, "fa-solid", "fa-user-shield"], [1, "roles-grid"], [1, "no-roles"], [1, "role-item"], ["aria-hidden", "true", 1, "fa-solid", "fa-shield"], ["aria-hidden", "true", 1, "fa-solid", "fa-users-slash", "empty-icon"], [1, "empty-text"], [1, "empty-subtext"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "delete-dialog-title", "aria-describedby", "delete-dialog-desc", 1, "modal-backdrop", 3, "click"], [1, "modal-dialog", 3, "click"], [1, "modal-header"], ["id", "delete-dialog-title", 1, "modal-title"], ["aria-hidden", "true", 1, "fa-solid", "fa-exclamation-triangle", "text-danger"], ["aria-label", "Close dialog", 1, "modal-close", 3, "click"], ["id", "delete-dialog-desc", 1, "modal-body"], [1, "text-muted"], [1, "modal-footer"], [1, "mj-btn", "mj-btn-danger", 3, "click"], [1, "mj-btn", "mj-btn-secondary", 3, "click"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "bulk-action-dialog-title", "aria-describedby", "bulk-action-dialog-desc", 1, "modal-backdrop", 3, "click"], ["id", "bulk-action-dialog-title", 1, "modal-title"], ["aria-hidden", "true", 1, "fa-solid", "fa-toggle-on", "text-success"], ["aria-hidden", "true", 1, "fa-solid", "fa-toggle-off", "text-warning"], ["id", "bulk-action-dialog-desc", 1, "modal-body"], [3, "click", "disabled"], [1, "mj-btn", "mj-btn-secondary", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-spinner", "fa-spin"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "bulk-role-dialog-title", "aria-describedby", "bulk-role-dialog-desc", 1, "modal-backdrop", 3, "click"], ["id", "bulk-role-dialog-title", 1, "modal-title"], ["id", "bulk-role-dialog-desc", 1, "modal-body"], [1, "form-field"], ["for", "bulk-role-select", 1, "field-label"], ["id", "bulk-role-select", "aria-label", "Select role to assign", 1, "field-select", 3, "ngModelChange", "ngModel"], [1, "mj-btn", "mj-btn-primary", 3, "click", "disabled"]], template: function UserManagementComponent_Template(rf, ctx) { if (rf & 1) {
|
|
1130
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: UserManagementComponent, selectors: [["mj-user-management"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 84, vars: 31, consts: [[1, "user-management-container"], [1, "sticky-header"], ["role", "toolbar", "aria-label", "User management actions", 1, "action-buttons"], ["aria-label", "Refresh user list", 1, "mj-btn", "mj-btn-secondary", "mj-btn-icon-mobile", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-refresh"], [1, "btn-text"], ["aria-label", "Export users to file", 1, "mj-btn", "mj-btn-secondary", "mj-btn-icon-mobile", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-download"], ["aria-label", "Add new user", 1, "mj-btn", "mj-btn-primary", "mj-btn-icon-mobile", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-plus"], ["role", "toolbar", "aria-label", "Bulk actions", 1, "bulk-action-toolbar"], ["role", "region", "aria-label", "User statistics", 1, "mj-grid-4"], [1, "mj-card"], ["aria-hidden", "true", 1, "stat-icon", "stat-icon-total"], [1, "fa-solid", "fa-users"], [1, "stat-content"], ["aria-label", "Total users count", 1, "stat-value"], [1, "stat-label"], ["aria-hidden", "true", 1, "stat-icon", "stat-icon-active"], [1, "fa-solid", "fa-user-check"], ["aria-label", "Active users count", 1, "stat-value"], ["aria-hidden", "true", 1, "stat-icon", "stat-icon-inactive"], [1, "fa-solid", "fa-user-xmark"], ["aria-label", "Inactive users count", 1, "stat-value"], ["aria-hidden", "true", 1, "stat-icon", "stat-icon-admin"], [1, "fa-solid", "fa-shield-halved"], ["aria-label", "Owner users count", 1, "stat-value"], ["role", "search", "aria-label", "Filter users", 1, "filters-section"], [1, "filters-row"], [1, "mj-search"], ["aria-hidden", "true", 1, "fa-solid", "fa-search", "mj-search-icon"], ["type", "text", "placeholder", "Search users by name or email...", "aria-label", "Search users by name or email", 1, "mj-search-input", 3, "input", "value"], ["aria-label", "Toggle filters", 1, "mobile-filter-toggle", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-filter"], [1, "filter-badge"], [1, "mj-filter-group", "desktop-filter-group"], ["id", "status-filter-label", 1, "mj-filter-label"], ["role", "group", "aria-labelledby", "status-filter-label", 1, "mj-filter-buttons"], [1, "mj-btn", "mj-btn-ghost", 3, "click"], ["for", "role-filter", 1, "mj-filter-label"], ["id", "role-filter", "aria-label", "Filter by role", 1, "mj-filter-select", 3, "change"], ["value", ""], [3, "value"], [1, "mobile-filter-dropdown"], [1, "scrollable-content"], ["role", "status", "aria-live", "polite", "aria-busy", "true", 1, "loading-container"], ["role", "alert", "aria-live", "assertive", 1, "error-container"], [1, "content-area"], [3, "result", "data", "visible"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "delete-dialog-title", "aria-describedby", "delete-dialog-desc", 1, "modal-backdrop"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "bulk-action-dialog-title", "aria-describedby", "bulk-action-dialog-desc", 1, "modal-backdrop"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "bulk-role-dialog-title", "aria-describedby", "bulk-role-dialog-desc", 1, "modal-backdrop"], [1, "bulk-selection-info"], ["aria-hidden", "true", 1, "fa-solid", "fa-check-square"], ["aria-label", "Clear selection", 1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-times"], [1, "bulk-action-buttons"], ["aria-label", "Enable selected users", 1, "mj-btn", "mj-btn-secondary", "mj-btn-sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-toggle-on"], ["aria-label", "Disable selected users", 1, "mj-btn", "mj-btn-secondary", "mj-btn-sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-toggle-off"], ["aria-label", "Assign role to selected users", 1, "mj-btn", "mj-btn-secondary", "mj-btn-sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-user-tag"], ["aria-label", "Delete selected users", 1, "mj-btn", "mj-btn-danger", "mj-btn-sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-trash"], [1, "mobile-filter-content"], [1, "mj-filter-group"], ["id", "status-filter-label-mobile", 1, "mj-filter-label"], ["aria-labelledby", "status-filter-label-mobile", 1, "mj-filter-select", 3, "change", "value"], ["value", "all"], ["value", "active"], ["value", "inactive"], ["for", "role-filter-mobile", 1, "mj-filter-label"], ["id", "role-filter-mobile", "aria-label", "Filter by role", 1, "mj-filter-select", 3, "change"], [1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", "clear-filters-btn"], [1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", "clear-filters-btn", 3, "click"], ["text", "Loading users...", "size", "medium"], [1, "error-content"], ["aria-hidden", "true", 1, "fa-solid", "fa-exclamation-triangle", "error-icon"], [1, "error-message"], ["aria-label", "Retry loading users", 1, "retry-button", 3, "click"], ["role", "list", "aria-label", "Users list", 1, "users-list"], [1, "list-header"], [1, "select-all-label"], ["type", "checkbox", "aria-label", "Select all users", 1, "checkbox", 3, "change", "checked", "indeterminate"], [1, "select-all-text"], [1, "selection-count"], ["role", "listitem", 1, "user-card", 3, "expanded", "selected"], ["role", "status", 1, "empty-state"], ["role", "listitem", 1, "user-card"], ["role", "button", "tabindex", "0", 1, "user-header", 3, "click", "keydown.enter"], [1, "user-selection", 3, "click"], ["type", "checkbox", 1, "checkbox", 3, "change", "checked"], [1, "user-info"], ["aria-hidden", "true", 1, "user-avatar"], [1, "user-details"], [1, "user-name"], [1, "user-email"], [1, "user-meta"], [1, "status-badge"], ["aria-hidden", "true"], [1, "user-actions", "desktop-only", 3, "click"], ["title", "Edit", 1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-edit"], [1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", 3, "click", "title"], ["title", "Delete", 1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", "mj-btn-danger", 3, "click"], ["aria-label", "Toggle user details", 1, "expand-btn"], ["aria-hidden", "true", 1, "fa-solid", "fa-chevron-down"], [1, "user-content"], [1, "mobile-actions-bar", 3, "click"], [1, "mobile-action-buttons"], [1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", 3, "click"], [1, "btn-label"], [1, "mj-btn", "mj-btn-ghost", "mj-btn-sm", "mj-btn-danger", 3, "click"], [1, "user-stats"], [1, "stat-item"], [1, "stat-value"], ["aria-hidden", "true", 1, "fa-solid", "fa-id-card"], ["aria-hidden", "true", 1, "fa-solid", "fa-calendar"], ["aria-hidden", "true", 1, "fa-solid", "fa-clock"], [1, "roles-section"], [1, "section-title"], ["aria-hidden", "true", 1, "fa-solid", "fa-user-shield"], [1, "roles-grid"], [1, "no-roles"], [1, "role-item"], ["aria-hidden", "true", 1, "fa-solid", "fa-shield"], ["aria-hidden", "true", 1, "fa-solid", "fa-users-slash", "empty-icon"], [1, "empty-text"], [1, "empty-subtext"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "delete-dialog-title", "aria-describedby", "delete-dialog-desc", 1, "modal-backdrop", 3, "click"], [1, "modal-dialog", 3, "click"], [1, "modal-header"], ["id", "delete-dialog-title", 1, "modal-title"], ["aria-hidden", "true", 1, "fa-solid", "fa-exclamation-triangle", "text-danger"], ["aria-label", "Close dialog", 1, "modal-close", 3, "click"], ["id", "delete-dialog-desc", 1, "modal-body"], [1, "text-muted"], [1, "modal-footer"], [1, "mj-btn", "mj-btn-danger", 3, "click"], [1, "mj-btn", "mj-btn-secondary", 3, "click"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "bulk-action-dialog-title", "aria-describedby", "bulk-action-dialog-desc", 1, "modal-backdrop", 3, "click"], ["id", "bulk-action-dialog-title", 1, "modal-title"], ["aria-hidden", "true", 1, "fa-solid", "fa-toggle-on", "text-success"], ["aria-hidden", "true", 1, "fa-solid", "fa-toggle-off", "text-warning"], ["id", "bulk-action-dialog-desc", 1, "modal-body"], [3, "click", "disabled"], [1, "mj-btn", "mj-btn-secondary", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-spinner", "fa-spin"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "bulk-role-dialog-title", "aria-describedby", "bulk-role-dialog-desc", 1, "modal-backdrop", 3, "click"], ["id", "bulk-role-dialog-title", 1, "modal-title"], ["id", "bulk-role-dialog-desc", 1, "modal-body"], [1, "form-field"], ["for", "bulk-role-select", 1, "field-label"], ["id", "bulk-role-select", "aria-label", "Select role to assign", 1, "field-select", 3, "ngModelChange", "ngModel"], [1, "mj-btn", "mj-btn-primary", 3, "click", "disabled"]], template: function UserManagementComponent_Template(rf, ctx) { if (rf & 1) {
|
|
1131
1131
|
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "button", 3);
|
|
1132
1132
|
i0.ɵɵlistener("click", function UserManagementComponent_Template_button_click_3_listener() { return ctx.refreshData(); });
|
|
1133
1133
|
i0.ɵɵelement(4, "i", 4);
|
|
@@ -1146,7 +1146,7 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1146
1146
|
i0.ɵɵelementStart(13, "span", 5);
|
|
1147
1147
|
i0.ɵɵtext(14, "Add User");
|
|
1148
1148
|
i0.ɵɵelementEnd()()();
|
|
1149
|
-
i0.ɵɵ
|
|
1149
|
+
i0.ɵɵconditionalCreate(15, UserManagementComponent_Conditional_15_Template, 25, 2, "div", 10);
|
|
1150
1150
|
i0.ɵɵelementStart(16, "div", 11)(17, "div", 12)(18, "div", 13);
|
|
1151
1151
|
i0.ɵɵelement(19, "i", 14);
|
|
1152
1152
|
i0.ɵɵelementEnd();
|
|
@@ -1191,7 +1191,7 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1191
1191
|
i0.ɵɵelementStart(54, "button", 32);
|
|
1192
1192
|
i0.ɵɵlistener("click", function UserManagementComponent_Template_button_click_54_listener() { return ctx.showMobileFilters = !ctx.showMobileFilters; });
|
|
1193
1193
|
i0.ɵɵelement(55, "i", 33);
|
|
1194
|
-
i0.ɵɵ
|
|
1194
|
+
i0.ɵɵconditionalCreate(56, UserManagementComponent_Conditional_56_Template, 2, 1, "span", 34);
|
|
1195
1195
|
i0.ɵɵelementEnd();
|
|
1196
1196
|
i0.ɵɵelementStart(57, "div", 35)(58, "label", 36);
|
|
1197
1197
|
i0.ɵɵtext(59, "Status");
|
|
@@ -1218,15 +1218,19 @@ let UserManagementComponent = class UserManagementComponent extends BaseDashboar
|
|
|
1218
1218
|
i0.ɵɵelementEnd();
|
|
1219
1219
|
i0.ɵɵrepeaterCreate(73, UserManagementComponent_For_74_Template, 2, 2, "option", 42, _forTrack0);
|
|
1220
1220
|
i0.ɵɵelementEnd()()();
|
|
1221
|
-
i0.ɵɵ
|
|
1221
|
+
i0.ɵɵconditionalCreate(75, UserManagementComponent_Conditional_75_Template, 21, 2, "div", 43);
|
|
1222
1222
|
i0.ɵɵelementEnd()();
|
|
1223
1223
|
i0.ɵɵelementStart(76, "div", 44);
|
|
1224
|
-
i0.ɵɵ
|
|
1224
|
+
i0.ɵɵconditionalCreate(77, UserManagementComponent_Conditional_77_Template, 2, 0, "div", 45);
|
|
1225
|
+
i0.ɵɵconditionalCreate(78, UserManagementComponent_Conditional_78_Template, 8, 1, "div", 46);
|
|
1226
|
+
i0.ɵɵconditionalCreate(79, UserManagementComponent_Conditional_79_Template, 11, 5, "div", 47);
|
|
1225
1227
|
i0.ɵɵelementEnd();
|
|
1226
1228
|
i0.ɵɵelementStart(80, "mj-user-dialog", 48);
|
|
1227
1229
|
i0.ɵɵlistener("result", function UserManagementComponent_Template_mj_user_dialog_result_80_listener($event) { return ctx.onUserDialogResult($event); });
|
|
1228
1230
|
i0.ɵɵelementEnd();
|
|
1229
|
-
i0.ɵɵ
|
|
1231
|
+
i0.ɵɵconditionalCreate(81, UserManagementComponent_Conditional_81_Template, 22, 1, "div", 49);
|
|
1232
|
+
i0.ɵɵconditionalCreate(82, UserManagementComponent_Conditional_82_Template, 20, 9, "div", 50);
|
|
1233
|
+
i0.ɵɵconditionalCreate(83, UserManagementComponent_Conditional_83_Template, 25, 6, "div", 51);
|
|
1230
1234
|
i0.ɵɵelementEnd();
|
|
1231
1235
|
} if (rf & 2) {
|
|
1232
1236
|
i0.ɵɵadvance(3);
|
|
@@ -1285,7 +1289,7 @@ UserManagementComponent = __decorate([
|
|
|
1285
1289
|
export { UserManagementComponent };
|
|
1286
1290
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UserManagementComponent, [{
|
|
1287
1291
|
type: Component,
|
|
1288
|
-
args: [{ selector: 'mj-user-management', template: "<div class=\"user-management-container\">\n <!-- Sticky Header Section -->\n <div class=\"sticky-header\">\n <!-- Action Buttons -->\n <div class=\"action-buttons\" role=\"toolbar\" aria-label=\"User management actions\">\n <button\n class=\"mj-btn mj-btn-secondary mj-btn-icon-mobile\"\n (click)=\"refreshData()\"\n [disabled]=\"isLoading\"\n aria-label=\"Refresh user list\"\n >\n <i class=\"fa-solid fa-refresh\" [class.fa-spin]=\"isLoading\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Refresh</span>\n </button>\n <button\n class=\"mj-btn mj-btn-secondary mj-btn-icon-mobile\"\n (click)=\"exportUsers()\"\n aria-label=\"Export users to file\"\n >\n <i class=\"fa-solid fa-download\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Export</span>\n </button>\n <button\n class=\"mj-btn mj-btn-primary mj-btn-icon-mobile\"\n (click)=\"createNewUser()\"\n aria-label=\"Add new user\"\n >\n <i class=\"fa-solid fa-plus\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Add User</span>\n </button>\n </div>\n\n <!-- Bulk Action Toolbar -->\n @if (hasSelection) {\n <div class=\"bulk-action-toolbar\" role=\"toolbar\" aria-label=\"Bulk actions\">\n <div class=\"bulk-selection-info\">\n <i class=\"fa-solid fa-check-square\" aria-hidden=\"true\"></i>\n <span>{{ selectedCount }} user{{ selectedCount > 1 ? 's' : '' }} selected</span>\n <button\n class=\"mj-btn mj-btn-ghost mj-btn-sm\"\n (click)=\"clearSelection()\"\n aria-label=\"Clear selection\"\n >\n <i class=\"fa-solid fa-times\" aria-hidden=\"true\"></i>\n Clear\n </button>\n </div>\n <div class=\"bulk-action-buttons\">\n <button\n class=\"mj-btn mj-btn-secondary mj-btn-sm\"\n (click)=\"confirmBulkAction('enable')\"\n aria-label=\"Enable selected users\"\n >\n <i class=\"fa-solid fa-toggle-on\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Enable</span>\n </button>\n <button\n class=\"mj-btn mj-btn-secondary mj-btn-sm\"\n (click)=\"confirmBulkAction('disable')\"\n aria-label=\"Disable selected users\"\n >\n <i class=\"fa-solid fa-toggle-off\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Disable</span>\n </button>\n <button\n class=\"mj-btn mj-btn-secondary mj-btn-sm\"\n (click)=\"openBulkRoleAssign()\"\n aria-label=\"Assign role to selected users\"\n >\n <i class=\"fa-solid fa-user-tag\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Assign Role</span>\n </button>\n <button\n class=\"mj-btn mj-btn-danger mj-btn-sm\"\n (click)=\"confirmBulkAction('delete')\"\n aria-label=\"Delete selected users\"\n >\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Delete</span>\n </button>\n </div>\n </div>\n }\n\n <!-- Stats Cards -->\n <div class=\"mj-grid-4\" role=\"region\" aria-label=\"User statistics\">\n <div class=\"mj-card\">\n <div class=\"stat-icon stat-icon-total\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-users\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" aria-label=\"Total users count\">{{ stats.totalUsers }}</div>\n <div class=\"stat-label\">Total Users</div>\n </div>\n </div>\n\n <div class=\"mj-card\">\n <div class=\"stat-icon stat-icon-active\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-user-check\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" aria-label=\"Active users count\">{{ stats.activeUsers }}</div>\n <div class=\"stat-label\">Active Users</div>\n </div>\n </div>\n\n <div class=\"mj-card\">\n <div class=\"stat-icon stat-icon-inactive\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-user-xmark\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" aria-label=\"Inactive users count\">{{ stats.inactiveUsers }}</div>\n <div class=\"stat-label\">Inactive Users</div>\n </div>\n </div>\n\n <div class=\"mj-card\">\n <div class=\"stat-icon stat-icon-admin\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" aria-label=\"Owner users count\">{{ stats.adminUsers }}</div>\n <div class=\"stat-label\">Owners</div>\n </div>\n </div>\n </div>\n\n <!-- Filters Section -->\n <div class=\"filters-section\" role=\"search\" aria-label=\"Filter users\">\n <div class=\"filters-row\">\n <!-- Search -->\n <div class=\"mj-search\">\n <i class=\"fa-solid fa-search mj-search-icon\" aria-hidden=\"true\"></i>\n <input\n type=\"text\"\n class=\"mj-search-input\"\n placeholder=\"Search users by name or email...\"\n (input)=\"onSearchChange($event)\"\n [value]=\"filters$.value.search\"\n aria-label=\"Search users by name or email\"\n />\n </div>\n\n <!-- Mobile: Filter Toggle Button -->\n <button\n class=\"mobile-filter-toggle\"\n (click)=\"showMobileFilters = !showMobileFilters\"\n [class.active]=\"showMobileFilters\"\n [attr.aria-expanded]=\"showMobileFilters\"\n aria-label=\"Toggle filters\"\n >\n <i class=\"fa-solid fa-filter\" aria-hidden=\"true\"></i>\n @if (hasActiveFilters) {\n <span class=\"filter-badge\">{{ activeFilterCount }}</span>\n }\n </button>\n\n <!-- Desktop: Status Filter - Button group -->\n <div class=\"mj-filter-group desktop-filter-group\">\n <label class=\"mj-filter-label\" id=\"status-filter-label\">Status</label>\n <div class=\"mj-filter-buttons\" role=\"group\" aria-labelledby=\"status-filter-label\">\n <button\n class=\"mj-btn mj-btn-ghost\"\n [class.mj-btn-primary]=\"filters$.value.status === 'all'\"\n (click)=\"onStatusFilterChange('all')\"\n [attr.aria-pressed]=\"filters$.value.status === 'all'\"\n >\n All\n </button>\n <button\n class=\"mj-btn mj-btn-ghost\"\n [class.mj-btn-primary]=\"filters$.value.status === 'active'\"\n (click)=\"onStatusFilterChange('active')\"\n [attr.aria-pressed]=\"filters$.value.status === 'active'\"\n >\n Active\n </button>\n <button\n class=\"mj-btn mj-btn-ghost\"\n [class.mj-btn-primary]=\"filters$.value.status === 'inactive'\"\n (click)=\"onStatusFilterChange('inactive')\"\n [attr.aria-pressed]=\"filters$.value.status === 'inactive'\"\n >\n Inactive\n </button>\n </div>\n </div>\n\n <!-- Desktop: Role Filter -->\n <div class=\"mj-filter-group desktop-filter-group\">\n <label class=\"mj-filter-label\" for=\"role-filter\">Role</label>\n <select\n id=\"role-filter\"\n class=\"mj-filter-select\"\n (change)=\"onRoleFilterChange($any($event.target).value)\"\n aria-label=\"Filter by role\"\n >\n <option value=\"\">All Roles</option>\n @for (role of roles; track role.ID) {\n <option [value]=\"role.ID\">{{ role.Name }}</option>\n }\n </select>\n </div>\n </div>\n\n <!-- Mobile: Filter Dropdown Panel -->\n @if (showMobileFilters) {\n <div class=\"mobile-filter-dropdown\">\n <div class=\"mobile-filter-content\">\n <!-- Status Filter -->\n <div class=\"mj-filter-group\">\n <label class=\"mj-filter-label\" id=\"status-filter-label-mobile\">Status</label>\n <select\n class=\"mj-filter-select\"\n [value]=\"filters$.value.status\"\n (change)=\"onStatusFilterChange($any($event.target).value)\"\n aria-labelledby=\"status-filter-label-mobile\"\n >\n <option value=\"all\">All Status</option>\n <option value=\"active\">Active</option>\n <option value=\"inactive\">Inactive</option>\n </select>\n </div>\n\n <!-- Role Filter -->\n <div class=\"mj-filter-group\">\n <label class=\"mj-filter-label\" for=\"role-filter-mobile\">Role</label>\n <select\n id=\"role-filter-mobile\"\n class=\"mj-filter-select\"\n (change)=\"onRoleFilterChange($any($event.target).value)\"\n aria-label=\"Filter by role\"\n >\n <option value=\"\">All Roles</option>\n @for (role of roles; track role.ID) {\n <option [value]=\"role.ID\">{{ role.Name }}</option>\n }\n </select>\n </div>\n\n <!-- Clear Filters Button -->\n @if (hasActiveFilters) {\n <button class=\"mj-btn mj-btn-ghost mj-btn-sm clear-filters-btn\" (click)=\"clearFilters()\">\n <i class=\"fa-solid fa-times\" aria-hidden=\"true\"></i>\n Clear Filters\n </button>\n }\n </div>\n </div>\n }\n </div>\n </div><!-- End Sticky Header -->\n\n <!-- Scrollable Content Section -->\n <div class=\"scrollable-content\">\n <!-- Loading State -->\n @if (isLoading) {\n <div class=\"loading-container\" role=\"status\" aria-live=\"polite\" aria-busy=\"true\">\n <mj-loading text=\"Loading users...\" size=\"medium\"></mj-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (error && !isLoading) {\n <div class=\"error-container\" role=\"alert\" aria-live=\"assertive\">\n <div class=\"error-content\">\n <i class=\"fa-solid fa-exclamation-triangle error-icon\" aria-hidden=\"true\"></i>\n <p class=\"error-message\">{{ error }}</p>\n <button class=\"retry-button\" (click)=\"loadInitialData()\" aria-label=\"Retry loading users\">\n <i class=\"fa-solid fa-refresh\" aria-hidden=\"true\"></i>\n Try Again\n </button>\n </div>\n </div>\n }\n\n <!-- Content Area -->\n @if (!isLoading && !error) {\n <div class=\"content-area\">\n <!-- Users List -->\n <div class=\"users-list\" role=\"list\" aria-label=\"Users list\">\n <!-- List Header with Select All -->\n <div class=\"list-header\">\n <label class=\"select-all-label\">\n <input\n type=\"checkbox\"\n class=\"checkbox\"\n [checked]=\"isAllSelected\"\n [indeterminate]=\"isIndeterminate\"\n (change)=\"toggleSelectAll()\"\n aria-label=\"Select all users\"\n />\n <span class=\"select-all-text\">Select All ({{ filteredUsers.length }})</span>\n </label>\n @if (hasSelection) {\n <span class=\"selection-count\">{{ selectedCount }} selected</span>\n }\n </div>\n\n @for (user of filteredUsers; track user.ID) {\n <div\n class=\"user-card\"\n [class.expanded]=\"isUserExpanded(user.ID)\"\n [class.selected]=\"isUserSelected(user.ID)\"\n role=\"listitem\"\n >\n <!-- User Card Header - Always Visible -->\n <div\n class=\"user-header\"\n (click)=\"toggleUserExpansion(user.ID)\"\n role=\"button\"\n [attr.aria-expanded]=\"isUserExpanded(user.ID)\"\n tabindex=\"0\"\n (keydown.enter)=\"toggleUserExpansion(user.ID)\"\n >\n <!-- Selection Checkbox -->\n <div class=\"user-selection\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n class=\"checkbox\"\n [checked]=\"isUserSelected(user.ID)\"\n (change)=\"toggleUserSelection(user.ID, $event)\"\n [attr.aria-label]=\"'Select ' + user.Name\"\n />\n </div>\n\n <!-- User Info -->\n <div class=\"user-info\">\n <div class=\"user-avatar\" aria-hidden=\"true\">\n {{ getUserInitials(user) }}\n </div>\n <div class=\"user-details\">\n <h3 class=\"user-name\">{{ user.Name }}</h3>\n <p class=\"user-email\">{{ user.Email }}</p>\n </div>\n </div>\n\n <!-- User Meta -->\n <div class=\"user-meta\">\n <span class=\"status-badge\" [class]=\"getStatusClass(user)\">\n <i [class]=\"'fa-solid ' + getStatusIcon(user)\" aria-hidden=\"true\"></i>\n {{ user.IsActive ? 'Active' : 'Inactive' }}\n </span>\n <div class=\"user-actions desktop-only\" (click)=\"$event.stopPropagation()\">\n <button\n class=\"mj-btn mj-btn-ghost mj-btn-sm\"\n (click)=\"editUser(user)\"\n [attr.aria-label]=\"'Edit ' + user.Name\"\n title=\"Edit\"\n >\n <i class=\"fa-solid fa-edit\" aria-hidden=\"true\"></i>\n </button>\n <button\n class=\"mj-btn mj-btn-ghost mj-btn-sm\"\n (click)=\"toggleUserStatus(user)\"\n [attr.aria-label]=\"(user.IsActive ? 'Deactivate ' : 'Activate ') + user.Name\"\n [title]=\"user.IsActive ? 'Deactivate' : 'Activate'\"\n >\n <i [class]=\"user.IsActive ? 'fa-solid fa-toggle-on' : 'fa-solid fa-toggle-off'\" aria-hidden=\"true\"></i>\n </button>\n <button\n class=\"mj-btn mj-btn-ghost mj-btn-sm mj-btn-danger\"\n (click)=\"confirmDeleteUser(user)\"\n [attr.aria-label]=\"'Delete ' + user.Name\"\n title=\"Delete\"\n >\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <button class=\"expand-btn\" aria-label=\"Toggle user details\">\n <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n\n <!-- User Card Content - Expanded -->\n @if (isUserExpanded(user.ID)) {\n <div class=\"user-content\">\n <!-- Mobile Actions Bar -->\n <div class=\"mobile-actions-bar\" (click)=\"$event.stopPropagation()\">\n <span class=\"status-badge\" [class]=\"getStatusClass(user)\">\n <i [class]=\"'fa-solid ' + getStatusIcon(user)\" aria-hidden=\"true\"></i>\n {{ user.IsActive ? 'Active' : 'Inactive' }}\n </span>\n <div class=\"mobile-action-buttons\">\n <button\n class=\"mj-btn mj-btn-ghost mj-btn-sm\"\n (click)=\"editUser(user)\"\n [attr.aria-label]=\"'Edit ' + user.Name\"\n >\n <i class=\"fa-solid fa-edit\" aria-hidden=\"true\"></i>\n <span class=\"btn-label\">Edit</span>\n </button>\n <button\n class=\"mj-btn mj-btn-ghost mj-btn-sm\"\n (click)=\"toggleUserStatus(user)\"\n [attr.aria-label]=\"(user.IsActive ? 'Deactivate ' : 'Activate ') + user.Name\"\n >\n <i [class]=\"user.IsActive ? 'fa-solid fa-toggle-on' : 'fa-solid fa-toggle-off'\" aria-hidden=\"true\"></i>\n <span class=\"btn-label\">{{ user.IsActive ? 'Disable' : 'Enable' }}</span>\n </button>\n <button\n class=\"mj-btn mj-btn-ghost mj-btn-sm mj-btn-danger\"\n (click)=\"confirmDeleteUser(user)\"\n [attr.aria-label]=\"'Delete ' + user.Name\"\n >\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n <span class=\"btn-label\">Delete</span>\n </button>\n </div>\n </div>\n\n <!-- User Stats -->\n <div class=\"user-stats\">\n <div class=\"stat-item\">\n <i class=\"fa-solid fa-user-tag\" aria-hidden=\"true\"></i>\n <span class=\"stat-label\">Type:</span>\n <span class=\"stat-value\">\n <i [class]=\"'fa-solid ' + getUserTypeIcon(user)\" aria-hidden=\"true\"></i>\n {{ user.Type }}\n </span>\n </div>\n <div class=\"stat-item\">\n <i class=\"fa-solid fa-id-card\" aria-hidden=\"true\"></i>\n <span class=\"stat-label\">Full Name:</span>\n <span class=\"stat-value\">{{ user.FirstName }} {{ user.LastName }}</span>\n </div>\n <div class=\"stat-item\">\n <i class=\"fa-solid fa-calendar\" aria-hidden=\"true\"></i>\n <span class=\"stat-label\">Created:</span>\n <span class=\"stat-value\">{{ user.__mj_CreatedAt | date:'short' }}</span>\n </div>\n <div class=\"stat-item\">\n <i class=\"fa-solid fa-clock\" aria-hidden=\"true\"></i>\n <span class=\"stat-label\">Updated:</span>\n <span class=\"stat-value\">{{ user.__mj_UpdatedAt | date:'short' }}</span>\n </div>\n </div>\n\n <!-- User Roles Section -->\n <div class=\"roles-section\">\n <h4 class=\"section-title\">\n <i class=\"fa-solid fa-user-shield\" aria-hidden=\"true\"></i>\n Assigned Roles\n </h4>\n @if (getUserRoles(user.ID).length > 0) {\n <div class=\"roles-grid\">\n @for (role of getUserRoles(user.ID); track role.ID) {\n <div class=\"role-item\">\n <i class=\"fa-solid fa-shield\" aria-hidden=\"true\"></i>\n {{ role.Name }}\n </div>\n }\n </div>\n } @else {\n <p class=\"no-roles\">No roles assigned to this user</p>\n }\n </div>\n </div>\n }\n </div>\n }\n\n @if (filteredUsers.length === 0) {\n <div class=\"empty-state\" role=\"status\">\n <i class=\"fa-solid fa-users-slash empty-icon\" aria-hidden=\"true\"></i>\n <p class=\"empty-text\">No users found</p>\n <p class=\"empty-subtext\">Try adjusting your filters or add a new user</p>\n </div>\n }\n </div>\n </div>\n }\n </div><!-- End Scrollable Content -->\n\n <!-- User Create/Edit Dialog -->\n <mj-user-dialog\n [data]=\"userDialogData\"\n [visible]=\"showUserDialog\"\n (result)=\"onUserDialogResult($event)\">\n </mj-user-dialog>\n\n <!-- Delete Confirmation Dialog -->\n @if (showDeleteConfirm && selectedUser) {\n <div\n class=\"modal-backdrop\"\n (click)=\"showDeleteConfirm = false\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"delete-dialog-title\"\n aria-describedby=\"delete-dialog-desc\"\n >\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3 class=\"modal-title\" id=\"delete-dialog-title\">\n <i class=\"fa-solid fa-exclamation-triangle text-danger\" aria-hidden=\"true\"></i>\n Confirm Delete\n </h3>\n <button\n class=\"modal-close\"\n (click)=\"showDeleteConfirm = false\"\n aria-label=\"Close dialog\"\n >\n <i class=\"fa-solid fa-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <div class=\"modal-body\" id=\"delete-dialog-desc\">\n <p>Are you sure you want to delete user <strong>{{ selectedUser.Name }}</strong>?</p>\n <p class=\"text-muted\">This action cannot be undone.</p>\n </div>\n <div class=\"modal-footer\">\n <button class=\"mj-btn mj-btn-danger\" (click)=\"deleteUser()\">\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n Delete User\n </button>\n <button class=\"mj-btn mj-btn-secondary\" (click)=\"showDeleteConfirm = false\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Bulk Action Confirmation Dialog -->\n @if (showBulkActionConfirm) {\n <div\n class=\"modal-backdrop\"\n (click)=\"cancelBulkAction()\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"bulk-action-dialog-title\"\n aria-describedby=\"bulk-action-dialog-desc\"\n >\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3 class=\"modal-title\" id=\"bulk-action-dialog-title\">\n @if (bulkActionType === 'delete') {\n <i class=\"fa-solid fa-exclamation-triangle text-danger\" aria-hidden=\"true\"></i>\n } @else if (bulkActionType === 'enable') {\n <i class=\"fa-solid fa-toggle-on text-success\" aria-hidden=\"true\"></i>\n } @else {\n <i class=\"fa-solid fa-toggle-off text-warning\" aria-hidden=\"true\"></i>\n }\n {{ getBulkActionTitle() }}\n </h3>\n <button\n class=\"modal-close\"\n (click)=\"cancelBulkAction()\"\n aria-label=\"Close dialog\"\n >\n <i class=\"fa-solid fa-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <div class=\"modal-body\" id=\"bulk-action-dialog-desc\">\n <p>{{ getBulkActionMessage() }}</p>\n @if (bulkActionType === 'delete') {\n <p class=\"text-muted\">This action cannot be undone.</p>\n }\n </div>\n <div class=\"modal-footer\">\n <button\n [class]=\"bulkActionType === 'delete' ? 'mj-btn mj-btn-danger' : 'mj-btn mj-btn-primary'\"\n (click)=\"executeBulkAction()\"\n [disabled]=\"isLoading\"\n >\n @if (isLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n Processing...\n } @else {\n @if (bulkActionType === 'delete') {\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n } @else if (bulkActionType === 'enable') {\n <i class=\"fa-solid fa-toggle-on\" aria-hidden=\"true\"></i>\n } @else {\n <i class=\"fa-solid fa-toggle-off\" aria-hidden=\"true\"></i>\n }\n {{ getBulkActionButtonText() }}\n }\n </button>\n <button class=\"mj-btn mj-btn-secondary\" (click)=\"cancelBulkAction()\" [disabled]=\"isLoading\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Bulk Role Assignment Dialog -->\n @if (showBulkRoleAssign) {\n <div\n class=\"modal-backdrop\"\n (click)=\"cancelBulkRoleAssign()\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"bulk-role-dialog-title\"\n aria-describedby=\"bulk-role-dialog-desc\"\n >\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3 class=\"modal-title\" id=\"bulk-role-dialog-title\">\n <i class=\"fa-solid fa-user-tag\" aria-hidden=\"true\"></i>\n Assign Role to {{ selectedCount }} User{{ selectedCount > 1 ? 's' : '' }}\n </h3>\n <button\n class=\"modal-close\"\n (click)=\"cancelBulkRoleAssign()\"\n aria-label=\"Close dialog\"\n >\n <i class=\"fa-solid fa-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <div class=\"modal-body\" id=\"bulk-role-dialog-desc\">\n <p>Select a role to assign to the selected users:</p>\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"bulk-role-select\">Role</label>\n <select\n id=\"bulk-role-select\"\n class=\"field-select\"\n [(ngModel)]=\"bulkRoleId\"\n aria-label=\"Select role to assign\"\n >\n <option value=\"\">Select a role...</option>\n @for (role of roles; track role.ID) {\n <option [value]=\"role.ID\">{{ role.Name }}</option>\n }\n </select>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button\n class=\"mj-btn mj-btn-primary\"\n (click)=\"executeBulkRoleAssign()\"\n [disabled]=\"!bulkRoleId || isLoading\"\n >\n @if (isLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n Assigning...\n } @else {\n <i class=\"fa-solid fa-user-tag\" aria-hidden=\"true\"></i>\n Assign Role\n }\n </button>\n <button class=\"mj-btn mj-btn-secondary\" (click)=\"cancelBulkRoleAssign()\" [disabled]=\"isLoading\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: ["/* =============================================================================\n User Management Component - MD3 Design System\n Unified Expandable Card List Pattern\n ============================================================================= */\n\n/* -----------------------------------------------------------------------------\n MD3 Design Tokens (Local)\n ----------------------------------------------------------------------------- */\n:host {\n /* Primary - Deep Blue */\n --md-primary: #0076B6;\n --md-on-primary: #FFFFFF;\n --md-primary-container: #AAE7FD;\n --md-on-primary-container: #001F2A;\n\n /* Secondary - Light Orange */\n --md-secondary: #F5A623;\n --md-on-secondary: #FFFFFF;\n --md-secondary-container: #FFECD6;\n --md-on-secondary-container: #2D1600;\n\n /* Tertiary - Light Green */\n --md-tertiary: #4CAF50;\n --md-on-tertiary: #FFFFFF;\n --md-tertiary-container: #C8E6C9;\n --md-on-tertiary-container: #002204;\n\n /* Error - Red */\n --md-error: #D32F2F;\n --md-on-error: #FFFFFF;\n --md-error-container: #FFCDD2;\n --md-on-error-container: #410002;\n\n /* Surface Colors */\n --md-surface: #FAFCFF;\n --md-surface-container-lowest: #FFFFFF;\n --md-surface-container-low: #F3F5F9;\n --md-surface-container: #EDF0F4;\n --md-surface-container-high: #E7EAEE;\n --md-surface-container-highest: #E1E3E8;\n --md-on-surface: #191C20;\n --md-on-surface-variant: #43474E;\n --md-outline: #74777F;\n --md-outline-variant: #C4C6D0;\n\n /* Elevation */\n --md-elevation-1: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08);\n --md-elevation-2: 0 2px 4px rgba(0, 0, 0, 0.1), 0 4px 8px rgba(0, 0, 0, 0.08);\n --md-elevation-3: 0 4px 8px rgba(0, 0, 0, 0.1), 0 8px 16px rgba(0, 0, 0, 0.08);\n --md-elevation-4: 0 6px 12px rgba(0, 0, 0, 0.1), 0 12px 24px rgba(0, 0, 0, 0.08);\n --md-elevation-5: 0 8px 16px rgba(0, 0, 0, 0.12), 0 16px 32px rgba(0, 0, 0, 0.1);\n\n /* Corner Radii */\n --md-corner-extra-small: 4px;\n --md-corner-small: 8px;\n --md-corner-medium: 12px;\n --md-corner-large: 16px;\n --md-corner-extra-large: 28px;\n --md-corner-full: 9999px;\n\n /* Host Layout */\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n overflow: hidden;\n}\n\n/* -----------------------------------------------------------------------------\n Container & Layout\n ----------------------------------------------------------------------------- */\n.user-management-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n position: relative;\n width: 100%;\n background: var(--md-surface);\n}\n\n/* -----------------------------------------------------------------------------\n Sticky Header - Always visible controls\n ----------------------------------------------------------------------------- */\n.sticky-header {\n flex-shrink: 0;\n background: var(--md-surface);\n border-bottom: 1px solid var(--md-outline-variant);\n box-shadow: var(--md-elevation-1);\n z-index: 10;\n}\n\n/* -----------------------------------------------------------------------------\n Scrollable Content - Users list area\n ----------------------------------------------------------------------------- */\n.scrollable-content {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n -webkit-overflow-scrolling: touch;\n padding: 1rem;\n background: var(--md-surface-container-lowest);\n}\n\n@media (min-width: 768px) {\n .scrollable-content {\n padding: 1.5rem 2rem;\n }\n}\n\n@media (min-width: 1024px) {\n .scrollable-content {\n padding: 2rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Action Buttons (In Sticky Header)\n ----------------------------------------------------------------------------- */\n.action-buttons {\n flex-shrink: 0;\n display: flex;\n gap: 0.75rem;\n justify-content: flex-end;\n padding: 0.75rem 1rem;\n background: var(--md-surface);\n}\n\n@media (min-width: 768px) {\n .action-buttons {\n padding: 1rem 1.5rem;\n }\n}\n\n@media (max-width: 639px) {\n .action-buttons {\n justify-content: center;\n flex-wrap: wrap;\n }\n\n .mj-btn-icon-mobile .btn-text {\n display: none;\n }\n\n .mj-btn-icon-mobile {\n width: 48px;\n height: 48px;\n padding: 0;\n border-radius: var(--md-corner-full);\n min-width: 48px;\n min-height: 48px;\n }\n\n .mj-btn-icon-mobile i {\n font-size: 1.25rem;\n }\n\n .mj-btn-primary.mj-btn-icon-mobile {\n box-shadow: var(--md-elevation-2);\n }\n\n .mj-btn-primary.mj-btn-icon-mobile:hover {\n box-shadow: var(--md-elevation-3);\n transform: scale(1.05);\n }\n\n .mj-btn-secondary.mj-btn-icon-mobile {\n background: var(--md-surface);\n border: 1px solid var(--md-outline);\n }\n\n .mj-btn-secondary.mj-btn-icon-mobile:hover {\n background: var(--md-primary);\n color: var(--md-on-primary);\n border-color: var(--md-primary);\n }\n}\n\n/* -----------------------------------------------------------------------------\n Bulk Action Toolbar\n ----------------------------------------------------------------------------- */\n.bulk-action-toolbar {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n padding: 0.75rem 1rem;\n background: var(--md-primary-container);\n border-bottom: 1px solid var(--md-outline-variant);\n animation: slideDown 0.2s ease;\n}\n\n@media (min-width: 768px) {\n .bulk-action-toolbar {\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n padding: 1rem 1.5rem;\n }\n}\n\n.bulk-selection-info {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--md-on-primary-container);\n}\n\n.bulk-selection-info i {\n font-size: 1.25rem;\n}\n\n.bulk-action-buttons {\n display: flex;\n gap: 0.5rem;\n flex-wrap: wrap;\n}\n\n@media (max-width: 639px) {\n .bulk-action-buttons .btn-text {\n display: none;\n }\n\n .bulk-action-buttons .mj-btn-sm {\n padding: 0.5rem;\n min-width: 36px;\n }\n}\n\n@keyframes slideDown {\n from {\n transform: translateY(-10px);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Statistics Cards - Static Display (Non-interactive)\n ----------------------------------------------------------------------------- */\n.mj-grid-4 {\n display: none;\n grid-template-columns: repeat(2, 1fr);\n gap: 0.5rem;\n padding: 0 1rem 0.75rem 1rem;\n background: var(--md-surface);\n}\n\n@media (min-width: 768px) {\n .mj-grid-4 {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n padding: 0 1.5rem 1rem 1.5rem;\n gap: 0.75rem;\n }\n}\n\n@media (min-width: 1024px) {\n .mj-grid-4 {\n gap: 1rem;\n }\n}\n\n/* Static Card - No hover effects */\n.mj-card {\n background: var(--md-surface-container-low);\n border-radius: var(--md-corner-medium);\n padding: 1rem;\n box-shadow: none;\n display: flex;\n align-items: center;\n gap: 1rem;\n border: 1px solid var(--md-outline-variant);\n cursor: default;\n pointer-events: none;\n}\n\n@media (min-width: 768px) {\n .mj-card {\n padding: 1rem 1.25rem;\n }\n}\n\n.stat-icon {\n width: 48px;\n height: 48px;\n border-radius: var(--md-corner-medium);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.25rem;\n flex-shrink: 0;\n}\n\n@media (min-width: 768px) {\n .stat-icon {\n width: 48px;\n height: 48px;\n font-size: 1.25rem;\n }\n}\n\n.stat-icon-total {\n background: var(--md-primary-container);\n color: var(--md-primary);\n}\n\n.stat-icon-active {\n background: var(--md-tertiary-container);\n color: var(--md-tertiary);\n}\n\n.stat-icon-inactive {\n background: var(--md-error-container);\n color: var(--md-error);\n}\n\n.stat-icon-admin {\n background: var(--md-secondary-container);\n color: #B5751A;\n}\n\n.stat-content {\n flex: 1;\n min-width: 0;\n}\n\n.stat-content .stat-value {\n font-size: 1.75rem;\n font-weight: 700;\n color: var(--md-on-surface);\n line-height: 1;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 768px) {\n .stat-content .stat-value {\n font-size: 2rem;\n }\n}\n\n.stat-content .stat-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--md-on-surface-variant);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin-top: 0.25rem;\n}\n\n@media (min-width: 768px) {\n .stat-content .stat-label {\n font-size: 0.8125rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Filters Section\n ----------------------------------------------------------------------------- */\n.filters-section {\n flex-shrink: 0;\n background: var(--md-surface-container);\n margin: 0 1rem 0.75rem 1rem;\n padding: 0.75rem;\n border-radius: var(--md-corner-medium);\n border: 1px solid var(--md-outline-variant);\n}\n\n@media (min-width: 768px) {\n .filters-section {\n margin: 0 1.5rem 1rem 1.5rem;\n padding: 1rem 1.25rem;\n }\n}\n\n.filters-row {\n display: flex;\n gap: 1rem;\n align-items: flex-end;\n flex-wrap: wrap;\n}\n\n@media (min-width: 768px) {\n .filters-row {\n gap: 1.5rem;\n }\n}\n\n@media (max-width: 767px) {\n .filters-section {\n padding: 0.75rem;\n }\n\n .filters-row {\n display: flex;\n flex-direction: row;\n gap: 0.75rem;\n align-items: center;\n }\n\n .filters-row .mj-search {\n flex: 1;\n min-width: 0;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Search Input\n ----------------------------------------------------------------------------- */\n.mj-search {\n position: relative;\n flex: 1;\n min-width: 200px;\n max-width: 100%;\n}\n\n@media (min-width: 640px) {\n .mj-search {\n min-width: 280px;\n max-width: 500px;\n }\n}\n\n@media (min-width: 768px) {\n .mj-search {\n min-width: 320px;\n max-width: 600px;\n }\n}\n\n@media (max-width: 767px) {\n .mj-search {\n width: 100%;\n max-width: none;\n min-width: 0;\n }\n}\n\n.mj-search .mj-search-icon {\n position: absolute;\n left: 1rem;\n top: 50%;\n transform: translateY(-50%);\n color: var(--md-on-surface-variant);\n font-size: 1rem;\n pointer-events: none;\n transition: color 0.2s ease;\n}\n\n.mj-search .mj-search-input {\n width: 100%;\n padding: 0.875rem 1rem 0.875rem 2.75rem;\n border: 2px solid var(--md-outline-variant);\n border-radius: var(--md-corner-full);\n font-size: 1rem;\n background: var(--md-surface);\n color: var(--md-on-surface);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 48px;\n}\n\n@media (min-width: 768px) {\n .mj-search .mj-search-input {\n padding: 1rem 1.25rem 1rem 3rem;\n min-height: 52px;\n font-size: 1.0625rem;\n }\n\n .mj-search .mj-search-icon {\n left: 1.125rem;\n font-size: 1.125rem;\n }\n}\n\n.mj-search .mj-search-input::placeholder {\n color: var(--md-on-surface-variant);\n}\n\n.mj-search .mj-search-input:hover {\n border-color: var(--md-primary);\n background: var(--md-surface-container-lowest);\n}\n\n.mj-search .mj-search-input:focus {\n outline: none;\n border-color: var(--md-primary);\n box-shadow: 0 0 0 3px rgba(0, 118, 182, 0.2);\n background: var(--md-surface);\n}\n\n.mj-search:focus-within .mj-search-icon {\n color: var(--md-primary);\n}\n\n/* -----------------------------------------------------------------------------\n Mobile Filter Toggle Button\n ----------------------------------------------------------------------------- */\n.mobile-filter-toggle {\n display: none;\n align-items: center;\n justify-content: center;\n position: relative;\n width: 48px;\n height: 48px;\n min-width: 48px;\n border: 1px solid var(--md-outline-variant);\n border-radius: var(--md-corner-small);\n background: var(--md-surface-container-low);\n color: var(--md-on-surface-variant);\n font-size: 1.125rem;\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n box-shadow: var(--md-elevation-1);\n flex-shrink: 0;\n}\n\n.mobile-filter-toggle:hover {\n background: var(--md-surface-container);\n border-color: var(--md-primary);\n color: var(--md-primary);\n}\n\n.mobile-filter-toggle.active {\n background: var(--md-primary);\n color: var(--md-on-primary);\n border-color: var(--md-primary);\n}\n\n.filter-badge {\n position: absolute;\n top: -4px;\n right: -4px;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n border-radius: var(--md-corner-full);\n background: var(--md-error);\n color: var(--md-on-error);\n font-size: 0.6875rem;\n font-weight: 700;\n display: flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n}\n\n/* -----------------------------------------------------------------------------\n Mobile Filter Dropdown Panel\n ----------------------------------------------------------------------------- */\n.mobile-filter-dropdown {\n display: none;\n margin-top: 0.75rem;\n padding-top: 0.75rem;\n border-top: 1px solid var(--md-outline-variant);\n animation: slideDown 0.2s ease;\n}\n\n/* Mobile only - show filter toggle and dropdown */\n@media (max-width: 767px) {\n .mobile-filter-toggle {\n display: flex !important;\n }\n\n .mobile-filter-dropdown {\n display: block;\n }\n\n .desktop-filter-group {\n display: none !important;\n }\n}\n\n.mobile-filter-content {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.mobile-filter-content .mj-filter-group {\n width: 100%;\n}\n\n.mobile-filter-content .mj-filter-select {\n width: 100%;\n}\n\n.clear-filters-btn {\n align-self: flex-start;\n margin-top: 0.25rem;\n}\n\n/* -----------------------------------------------------------------------------\n Filter Controls - Mobile/Desktop visibility\n ----------------------------------------------------------------------------- */\n\n/* Desktop filter groups - hidden by default, shown on desktop */\n.desktop-filter-group {\n display: none !important;\n}\n\n/* Desktop only - show filter groups, hide mobile toggle */\n@media screen and (min-width: 768px) {\n .desktop-filter-group {\n display: flex !important;\n flex-direction: column;\n gap: 0.375rem;\n }\n\n .mobile-filter-toggle {\n display: none !important;\n }\n\n .mobile-filter-dropdown {\n display: none !important;\n }\n}\n\n.mj-filter-group {\n display: flex;\n flex-direction: column;\n gap: 0.375rem;\n}\n\n@media (max-width: 767px) {\n .mj-filter-group {\n flex: 1;\n min-width: 100px;\n }\n}\n\n.mj-filter-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--md-on-surface);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.mj-filter-buttons {\n display: flex;\n background: var(--md-surface-container);\n border-radius: var(--md-corner-full);\n padding: 4px;\n gap: 2px;\n}\n\n.mj-filter-buttons .mj-btn {\n border-radius: var(--md-corner-full);\n}\n\n.mj-filter-buttons .mj-btn-primary {\n background-color: var(--md-primary);\n color: var(--md-on-primary);\n box-shadow: var(--md-elevation-1);\n}\n\n.mj-filter-select {\n padding: 0.625rem 1rem;\n border: 1px solid var(--md-outline-variant);\n border-radius: var(--md-corner-small);\n background: var(--md-surface-container-low);\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--md-on-surface);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-width: 120px;\n box-shadow: var(--md-elevation-1);\n}\n\n.mj-filter-select:hover {\n background: var(--md-surface-container);\n border-color: var(--md-primary);\n}\n\n@media (max-width: 767px) {\n .mj-filter-select {\n width: 100%;\n padding: 0.5rem 0.75rem;\n font-size: 0.8125rem;\n }\n}\n\n.mj-filter-select:focus {\n outline: none;\n border-color: var(--md-primary);\n box-shadow: 0 0 0 3px rgba(0, 118, 182, 0.2);\n}\n\n/* -----------------------------------------------------------------------------\n Content Area\n ----------------------------------------------------------------------------- */\n.content-area {\n flex: 1 1 auto;\n overflow: visible;\n position: relative;\n background: var(--md-surface);\n border-radius: var(--md-corner-large);\n box-shadow: var(--md-elevation-1);\n padding: 1rem;\n border: 1px solid var(--md-outline-variant);\n}\n\n@media (min-width: 768px) {\n .content-area {\n padding: 1.5rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Users List - Unified Expandable Card Pattern\n ----------------------------------------------------------------------------- */\n.users-list {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n/* List Header with Select All */\n.list-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.75rem 1rem;\n background: var(--md-surface-container);\n border-radius: var(--md-corner-small);\n border: 1px solid var(--md-outline-variant);\n margin-bottom: 0.5rem;\n}\n\n.select-all-label {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n cursor: pointer;\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--md-on-surface);\n}\n\n.select-all-text {\n font-weight: 500;\n}\n\n.selection-count {\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--md-primary);\n background: var(--md-primary-container);\n padding: 0.25rem 0.75rem;\n border-radius: var(--md-corner-full);\n}\n\n/* User Card - Interactive */\n.user-card {\n background: var(--md-surface);\n border: 1px solid var(--md-outline-variant);\n border-radius: var(--md-corner-large);\n overflow: hidden;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.user-card:hover {\n box-shadow: var(--md-elevation-2);\n border-color: var(--md-primary);\n}\n\n.user-card.expanded {\n box-shadow: var(--md-elevation-3);\n border-color: var(--md-primary);\n}\n\n.user-card.selected {\n background: var(--md-primary-container);\n border-color: var(--md-primary);\n}\n\n.user-card.selected .user-header {\n background: transparent;\n}\n\n.user-card.selected:hover .user-header {\n background: rgba(0, 118, 182, 0.08);\n}\n\n/* User Card Header */\n.user-header {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 1.25rem 1.5rem;\n cursor: pointer;\n background: var(--md-surface-container-lowest);\n transition: background-color 0.2s ease;\n}\n\n.user-header:hover {\n background: var(--md-surface-container-low);\n}\n\n@media (max-width: 639px) {\n .user-header {\n padding: 0.875rem 1rem;\n gap: 0.625rem;\n }\n}\n\n/* User Selection */\n.user-selection {\n flex-shrink: 0;\n}\n\n.checkbox {\n width: 20px;\n height: 20px;\n cursor: pointer;\n accent-color: var(--md-primary);\n}\n\n/* User Info */\n.user-info {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n flex: 1;\n min-width: 0;\n}\n\n.user-avatar {\n width: 48px;\n height: 48px;\n border-radius: var(--md-corner-full);\n background: linear-gradient(135deg, var(--md-primary) 0%, #3395C8 100%);\n color: var(--md-on-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 0.875rem;\n font-weight: 600;\n flex-shrink: 0;\n}\n\n@media (max-width: 639px) {\n .user-avatar {\n width: 40px;\n height: 40px;\n font-size: 0.8125rem;\n }\n\n .user-details {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n }\n\n .user-name {\n font-size: 0.9375rem;\n font-weight: 600;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .user-email {\n font-size: 0.8125rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n}\n\n.user-details {\n flex: 1;\n min-width: 0;\n}\n\n.user-name {\n font-size: 1rem;\n font-weight: 600;\n color: var(--md-on-surface);\n margin: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.user-email {\n font-size: 0.875rem;\n color: var(--md-on-surface-variant);\n margin: 0.125rem 0 0 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* User Meta */\n.user-meta {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n flex-shrink: 0;\n}\n\n@media (max-width: 767px) {\n .user-meta .status-badge,\n .user-meta .user-actions {\n display: none !important;\n }\n\n .user-meta {\n gap: 0;\n }\n}\n\n/* Desktop Actions */\n.desktop-only {\n display: none;\n}\n\n@media (min-width: 768px) {\n .desktop-only {\n display: flex;\n gap: 0.25rem;\n }\n}\n\n.user-actions {\n display: flex;\n gap: 0.25rem;\n}\n\n/* Expand Button */\n.expand-btn {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--md-on-surface-variant);\n font-size: 1rem;\n cursor: pointer;\n transition: all 0.2s ease;\n border-radius: var(--md-corner-full);\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.expand-btn:hover {\n background: var(--md-primary);\n color: var(--md-on-primary);\n}\n\n.expand-btn i {\n transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1);\n}\n\n.user-card.expanded .expand-btn i {\n transform: rotate(180deg);\n}\n\n/* User Card Content - Expanded */\n.user-content {\n padding: 1.25rem 1.5rem;\n background: var(--md-surface);\n border-top: 1px solid var(--md-outline-variant);\n animation: slideDown 0.3s cubic-bezier(0, 0, 0.2, 1);\n}\n\n/* Mobile Actions Bar */\n.mobile-actions-bar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: 1rem;\n margin-bottom: 1rem;\n border-bottom: 1px solid var(--md-outline-variant);\n}\n\n@media (min-width: 768px) {\n .mobile-actions-bar {\n display: none;\n }\n}\n\n.mobile-action-buttons {\n display: flex;\n gap: 0.5rem;\n}\n\n.mobile-action-buttons .btn-label {\n margin-left: 0.375rem;\n}\n\n/* Status Badge */\n.status-badge {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.875rem;\n border-radius: var(--md-corner-full);\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.status-badge.status-active {\n background: var(--md-tertiary-container);\n color: #1B5E20;\n border: 1px solid var(--md-tertiary);\n}\n\n.status-badge.status-inactive {\n background: var(--md-error-container);\n color: var(--md-on-error-container);\n border: 1px solid var(--md-error);\n}\n\n.status-badge i {\n font-size: 0.625rem;\n}\n\n/* User Stats in Expanded Content */\n.user-stats {\n display: flex;\n flex-wrap: wrap;\n gap: 1rem;\n margin-bottom: 1rem;\n}\n\n.user-stats .stat-item {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-size: 0.875rem;\n color: var(--md-on-surface-variant);\n}\n\n.user-stats .stat-item i {\n color: var(--md-primary);\n font-size: 0.875rem;\n}\n\n.user-stats .stat-label {\n color: var(--md-on-surface-variant);\n}\n\n.user-stats .stat-value {\n color: var(--md-on-surface);\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.user-stats .stat-value i {\n font-size: 0.75rem;\n}\n\n/* Roles Section */\n.roles-section {\n padding-top: 1rem;\n border-top: 1px solid var(--md-outline-variant);\n}\n\n.section-title {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n font-size: 1rem;\n font-weight: 600;\n color: var(--md-on-surface);\n margin: 0 0 0.75rem 0;\n}\n\n.section-title i {\n color: var(--md-primary);\n font-size: 1.125rem;\n}\n\n.roles-grid {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n\n.role-item {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.875rem;\n background: var(--md-secondary-container);\n color: #7A4D0C;\n border: 1px solid var(--md-secondary);\n border-radius: var(--md-corner-full);\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.role-item i {\n font-size: 0.625rem;\n}\n\n.no-roles {\n font-size: 0.875rem;\n color: var(--md-on-surface-variant);\n margin: 0;\n}\n\n/* -----------------------------------------------------------------------------\n Empty State\n ----------------------------------------------------------------------------- */\n.empty-state {\n text-align: center;\n padding: 4rem 2rem;\n}\n\n.empty-state .empty-icon {\n font-size: 4rem;\n color: var(--md-outline-variant);\n margin-bottom: 1.5rem;\n}\n\n.empty-state .empty-text {\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--md-on-surface);\n margin: 0 0 0.5rem 0;\n}\n\n.empty-state .empty-subtext {\n font-size: 1rem;\n color: var(--md-on-surface-variant);\n margin: 0;\n}\n\n/* -----------------------------------------------------------------------------\n Loading State\n ----------------------------------------------------------------------------- */\n.loading-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 4rem 2rem;\n}\n\n/* -----------------------------------------------------------------------------\n Error State\n ----------------------------------------------------------------------------- */\n.error-container {\n text-align: center;\n padding: 4rem 2rem;\n}\n\n.error-container .error-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1rem;\n}\n\n.error-container .error-icon {\n font-size: 3.5rem;\n color: var(--md-error);\n margin-bottom: 1rem;\n}\n\n.error-container .error-message {\n font-size: 1.0625rem;\n color: var(--md-on-surface);\n margin: 0 0 1.5rem 0;\n}\n\n.error-container .retry-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.75rem 1.5rem;\n font-size: 0.875rem;\n font-weight: 600;\n border: none;\n border-radius: var(--md-corner-full);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n background: var(--md-primary);\n color: var(--md-on-primary);\n box-shadow: var(--md-elevation-1);\n min-height: 44px;\n}\n\n.error-container .retry-button:hover {\n background: #3395C8;\n box-shadow: var(--md-elevation-2);\n}\n\n.error-container .retry-button:active {\n background: #4BA5D4;\n transform: scale(0.98);\n}\n\n/* -----------------------------------------------------------------------------\n Modal Dialog\n ----------------------------------------------------------------------------- */\n.modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.4);\n backdrop-filter: blur(4px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n padding: 1rem;\n animation: fadeIn 0.2s ease;\n}\n\n.modal-dialog {\n background: var(--md-surface);\n border-radius: var(--md-corner-extra-large);\n box-shadow: var(--md-elevation-5);\n max-width: 500px;\n width: 100%;\n max-height: 90vh;\n overflow: hidden;\n animation: slideUp 0.3s ease;\n}\n\n.modal-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1.5rem;\n border-bottom: 1px solid var(--md-outline-variant);\n}\n\n.modal-header .modal-title {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 1.375rem;\n font-weight: 600;\n color: var(--md-on-surface);\n margin: 0;\n}\n\n.modal-header .modal-close {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--md-on-surface-variant);\n font-size: 1.25rem;\n cursor: pointer;\n border-radius: var(--md-corner-full);\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n}\n\n.modal-header .modal-close:hover {\n background: var(--md-primary);\n color: var(--md-on-primary);\n}\n\n.modal-body {\n padding: 1.5rem;\n}\n\n.modal-body p {\n font-size: 1rem;\n color: var(--md-on-surface);\n margin: 0 0 1rem 0;\n line-height: 1.5;\n}\n\n.modal-body p:last-child {\n margin-bottom: 0;\n}\n\n.modal-body .text-muted {\n font-size: 0.875rem;\n color: var(--md-on-surface-variant);\n}\n\n.modal-footer {\n display: flex;\n justify-content: flex-start;\n gap: 0.75rem;\n padding: 1.5rem;\n border-top: 1px solid var(--md-outline-variant);\n background: var(--md-surface-container-low);\n}\n\n@media (max-width: 639px) {\n .modal-dialog {\n width: 95%;\n max-height: 85vh;\n }\n\n .modal-header {\n padding: 1rem;\n }\n\n .modal-header .modal-title {\n font-size: 1.125rem;\n }\n\n .modal-body {\n padding: 1rem;\n }\n\n .modal-body p {\n font-size: 0.9375rem;\n }\n\n .modal-footer {\n padding: 1rem;\n flex-wrap: wrap;\n }\n\n .modal-footer .mj-btn {\n flex: 1;\n min-width: 100px;\n }\n}\n\n/* Form Field in Modal */\n.form-field {\n margin-bottom: 1rem;\n}\n\n.form-field .field-label {\n display: block;\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--md-on-surface);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin-bottom: 0.5rem;\n}\n\n.form-field .field-select {\n width: 100%;\n padding: 0.75rem 1rem;\n border: 1px solid var(--md-outline);\n border-radius: var(--md-corner-small);\n background: var(--md-surface);\n font-size: 1rem;\n color: var(--md-on-surface);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.form-field .field-select:focus {\n outline: none;\n border-color: var(--md-primary);\n box-shadow: 0 0 0 3px rgba(0, 118, 182, 0.2);\n}\n\n/* -----------------------------------------------------------------------------\n MJ Button System - MD3 Compliant\n ----------------------------------------------------------------------------- */\n.mj-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.75rem 1.5rem;\n font-size: 0.875rem;\n font-weight: 600;\n border: none;\n border-radius: var(--md-corner-full);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n white-space: nowrap;\n min-height: 44px;\n}\n\n.mj-btn:disabled {\n opacity: 0.38;\n cursor: not-allowed;\n}\n\n/* Primary Button - Dark, lightens on hover */\n.mj-btn-primary {\n background: var(--md-primary);\n color: var(--md-on-primary);\n box-shadow: var(--md-elevation-1);\n}\n\n.mj-btn-primary:hover:not(:disabled) {\n background: #3395C8;\n box-shadow: var(--md-elevation-2);\n}\n\n.mj-btn-primary:active:not(:disabled) {\n background: #4BA5D4;\n transform: scale(0.98);\n}\n\n/* Secondary Button - Light, fills with primary on hover */\n.mj-btn-secondary {\n background: var(--md-surface);\n color: var(--md-primary);\n border: 1px solid var(--md-outline);\n}\n\n.mj-btn-secondary:hover:not(:disabled) {\n background: var(--md-primary);\n color: var(--md-on-primary);\n border-color: var(--md-primary);\n}\n\n.mj-btn-secondary:active:not(:disabled) {\n background: #005A8C;\n border-color: #005A8C;\n}\n\n/* Ghost Button - Transparent, fills with primary on hover */\n.mj-btn-ghost {\n background: transparent;\n color: var(--md-on-surface-variant);\n}\n\n.mj-btn-ghost:hover:not(:disabled) {\n background: var(--md-primary);\n color: var(--md-on-primary);\n}\n\n.mj-btn-ghost:active:not(:disabled) {\n background: #005A8C;\n}\n\n/* Danger Button - Dark red, lightens on hover */\n.mj-btn-danger {\n background: var(--md-error);\n color: var(--md-on-error);\n box-shadow: var(--md-elevation-1);\n}\n\n.mj-btn-danger:hover:not(:disabled) {\n background: #E57373;\n box-shadow: var(--md-elevation-2);\n}\n\n.mj-btn-danger:active:not(:disabled) {\n background: #EF9A9A;\n transform: scale(0.98);\n}\n\n/* Small Button */\n.mj-btn-sm {\n padding: 0.5rem 0.875rem;\n font-size: 0.8125rem;\n min-height: 36px;\n}\n\n/* Ghost Danger Button */\n.mj-btn-ghost.mj-btn-danger {\n background: transparent;\n color: var(--md-error);\n box-shadow: none;\n}\n\n.mj-btn-ghost.mj-btn-danger:hover:not(:disabled) {\n background: var(--md-error-container);\n color: var(--md-error);\n}\n\n/* -----------------------------------------------------------------------------\n Utility Classes\n ----------------------------------------------------------------------------- */\n.text-danger {\n color: var(--md-error);\n}\n\n.text-success {\n color: var(--md-tertiary);\n}\n\n.text-warning {\n color: var(--md-secondary);\n}\n\n/* -----------------------------------------------------------------------------\n Animations\n ----------------------------------------------------------------------------- */\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n@keyframes slideUp {\n from {\n transform: translateY(20px);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Accessibility: Focus Indicators\n ----------------------------------------------------------------------------- */\n.mj-search-input:focus-visible,\n.mj-filter-select:focus-visible,\nbutton:focus-visible,\n.checkbox:focus-visible,\n.user-header:focus-visible {\n outline: 2px solid var(--md-primary);\n outline-offset: 2px;\n}\n\n/* -----------------------------------------------------------------------------\n Accessibility: Reduced Motion\n ----------------------------------------------------------------------------- */\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Large Desktop (1440px+)\n ----------------------------------------------------------------------------- */\n@media (min-width: 1440px) {\n .user-management-container {\n max-width: 1920px;\n margin: 0 auto;\n }\n\n .action-buttons {\n padding: 1.25rem 2rem;\n }\n\n .bulk-action-toolbar {\n padding: 1.25rem 2rem;\n }\n\n .mj-grid-4 {\n gap: 1.5rem;\n padding: 0 2rem 1rem 2rem;\n }\n\n .filters-section {\n margin: 0 2rem 1rem 2rem;\n padding: 1rem 1.5rem;\n }\n\n .scrollable-content {\n padding: 2rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Mobile (< 640px) - Compact adjustments\n ----------------------------------------------------------------------------- */\n@media (max-width: 639px) {\n .sticky-header {\n padding-bottom: 0;\n }\n\n .mj-grid-4 {\n grid-template-columns: repeat(2, 1fr);\n gap: 0.5rem;\n padding: 0 0.75rem 0.5rem 0.75rem;\n }\n\n .mj-card {\n padding: 0.75rem;\n gap: 0.75rem;\n }\n\n .stat-icon {\n width: 40px;\n height: 40px;\n font-size: 1.125rem;\n }\n\n .stat-content .stat-value {\n font-size: 1.5rem;\n }\n\n .stat-content .stat-label {\n font-size: 0.6875rem;\n }\n\n .filters-section {\n margin: 0 0.75rem 0.5rem 0.75rem;\n padding: 0.75rem;\n }\n\n .scrollable-content {\n padding: 0.75rem;\n }\n\n .content-area {\n padding: 0.875rem;\n border-radius: var(--md-corner-medium);\n }\n\n .list-header {\n padding: 0.625rem 0.75rem;\n }\n\n .empty-state {\n padding: 2rem 1rem;\n }\n\n .empty-state .empty-icon {\n font-size: 3rem;\n }\n\n .empty-state .empty-text {\n font-size: 1.0625rem;\n }\n\n .empty-state .empty-subtext {\n font-size: 0.875rem;\n }\n}\n"] }]
|
|
1292
|
+
args: [{ standalone: false, selector: 'mj-user-management', template: "<div class=\"user-management-container\">\n <!-- Sticky Header Section -->\n <div class=\"sticky-header\">\n <!-- Action Buttons -->\n <div class=\"action-buttons\" role=\"toolbar\" aria-label=\"User management actions\">\n <button\n class=\"mj-btn mj-btn-secondary mj-btn-icon-mobile\"\n (click)=\"refreshData()\"\n [disabled]=\"isLoading\"\n aria-label=\"Refresh user list\"\n >\n <i class=\"fa-solid fa-refresh\" [class.fa-spin]=\"isLoading\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Refresh</span>\n </button>\n <button\n class=\"mj-btn mj-btn-secondary mj-btn-icon-mobile\"\n (click)=\"exportUsers()\"\n aria-label=\"Export users to file\"\n >\n <i class=\"fa-solid fa-download\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Export</span>\n </button>\n <button\n class=\"mj-btn mj-btn-primary mj-btn-icon-mobile\"\n (click)=\"createNewUser()\"\n aria-label=\"Add new user\"\n >\n <i class=\"fa-solid fa-plus\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Add User</span>\n </button>\n </div>\n\n <!-- Bulk Action Toolbar -->\n @if (hasSelection) {\n <div class=\"bulk-action-toolbar\" role=\"toolbar\" aria-label=\"Bulk actions\">\n <div class=\"bulk-selection-info\">\n <i class=\"fa-solid fa-check-square\" aria-hidden=\"true\"></i>\n <span>{{ selectedCount }} user{{ selectedCount > 1 ? 's' : '' }} selected</span>\n <button\n class=\"mj-btn mj-btn-ghost mj-btn-sm\"\n (click)=\"clearSelection()\"\n aria-label=\"Clear selection\"\n >\n <i class=\"fa-solid fa-times\" aria-hidden=\"true\"></i>\n Clear\n </button>\n </div>\n <div class=\"bulk-action-buttons\">\n <button\n class=\"mj-btn mj-btn-secondary mj-btn-sm\"\n (click)=\"confirmBulkAction('enable')\"\n aria-label=\"Enable selected users\"\n >\n <i class=\"fa-solid fa-toggle-on\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Enable</span>\n </button>\n <button\n class=\"mj-btn mj-btn-secondary mj-btn-sm\"\n (click)=\"confirmBulkAction('disable')\"\n aria-label=\"Disable selected users\"\n >\n <i class=\"fa-solid fa-toggle-off\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Disable</span>\n </button>\n <button\n class=\"mj-btn mj-btn-secondary mj-btn-sm\"\n (click)=\"openBulkRoleAssign()\"\n aria-label=\"Assign role to selected users\"\n >\n <i class=\"fa-solid fa-user-tag\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Assign Role</span>\n </button>\n <button\n class=\"mj-btn mj-btn-danger mj-btn-sm\"\n (click)=\"confirmBulkAction('delete')\"\n aria-label=\"Delete selected users\"\n >\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Delete</span>\n </button>\n </div>\n </div>\n }\n\n <!-- Stats Cards -->\n <div class=\"mj-grid-4\" role=\"region\" aria-label=\"User statistics\">\n <div class=\"mj-card\">\n <div class=\"stat-icon stat-icon-total\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-users\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" aria-label=\"Total users count\">{{ stats.totalUsers }}</div>\n <div class=\"stat-label\">Total Users</div>\n </div>\n </div>\n\n <div class=\"mj-card\">\n <div class=\"stat-icon stat-icon-active\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-user-check\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" aria-label=\"Active users count\">{{ stats.activeUsers }}</div>\n <div class=\"stat-label\">Active Users</div>\n </div>\n </div>\n\n <div class=\"mj-card\">\n <div class=\"stat-icon stat-icon-inactive\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-user-xmark\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" aria-label=\"Inactive users count\">{{ stats.inactiveUsers }}</div>\n <div class=\"stat-label\">Inactive Users</div>\n </div>\n </div>\n\n <div class=\"mj-card\">\n <div class=\"stat-icon stat-icon-admin\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" aria-label=\"Owner users count\">{{ stats.adminUsers }}</div>\n <div class=\"stat-label\">Owners</div>\n </div>\n </div>\n </div>\n\n <!-- Filters Section -->\n <div class=\"filters-section\" role=\"search\" aria-label=\"Filter users\">\n <div class=\"filters-row\">\n <!-- Search -->\n <div class=\"mj-search\">\n <i class=\"fa-solid fa-search mj-search-icon\" aria-hidden=\"true\"></i>\n <input\n type=\"text\"\n class=\"mj-search-input\"\n placeholder=\"Search users by name or email...\"\n (input)=\"onSearchChange($event)\"\n [value]=\"filters$.value.search\"\n aria-label=\"Search users by name or email\"\n />\n </div>\n\n <!-- Mobile: Filter Toggle Button -->\n <button\n class=\"mobile-filter-toggle\"\n (click)=\"showMobileFilters = !showMobileFilters\"\n [class.active]=\"showMobileFilters\"\n [attr.aria-expanded]=\"showMobileFilters\"\n aria-label=\"Toggle filters\"\n >\n <i class=\"fa-solid fa-filter\" aria-hidden=\"true\"></i>\n @if (hasActiveFilters) {\n <span class=\"filter-badge\">{{ activeFilterCount }}</span>\n }\n </button>\n\n <!-- Desktop: Status Filter - Button group -->\n <div class=\"mj-filter-group desktop-filter-group\">\n <label class=\"mj-filter-label\" id=\"status-filter-label\">Status</label>\n <div class=\"mj-filter-buttons\" role=\"group\" aria-labelledby=\"status-filter-label\">\n <button\n class=\"mj-btn mj-btn-ghost\"\n [class.mj-btn-primary]=\"filters$.value.status === 'all'\"\n (click)=\"onStatusFilterChange('all')\"\n [attr.aria-pressed]=\"filters$.value.status === 'all'\"\n >\n All\n </button>\n <button\n class=\"mj-btn mj-btn-ghost\"\n [class.mj-btn-primary]=\"filters$.value.status === 'active'\"\n (click)=\"onStatusFilterChange('active')\"\n [attr.aria-pressed]=\"filters$.value.status === 'active'\"\n >\n Active\n </button>\n <button\n class=\"mj-btn mj-btn-ghost\"\n [class.mj-btn-primary]=\"filters$.value.status === 'inactive'\"\n (click)=\"onStatusFilterChange('inactive')\"\n [attr.aria-pressed]=\"filters$.value.status === 'inactive'\"\n >\n Inactive\n </button>\n </div>\n </div>\n\n <!-- Desktop: Role Filter -->\n <div class=\"mj-filter-group desktop-filter-group\">\n <label class=\"mj-filter-label\" for=\"role-filter\">Role</label>\n <select\n id=\"role-filter\"\n class=\"mj-filter-select\"\n (change)=\"onRoleFilterChange($any($event.target).value)\"\n aria-label=\"Filter by role\"\n >\n <option value=\"\">All Roles</option>\n @for (role of roles; track role.ID) {\n <option [value]=\"role.ID\">{{ role.Name }}</option>\n }\n </select>\n </div>\n </div>\n\n <!-- Mobile: Filter Dropdown Panel -->\n @if (showMobileFilters) {\n <div class=\"mobile-filter-dropdown\">\n <div class=\"mobile-filter-content\">\n <!-- Status Filter -->\n <div class=\"mj-filter-group\">\n <label class=\"mj-filter-label\" id=\"status-filter-label-mobile\">Status</label>\n <select\n class=\"mj-filter-select\"\n [value]=\"filters$.value.status\"\n (change)=\"onStatusFilterChange($any($event.target).value)\"\n aria-labelledby=\"status-filter-label-mobile\"\n >\n <option value=\"all\">All Status</option>\n <option value=\"active\">Active</option>\n <option value=\"inactive\">Inactive</option>\n </select>\n </div>\n\n <!-- Role Filter -->\n <div class=\"mj-filter-group\">\n <label class=\"mj-filter-label\" for=\"role-filter-mobile\">Role</label>\n <select\n id=\"role-filter-mobile\"\n class=\"mj-filter-select\"\n (change)=\"onRoleFilterChange($any($event.target).value)\"\n aria-label=\"Filter by role\"\n >\n <option value=\"\">All Roles</option>\n @for (role of roles; track role.ID) {\n <option [value]=\"role.ID\">{{ role.Name }}</option>\n }\n </select>\n </div>\n\n <!-- Clear Filters Button -->\n @if (hasActiveFilters) {\n <button class=\"mj-btn mj-btn-ghost mj-btn-sm clear-filters-btn\" (click)=\"clearFilters()\">\n <i class=\"fa-solid fa-times\" aria-hidden=\"true\"></i>\n Clear Filters\n </button>\n }\n </div>\n </div>\n }\n </div>\n </div><!-- End Sticky Header -->\n\n <!-- Scrollable Content Section -->\n <div class=\"scrollable-content\">\n <!-- Loading State -->\n @if (isLoading) {\n <div class=\"loading-container\" role=\"status\" aria-live=\"polite\" aria-busy=\"true\">\n <mj-loading text=\"Loading users...\" size=\"medium\"></mj-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (error && !isLoading) {\n <div class=\"error-container\" role=\"alert\" aria-live=\"assertive\">\n <div class=\"error-content\">\n <i class=\"fa-solid fa-exclamation-triangle error-icon\" aria-hidden=\"true\"></i>\n <p class=\"error-message\">{{ error }}</p>\n <button class=\"retry-button\" (click)=\"loadInitialData()\" aria-label=\"Retry loading users\">\n <i class=\"fa-solid fa-refresh\" aria-hidden=\"true\"></i>\n Try Again\n </button>\n </div>\n </div>\n }\n\n <!-- Content Area -->\n @if (!isLoading && !error) {\n <div class=\"content-area\">\n <!-- Users List -->\n <div class=\"users-list\" role=\"list\" aria-label=\"Users list\">\n <!-- List Header with Select All -->\n <div class=\"list-header\">\n <label class=\"select-all-label\">\n <input\n type=\"checkbox\"\n class=\"checkbox\"\n [checked]=\"isAllSelected\"\n [indeterminate]=\"isIndeterminate\"\n (change)=\"toggleSelectAll()\"\n aria-label=\"Select all users\"\n />\n <span class=\"select-all-text\">Select All ({{ filteredUsers.length }})</span>\n </label>\n @if (hasSelection) {\n <span class=\"selection-count\">{{ selectedCount }} selected</span>\n }\n </div>\n\n @for (user of filteredUsers; track user.ID) {\n <div\n class=\"user-card\"\n [class.expanded]=\"isUserExpanded(user.ID)\"\n [class.selected]=\"isUserSelected(user.ID)\"\n role=\"listitem\"\n >\n <!-- User Card Header - Always Visible -->\n <div\n class=\"user-header\"\n (click)=\"toggleUserExpansion(user.ID)\"\n role=\"button\"\n [attr.aria-expanded]=\"isUserExpanded(user.ID)\"\n tabindex=\"0\"\n (keydown.enter)=\"toggleUserExpansion(user.ID)\"\n >\n <!-- Selection Checkbox -->\n <div class=\"user-selection\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n class=\"checkbox\"\n [checked]=\"isUserSelected(user.ID)\"\n (change)=\"toggleUserSelection(user.ID, $event)\"\n [attr.aria-label]=\"'Select ' + user.Name\"\n />\n </div>\n\n <!-- User Info -->\n <div class=\"user-info\">\n <div class=\"user-avatar\" aria-hidden=\"true\">\n {{ getUserInitials(user) }}\n </div>\n <div class=\"user-details\">\n <h3 class=\"user-name\">{{ user.Name }}</h3>\n <p class=\"user-email\">{{ user.Email }}</p>\n </div>\n </div>\n\n <!-- User Meta -->\n <div class=\"user-meta\">\n <span class=\"status-badge\" [class]=\"getStatusClass(user)\">\n <i [class]=\"'fa-solid ' + getStatusIcon(user)\" aria-hidden=\"true\"></i>\n {{ user.IsActive ? 'Active' : 'Inactive' }}\n </span>\n <div class=\"user-actions desktop-only\" (click)=\"$event.stopPropagation()\">\n <button\n class=\"mj-btn mj-btn-ghost mj-btn-sm\"\n (click)=\"editUser(user)\"\n [attr.aria-label]=\"'Edit ' + user.Name\"\n title=\"Edit\"\n >\n <i class=\"fa-solid fa-edit\" aria-hidden=\"true\"></i>\n </button>\n <button\n class=\"mj-btn mj-btn-ghost mj-btn-sm\"\n (click)=\"toggleUserStatus(user)\"\n [attr.aria-label]=\"(user.IsActive ? 'Deactivate ' : 'Activate ') + user.Name\"\n [title]=\"user.IsActive ? 'Deactivate' : 'Activate'\"\n >\n <i [class]=\"user.IsActive ? 'fa-solid fa-toggle-on' : 'fa-solid fa-toggle-off'\" aria-hidden=\"true\"></i>\n </button>\n <button\n class=\"mj-btn mj-btn-ghost mj-btn-sm mj-btn-danger\"\n (click)=\"confirmDeleteUser(user)\"\n [attr.aria-label]=\"'Delete ' + user.Name\"\n title=\"Delete\"\n >\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <button class=\"expand-btn\" aria-label=\"Toggle user details\">\n <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n\n <!-- User Card Content - Expanded -->\n @if (isUserExpanded(user.ID)) {\n <div class=\"user-content\">\n <!-- Mobile Actions Bar -->\n <div class=\"mobile-actions-bar\" (click)=\"$event.stopPropagation()\">\n <span class=\"status-badge\" [class]=\"getStatusClass(user)\">\n <i [class]=\"'fa-solid ' + getStatusIcon(user)\" aria-hidden=\"true\"></i>\n {{ user.IsActive ? 'Active' : 'Inactive' }}\n </span>\n <div class=\"mobile-action-buttons\">\n <button\n class=\"mj-btn mj-btn-ghost mj-btn-sm\"\n (click)=\"editUser(user)\"\n [attr.aria-label]=\"'Edit ' + user.Name\"\n >\n <i class=\"fa-solid fa-edit\" aria-hidden=\"true\"></i>\n <span class=\"btn-label\">Edit</span>\n </button>\n <button\n class=\"mj-btn mj-btn-ghost mj-btn-sm\"\n (click)=\"toggleUserStatus(user)\"\n [attr.aria-label]=\"(user.IsActive ? 'Deactivate ' : 'Activate ') + user.Name\"\n >\n <i [class]=\"user.IsActive ? 'fa-solid fa-toggle-on' : 'fa-solid fa-toggle-off'\" aria-hidden=\"true\"></i>\n <span class=\"btn-label\">{{ user.IsActive ? 'Disable' : 'Enable' }}</span>\n </button>\n <button\n class=\"mj-btn mj-btn-ghost mj-btn-sm mj-btn-danger\"\n (click)=\"confirmDeleteUser(user)\"\n [attr.aria-label]=\"'Delete ' + user.Name\"\n >\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n <span class=\"btn-label\">Delete</span>\n </button>\n </div>\n </div>\n\n <!-- User Stats -->\n <div class=\"user-stats\">\n <div class=\"stat-item\">\n <i class=\"fa-solid fa-user-tag\" aria-hidden=\"true\"></i>\n <span class=\"stat-label\">Type:</span>\n <span class=\"stat-value\">\n <i [class]=\"'fa-solid ' + getUserTypeIcon(user)\" aria-hidden=\"true\"></i>\n {{ user.Type }}\n </span>\n </div>\n <div class=\"stat-item\">\n <i class=\"fa-solid fa-id-card\" aria-hidden=\"true\"></i>\n <span class=\"stat-label\">Full Name:</span>\n <span class=\"stat-value\">{{ user.FirstName }} {{ user.LastName }}</span>\n </div>\n <div class=\"stat-item\">\n <i class=\"fa-solid fa-calendar\" aria-hidden=\"true\"></i>\n <span class=\"stat-label\">Created:</span>\n <span class=\"stat-value\">{{ user.__mj_CreatedAt | date:'short' }}</span>\n </div>\n <div class=\"stat-item\">\n <i class=\"fa-solid fa-clock\" aria-hidden=\"true\"></i>\n <span class=\"stat-label\">Updated:</span>\n <span class=\"stat-value\">{{ user.__mj_UpdatedAt | date:'short' }}</span>\n </div>\n </div>\n\n <!-- User Roles Section -->\n <div class=\"roles-section\">\n <h4 class=\"section-title\">\n <i class=\"fa-solid fa-user-shield\" aria-hidden=\"true\"></i>\n Assigned Roles\n </h4>\n @if (getUserRoles(user.ID).length > 0) {\n <div class=\"roles-grid\">\n @for (role of getUserRoles(user.ID); track role.ID) {\n <div class=\"role-item\">\n <i class=\"fa-solid fa-shield\" aria-hidden=\"true\"></i>\n {{ role.Name }}\n </div>\n }\n </div>\n } @else {\n <p class=\"no-roles\">No roles assigned to this user</p>\n }\n </div>\n </div>\n }\n </div>\n }\n\n @if (filteredUsers.length === 0) {\n <div class=\"empty-state\" role=\"status\">\n <i class=\"fa-solid fa-users-slash empty-icon\" aria-hidden=\"true\"></i>\n <p class=\"empty-text\">No users found</p>\n <p class=\"empty-subtext\">Try adjusting your filters or add a new user</p>\n </div>\n }\n </div>\n </div>\n }\n </div><!-- End Scrollable Content -->\n\n <!-- User Create/Edit Dialog -->\n <mj-user-dialog\n [data]=\"userDialogData\"\n [visible]=\"showUserDialog\"\n (result)=\"onUserDialogResult($event)\">\n </mj-user-dialog>\n\n <!-- Delete Confirmation Dialog -->\n @if (showDeleteConfirm && selectedUser) {\n <div\n class=\"modal-backdrop\"\n (click)=\"showDeleteConfirm = false\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"delete-dialog-title\"\n aria-describedby=\"delete-dialog-desc\"\n >\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3 class=\"modal-title\" id=\"delete-dialog-title\">\n <i class=\"fa-solid fa-exclamation-triangle text-danger\" aria-hidden=\"true\"></i>\n Confirm Delete\n </h3>\n <button\n class=\"modal-close\"\n (click)=\"showDeleteConfirm = false\"\n aria-label=\"Close dialog\"\n >\n <i class=\"fa-solid fa-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <div class=\"modal-body\" id=\"delete-dialog-desc\">\n <p>Are you sure you want to delete user <strong>{{ selectedUser.Name }}</strong>?</p>\n <p class=\"text-muted\">This action cannot be undone.</p>\n </div>\n <div class=\"modal-footer\">\n <button class=\"mj-btn mj-btn-danger\" (click)=\"deleteUser()\">\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n Delete User\n </button>\n <button class=\"mj-btn mj-btn-secondary\" (click)=\"showDeleteConfirm = false\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Bulk Action Confirmation Dialog -->\n @if (showBulkActionConfirm) {\n <div\n class=\"modal-backdrop\"\n (click)=\"cancelBulkAction()\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"bulk-action-dialog-title\"\n aria-describedby=\"bulk-action-dialog-desc\"\n >\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3 class=\"modal-title\" id=\"bulk-action-dialog-title\">\n @if (bulkActionType === 'delete') {\n <i class=\"fa-solid fa-exclamation-triangle text-danger\" aria-hidden=\"true\"></i>\n } @else if (bulkActionType === 'enable') {\n <i class=\"fa-solid fa-toggle-on text-success\" aria-hidden=\"true\"></i>\n } @else {\n <i class=\"fa-solid fa-toggle-off text-warning\" aria-hidden=\"true\"></i>\n }\n {{ getBulkActionTitle() }}\n </h3>\n <button\n class=\"modal-close\"\n (click)=\"cancelBulkAction()\"\n aria-label=\"Close dialog\"\n >\n <i class=\"fa-solid fa-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <div class=\"modal-body\" id=\"bulk-action-dialog-desc\">\n <p>{{ getBulkActionMessage() }}</p>\n @if (bulkActionType === 'delete') {\n <p class=\"text-muted\">This action cannot be undone.</p>\n }\n </div>\n <div class=\"modal-footer\">\n <button\n [class]=\"bulkActionType === 'delete' ? 'mj-btn mj-btn-danger' : 'mj-btn mj-btn-primary'\"\n (click)=\"executeBulkAction()\"\n [disabled]=\"isLoading\"\n >\n @if (isLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n Processing...\n } @else {\n @if (bulkActionType === 'delete') {\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n } @else if (bulkActionType === 'enable') {\n <i class=\"fa-solid fa-toggle-on\" aria-hidden=\"true\"></i>\n } @else {\n <i class=\"fa-solid fa-toggle-off\" aria-hidden=\"true\"></i>\n }\n {{ getBulkActionButtonText() }}\n }\n </button>\n <button class=\"mj-btn mj-btn-secondary\" (click)=\"cancelBulkAction()\" [disabled]=\"isLoading\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Bulk Role Assignment Dialog -->\n @if (showBulkRoleAssign) {\n <div\n class=\"modal-backdrop\"\n (click)=\"cancelBulkRoleAssign()\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"bulk-role-dialog-title\"\n aria-describedby=\"bulk-role-dialog-desc\"\n >\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3 class=\"modal-title\" id=\"bulk-role-dialog-title\">\n <i class=\"fa-solid fa-user-tag\" aria-hidden=\"true\"></i>\n Assign Role to {{ selectedCount }} User{{ selectedCount > 1 ? 's' : '' }}\n </h3>\n <button\n class=\"modal-close\"\n (click)=\"cancelBulkRoleAssign()\"\n aria-label=\"Close dialog\"\n >\n <i class=\"fa-solid fa-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <div class=\"modal-body\" id=\"bulk-role-dialog-desc\">\n <p>Select a role to assign to the selected users:</p>\n <div class=\"form-field\">\n <label class=\"field-label\" for=\"bulk-role-select\">Role</label>\n <select\n id=\"bulk-role-select\"\n class=\"field-select\"\n [(ngModel)]=\"bulkRoleId\"\n aria-label=\"Select role to assign\"\n >\n <option value=\"\">Select a role...</option>\n @for (role of roles; track role.ID) {\n <option [value]=\"role.ID\">{{ role.Name }}</option>\n }\n </select>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button\n class=\"mj-btn mj-btn-primary\"\n (click)=\"executeBulkRoleAssign()\"\n [disabled]=\"!bulkRoleId || isLoading\"\n >\n @if (isLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n Assigning...\n } @else {\n <i class=\"fa-solid fa-user-tag\" aria-hidden=\"true\"></i>\n Assign Role\n }\n </button>\n <button class=\"mj-btn mj-btn-secondary\" (click)=\"cancelBulkRoleAssign()\" [disabled]=\"isLoading\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: ["/* =============================================================================\n User Management Component - MD3 Design System\n Unified Expandable Card List Pattern\n ============================================================================= */\n\n/* -----------------------------------------------------------------------------\n MD3 Design Tokens (Local)\n ----------------------------------------------------------------------------- */\n:host {\n /* Primary - Deep Blue */\n --md-primary: #0076B6;\n --md-on-primary: #FFFFFF;\n --md-primary-container: #AAE7FD;\n --md-on-primary-container: #001F2A;\n\n /* Secondary - Light Orange */\n --md-secondary: #F5A623;\n --md-on-secondary: #FFFFFF;\n --md-secondary-container: #FFECD6;\n --md-on-secondary-container: #2D1600;\n\n /* Tertiary - Light Green */\n --md-tertiary: #4CAF50;\n --md-on-tertiary: #FFFFFF;\n --md-tertiary-container: #C8E6C9;\n --md-on-tertiary-container: #002204;\n\n /* Error - Red */\n --md-error: #D32F2F;\n --md-on-error: #FFFFFF;\n --md-error-container: #FFCDD2;\n --md-on-error-container: #410002;\n\n /* Surface Colors */\n --md-surface: #FAFCFF;\n --md-surface-container-lowest: #FFFFFF;\n --md-surface-container-low: #F3F5F9;\n --md-surface-container: #EDF0F4;\n --md-surface-container-high: #E7EAEE;\n --md-surface-container-highest: #E1E3E8;\n --md-on-surface: #191C20;\n --md-on-surface-variant: #43474E;\n --md-outline: #74777F;\n --md-outline-variant: #C4C6D0;\n\n /* Elevation */\n --md-elevation-1: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08);\n --md-elevation-2: 0 2px 4px rgba(0, 0, 0, 0.1), 0 4px 8px rgba(0, 0, 0, 0.08);\n --md-elevation-3: 0 4px 8px rgba(0, 0, 0, 0.1), 0 8px 16px rgba(0, 0, 0, 0.08);\n --md-elevation-4: 0 6px 12px rgba(0, 0, 0, 0.1), 0 12px 24px rgba(0, 0, 0, 0.08);\n --md-elevation-5: 0 8px 16px rgba(0, 0, 0, 0.12), 0 16px 32px rgba(0, 0, 0, 0.1);\n\n /* Corner Radii */\n --md-corner-extra-small: 4px;\n --md-corner-small: 8px;\n --md-corner-medium: 12px;\n --md-corner-large: 16px;\n --md-corner-extra-large: 28px;\n --md-corner-full: 9999px;\n\n /* Host Layout */\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n overflow: hidden;\n}\n\n/* -----------------------------------------------------------------------------\n Container & Layout\n ----------------------------------------------------------------------------- */\n.user-management-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n position: relative;\n width: 100%;\n background: var(--md-surface);\n}\n\n/* -----------------------------------------------------------------------------\n Sticky Header - Always visible controls\n ----------------------------------------------------------------------------- */\n.sticky-header {\n flex-shrink: 0;\n background: var(--md-surface);\n border-bottom: 1px solid var(--md-outline-variant);\n box-shadow: var(--md-elevation-1);\n z-index: 10;\n}\n\n/* -----------------------------------------------------------------------------\n Scrollable Content - Users list area\n ----------------------------------------------------------------------------- */\n.scrollable-content {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n -webkit-overflow-scrolling: touch;\n padding: 1rem;\n background: var(--md-surface-container-lowest);\n}\n\n@media (min-width: 768px) {\n .scrollable-content {\n padding: 1.5rem 2rem;\n }\n}\n\n@media (min-width: 1024px) {\n .scrollable-content {\n padding: 2rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Action Buttons (In Sticky Header)\n ----------------------------------------------------------------------------- */\n.action-buttons {\n flex-shrink: 0;\n display: flex;\n gap: 0.75rem;\n justify-content: flex-end;\n padding: 0.75rem 1rem;\n background: var(--md-surface);\n}\n\n@media (min-width: 768px) {\n .action-buttons {\n padding: 1rem 1.5rem;\n }\n}\n\n@media (max-width: 639px) {\n .action-buttons {\n justify-content: center;\n flex-wrap: wrap;\n }\n\n .mj-btn-icon-mobile .btn-text {\n display: none;\n }\n\n .mj-btn-icon-mobile {\n width: 48px;\n height: 48px;\n padding: 0;\n border-radius: var(--md-corner-full);\n min-width: 48px;\n min-height: 48px;\n }\n\n .mj-btn-icon-mobile i {\n font-size: 1.25rem;\n }\n\n .mj-btn-primary.mj-btn-icon-mobile {\n box-shadow: var(--md-elevation-2);\n }\n\n .mj-btn-primary.mj-btn-icon-mobile:hover {\n box-shadow: var(--md-elevation-3);\n transform: scale(1.05);\n }\n\n .mj-btn-secondary.mj-btn-icon-mobile {\n background: var(--md-surface);\n border: 1px solid var(--md-outline);\n }\n\n .mj-btn-secondary.mj-btn-icon-mobile:hover {\n background: var(--md-primary);\n color: var(--md-on-primary);\n border-color: var(--md-primary);\n }\n}\n\n/* -----------------------------------------------------------------------------\n Bulk Action Toolbar\n ----------------------------------------------------------------------------- */\n.bulk-action-toolbar {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n padding: 0.75rem 1rem;\n background: var(--md-primary-container);\n border-bottom: 1px solid var(--md-outline-variant);\n animation: slideDown 0.2s ease;\n}\n\n@media (min-width: 768px) {\n .bulk-action-toolbar {\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n padding: 1rem 1.5rem;\n }\n}\n\n.bulk-selection-info {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--md-on-primary-container);\n}\n\n.bulk-selection-info i {\n font-size: 1.25rem;\n}\n\n.bulk-action-buttons {\n display: flex;\n gap: 0.5rem;\n flex-wrap: wrap;\n}\n\n@media (max-width: 639px) {\n .bulk-action-buttons .btn-text {\n display: none;\n }\n\n .bulk-action-buttons .mj-btn-sm {\n padding: 0.5rem;\n min-width: 36px;\n }\n}\n\n@keyframes slideDown {\n from {\n transform: translateY(-10px);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Statistics Cards - Static Display (Non-interactive)\n ----------------------------------------------------------------------------- */\n.mj-grid-4 {\n display: none;\n grid-template-columns: repeat(2, 1fr);\n gap: 0.5rem;\n padding: 0 1rem 0.75rem 1rem;\n background: var(--md-surface);\n}\n\n@media (min-width: 768px) {\n .mj-grid-4 {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n padding: 0 1.5rem 1rem 1.5rem;\n gap: 0.75rem;\n }\n}\n\n@media (min-width: 1024px) {\n .mj-grid-4 {\n gap: 1rem;\n }\n}\n\n/* Static Card - No hover effects */\n.mj-card {\n background: var(--md-surface-container-low);\n border-radius: var(--md-corner-medium);\n padding: 1rem;\n box-shadow: none;\n display: flex;\n align-items: center;\n gap: 1rem;\n border: 1px solid var(--md-outline-variant);\n cursor: default;\n pointer-events: none;\n}\n\n@media (min-width: 768px) {\n .mj-card {\n padding: 1rem 1.25rem;\n }\n}\n\n.stat-icon {\n width: 48px;\n height: 48px;\n border-radius: var(--md-corner-medium);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.25rem;\n flex-shrink: 0;\n}\n\n@media (min-width: 768px) {\n .stat-icon {\n width: 48px;\n height: 48px;\n font-size: 1.25rem;\n }\n}\n\n.stat-icon-total {\n background: var(--md-primary-container);\n color: var(--md-primary);\n}\n\n.stat-icon-active {\n background: var(--md-tertiary-container);\n color: var(--md-tertiary);\n}\n\n.stat-icon-inactive {\n background: var(--md-error-container);\n color: var(--md-error);\n}\n\n.stat-icon-admin {\n background: var(--md-secondary-container);\n color: #B5751A;\n}\n\n.stat-content {\n flex: 1;\n min-width: 0;\n}\n\n.stat-content .stat-value {\n font-size: 1.75rem;\n font-weight: 700;\n color: var(--md-on-surface);\n line-height: 1;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 768px) {\n .stat-content .stat-value {\n font-size: 2rem;\n }\n}\n\n.stat-content .stat-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--md-on-surface-variant);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin-top: 0.25rem;\n}\n\n@media (min-width: 768px) {\n .stat-content .stat-label {\n font-size: 0.8125rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Filters Section\n ----------------------------------------------------------------------------- */\n.filters-section {\n flex-shrink: 0;\n background: var(--md-surface-container);\n margin: 0 1rem 0.75rem 1rem;\n padding: 0.75rem;\n border-radius: var(--md-corner-medium);\n border: 1px solid var(--md-outline-variant);\n}\n\n@media (min-width: 768px) {\n .filters-section {\n margin: 0 1.5rem 1rem 1.5rem;\n padding: 1rem 1.25rem;\n }\n}\n\n.filters-row {\n display: flex;\n gap: 1rem;\n align-items: flex-end;\n flex-wrap: wrap;\n}\n\n@media (min-width: 768px) {\n .filters-row {\n gap: 1.5rem;\n }\n}\n\n@media (max-width: 767px) {\n .filters-section {\n padding: 0.75rem;\n }\n\n .filters-row {\n display: flex;\n flex-direction: row;\n gap: 0.75rem;\n align-items: center;\n }\n\n .filters-row .mj-search {\n flex: 1;\n min-width: 0;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Search Input\n ----------------------------------------------------------------------------- */\n.mj-search {\n position: relative;\n flex: 1;\n min-width: 200px;\n max-width: 100%;\n}\n\n@media (min-width: 640px) {\n .mj-search {\n min-width: 280px;\n max-width: 500px;\n }\n}\n\n@media (min-width: 768px) {\n .mj-search {\n min-width: 320px;\n max-width: 600px;\n }\n}\n\n@media (max-width: 767px) {\n .mj-search {\n width: 100%;\n max-width: none;\n min-width: 0;\n }\n}\n\n.mj-search .mj-search-icon {\n position: absolute;\n left: 1rem;\n top: 50%;\n transform: translateY(-50%);\n color: var(--md-on-surface-variant);\n font-size: 1rem;\n pointer-events: none;\n transition: color 0.2s ease;\n}\n\n.mj-search .mj-search-input {\n width: 100%;\n padding: 0.875rem 1rem 0.875rem 2.75rem;\n border: 2px solid var(--md-outline-variant);\n border-radius: var(--md-corner-full);\n font-size: 1rem;\n background: var(--md-surface);\n color: var(--md-on-surface);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 48px;\n}\n\n@media (min-width: 768px) {\n .mj-search .mj-search-input {\n padding: 1rem 1.25rem 1rem 3rem;\n min-height: 52px;\n font-size: 1.0625rem;\n }\n\n .mj-search .mj-search-icon {\n left: 1.125rem;\n font-size: 1.125rem;\n }\n}\n\n.mj-search .mj-search-input::placeholder {\n color: var(--md-on-surface-variant);\n}\n\n.mj-search .mj-search-input:hover {\n border-color: var(--md-primary);\n background: var(--md-surface-container-lowest);\n}\n\n.mj-search .mj-search-input:focus {\n outline: none;\n border-color: var(--md-primary);\n box-shadow: 0 0 0 3px rgba(0, 118, 182, 0.2);\n background: var(--md-surface);\n}\n\n.mj-search:focus-within .mj-search-icon {\n color: var(--md-primary);\n}\n\n/* -----------------------------------------------------------------------------\n Mobile Filter Toggle Button\n ----------------------------------------------------------------------------- */\n.mobile-filter-toggle {\n display: none;\n align-items: center;\n justify-content: center;\n position: relative;\n width: 48px;\n height: 48px;\n min-width: 48px;\n border: 1px solid var(--md-outline-variant);\n border-radius: var(--md-corner-small);\n background: var(--md-surface-container-low);\n color: var(--md-on-surface-variant);\n font-size: 1.125rem;\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n box-shadow: var(--md-elevation-1);\n flex-shrink: 0;\n}\n\n.mobile-filter-toggle:hover {\n background: var(--md-surface-container);\n border-color: var(--md-primary);\n color: var(--md-primary);\n}\n\n.mobile-filter-toggle.active {\n background: var(--md-primary);\n color: var(--md-on-primary);\n border-color: var(--md-primary);\n}\n\n.filter-badge {\n position: absolute;\n top: -4px;\n right: -4px;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n border-radius: var(--md-corner-full);\n background: var(--md-error);\n color: var(--md-on-error);\n font-size: 0.6875rem;\n font-weight: 700;\n display: flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n}\n\n/* -----------------------------------------------------------------------------\n Mobile Filter Dropdown Panel\n ----------------------------------------------------------------------------- */\n.mobile-filter-dropdown {\n display: none;\n margin-top: 0.75rem;\n padding-top: 0.75rem;\n border-top: 1px solid var(--md-outline-variant);\n animation: slideDown 0.2s ease;\n}\n\n/* Mobile only - show filter toggle and dropdown */\n@media (max-width: 767px) {\n .mobile-filter-toggle {\n display: flex !important;\n }\n\n .mobile-filter-dropdown {\n display: block;\n }\n\n .desktop-filter-group {\n display: none !important;\n }\n}\n\n.mobile-filter-content {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.mobile-filter-content .mj-filter-group {\n width: 100%;\n}\n\n.mobile-filter-content .mj-filter-select {\n width: 100%;\n}\n\n.clear-filters-btn {\n align-self: flex-start;\n margin-top: 0.25rem;\n}\n\n/* -----------------------------------------------------------------------------\n Filter Controls - Mobile/Desktop visibility\n ----------------------------------------------------------------------------- */\n\n/* Desktop filter groups - hidden by default, shown on desktop */\n.desktop-filter-group {\n display: none !important;\n}\n\n/* Desktop only - show filter groups, hide mobile toggle */\n@media screen and (min-width: 768px) {\n .desktop-filter-group {\n display: flex !important;\n flex-direction: column;\n gap: 0.375rem;\n }\n\n .mobile-filter-toggle {\n display: none !important;\n }\n\n .mobile-filter-dropdown {\n display: none !important;\n }\n}\n\n.mj-filter-group {\n display: flex;\n flex-direction: column;\n gap: 0.375rem;\n}\n\n@media (max-width: 767px) {\n .mj-filter-group {\n flex: 1;\n min-width: 100px;\n }\n}\n\n.mj-filter-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--md-on-surface);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.mj-filter-buttons {\n display: flex;\n background: var(--md-surface-container);\n border-radius: var(--md-corner-full);\n padding: 4px;\n gap: 2px;\n}\n\n.mj-filter-buttons .mj-btn {\n border-radius: var(--md-corner-full);\n}\n\n.mj-filter-buttons .mj-btn-primary {\n background-color: var(--md-primary);\n color: var(--md-on-primary);\n box-shadow: var(--md-elevation-1);\n}\n\n.mj-filter-select {\n padding: 0.625rem 1rem;\n border: 1px solid var(--md-outline-variant);\n border-radius: var(--md-corner-small);\n background: var(--md-surface-container-low);\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--md-on-surface);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-width: 120px;\n box-shadow: var(--md-elevation-1);\n}\n\n.mj-filter-select:hover {\n background: var(--md-surface-container);\n border-color: var(--md-primary);\n}\n\n@media (max-width: 767px) {\n .mj-filter-select {\n width: 100%;\n padding: 0.5rem 0.75rem;\n font-size: 0.8125rem;\n }\n}\n\n.mj-filter-select:focus {\n outline: none;\n border-color: var(--md-primary);\n box-shadow: 0 0 0 3px rgba(0, 118, 182, 0.2);\n}\n\n/* -----------------------------------------------------------------------------\n Content Area\n ----------------------------------------------------------------------------- */\n.content-area {\n flex: 1 1 auto;\n overflow: visible;\n position: relative;\n background: var(--md-surface);\n border-radius: var(--md-corner-large);\n box-shadow: var(--md-elevation-1);\n padding: 1rem;\n border: 1px solid var(--md-outline-variant);\n}\n\n@media (min-width: 768px) {\n .content-area {\n padding: 1.5rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Users List - Unified Expandable Card Pattern\n ----------------------------------------------------------------------------- */\n.users-list {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n/* List Header with Select All */\n.list-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.75rem 1rem;\n background: var(--md-surface-container);\n border-radius: var(--md-corner-small);\n border: 1px solid var(--md-outline-variant);\n margin-bottom: 0.5rem;\n}\n\n.select-all-label {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n cursor: pointer;\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--md-on-surface);\n}\n\n.select-all-text {\n font-weight: 500;\n}\n\n.selection-count {\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--md-primary);\n background: var(--md-primary-container);\n padding: 0.25rem 0.75rem;\n border-radius: var(--md-corner-full);\n}\n\n/* User Card - Interactive */\n.user-card {\n background: var(--md-surface);\n border: 1px solid var(--md-outline-variant);\n border-radius: var(--md-corner-large);\n overflow: hidden;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.user-card:hover {\n box-shadow: var(--md-elevation-2);\n border-color: var(--md-primary);\n}\n\n.user-card.expanded {\n box-shadow: var(--md-elevation-3);\n border-color: var(--md-primary);\n}\n\n.user-card.selected {\n background: var(--md-primary-container);\n border-color: var(--md-primary);\n}\n\n.user-card.selected .user-header {\n background: transparent;\n}\n\n.user-card.selected:hover .user-header {\n background: rgba(0, 118, 182, 0.08);\n}\n\n/* User Card Header */\n.user-header {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 1.25rem 1.5rem;\n cursor: pointer;\n background: var(--md-surface-container-lowest);\n transition: background-color 0.2s ease;\n}\n\n.user-header:hover {\n background: var(--md-surface-container-low);\n}\n\n@media (max-width: 639px) {\n .user-header {\n padding: 0.875rem 1rem;\n gap: 0.625rem;\n }\n}\n\n/* User Selection */\n.user-selection {\n flex-shrink: 0;\n}\n\n.checkbox {\n width: 20px;\n height: 20px;\n cursor: pointer;\n accent-color: var(--md-primary);\n}\n\n/* User Info */\n.user-info {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n flex: 1;\n min-width: 0;\n}\n\n.user-avatar {\n width: 48px;\n height: 48px;\n border-radius: var(--md-corner-full);\n background: linear-gradient(135deg, var(--md-primary) 0%, #3395C8 100%);\n color: var(--md-on-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 0.875rem;\n font-weight: 600;\n flex-shrink: 0;\n}\n\n@media (max-width: 639px) {\n .user-avatar {\n width: 40px;\n height: 40px;\n font-size: 0.8125rem;\n }\n\n .user-details {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n }\n\n .user-name {\n font-size: 0.9375rem;\n font-weight: 600;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .user-email {\n font-size: 0.8125rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n}\n\n.user-details {\n flex: 1;\n min-width: 0;\n}\n\n.user-name {\n font-size: 1rem;\n font-weight: 600;\n color: var(--md-on-surface);\n margin: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.user-email {\n font-size: 0.875rem;\n color: var(--md-on-surface-variant);\n margin: 0.125rem 0 0 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* User Meta */\n.user-meta {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n flex-shrink: 0;\n}\n\n@media (max-width: 767px) {\n .user-meta .status-badge,\n .user-meta .user-actions {\n display: none !important;\n }\n\n .user-meta {\n gap: 0;\n }\n}\n\n/* Desktop Actions */\n.desktop-only {\n display: none;\n}\n\n@media (min-width: 768px) {\n .desktop-only {\n display: flex;\n gap: 0.25rem;\n }\n}\n\n.user-actions {\n display: flex;\n gap: 0.25rem;\n}\n\n/* Expand Button */\n.expand-btn {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--md-on-surface-variant);\n font-size: 1rem;\n cursor: pointer;\n transition: all 0.2s ease;\n border-radius: var(--md-corner-full);\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.expand-btn:hover {\n background: var(--md-primary);\n color: var(--md-on-primary);\n}\n\n.expand-btn i {\n transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1);\n}\n\n.user-card.expanded .expand-btn i {\n transform: rotate(180deg);\n}\n\n/* User Card Content - Expanded */\n.user-content {\n padding: 1.25rem 1.5rem;\n background: var(--md-surface);\n border-top: 1px solid var(--md-outline-variant);\n animation: slideDown 0.3s cubic-bezier(0, 0, 0.2, 1);\n}\n\n/* Mobile Actions Bar */\n.mobile-actions-bar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: 1rem;\n margin-bottom: 1rem;\n border-bottom: 1px solid var(--md-outline-variant);\n}\n\n@media (min-width: 768px) {\n .mobile-actions-bar {\n display: none;\n }\n}\n\n.mobile-action-buttons {\n display: flex;\n gap: 0.5rem;\n}\n\n.mobile-action-buttons .btn-label {\n margin-left: 0.375rem;\n}\n\n/* Status Badge */\n.status-badge {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.875rem;\n border-radius: var(--md-corner-full);\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.status-badge.status-active {\n background: var(--md-tertiary-container);\n color: #1B5E20;\n border: 1px solid var(--md-tertiary);\n}\n\n.status-badge.status-inactive {\n background: var(--md-error-container);\n color: var(--md-on-error-container);\n border: 1px solid var(--md-error);\n}\n\n.status-badge i {\n font-size: 0.625rem;\n}\n\n/* User Stats in Expanded Content */\n.user-stats {\n display: flex;\n flex-wrap: wrap;\n gap: 1rem;\n margin-bottom: 1rem;\n}\n\n.user-stats .stat-item {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-size: 0.875rem;\n color: var(--md-on-surface-variant);\n}\n\n.user-stats .stat-item i {\n color: var(--md-primary);\n font-size: 0.875rem;\n}\n\n.user-stats .stat-label {\n color: var(--md-on-surface-variant);\n}\n\n.user-stats .stat-value {\n color: var(--md-on-surface);\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.user-stats .stat-value i {\n font-size: 0.75rem;\n}\n\n/* Roles Section */\n.roles-section {\n padding-top: 1rem;\n border-top: 1px solid var(--md-outline-variant);\n}\n\n.section-title {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n font-size: 1rem;\n font-weight: 600;\n color: var(--md-on-surface);\n margin: 0 0 0.75rem 0;\n}\n\n.section-title i {\n color: var(--md-primary);\n font-size: 1.125rem;\n}\n\n.roles-grid {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n\n.role-item {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.875rem;\n background: var(--md-secondary-container);\n color: #7A4D0C;\n border: 1px solid var(--md-secondary);\n border-radius: var(--md-corner-full);\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.role-item i {\n font-size: 0.625rem;\n}\n\n.no-roles {\n font-size: 0.875rem;\n color: var(--md-on-surface-variant);\n margin: 0;\n}\n\n/* -----------------------------------------------------------------------------\n Empty State\n ----------------------------------------------------------------------------- */\n.empty-state {\n text-align: center;\n padding: 4rem 2rem;\n}\n\n.empty-state .empty-icon {\n font-size: 4rem;\n color: var(--md-outline-variant);\n margin-bottom: 1.5rem;\n}\n\n.empty-state .empty-text {\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--md-on-surface);\n margin: 0 0 0.5rem 0;\n}\n\n.empty-state .empty-subtext {\n font-size: 1rem;\n color: var(--md-on-surface-variant);\n margin: 0;\n}\n\n/* -----------------------------------------------------------------------------\n Loading State\n ----------------------------------------------------------------------------- */\n.loading-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 4rem 2rem;\n}\n\n/* -----------------------------------------------------------------------------\n Error State\n ----------------------------------------------------------------------------- */\n.error-container {\n text-align: center;\n padding: 4rem 2rem;\n}\n\n.error-container .error-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1rem;\n}\n\n.error-container .error-icon {\n font-size: 3.5rem;\n color: var(--md-error);\n margin-bottom: 1rem;\n}\n\n.error-container .error-message {\n font-size: 1.0625rem;\n color: var(--md-on-surface);\n margin: 0 0 1.5rem 0;\n}\n\n.error-container .retry-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.75rem 1.5rem;\n font-size: 0.875rem;\n font-weight: 600;\n border: none;\n border-radius: var(--md-corner-full);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n background: var(--md-primary);\n color: var(--md-on-primary);\n box-shadow: var(--md-elevation-1);\n min-height: 44px;\n}\n\n.error-container .retry-button:hover {\n background: #3395C8;\n box-shadow: var(--md-elevation-2);\n}\n\n.error-container .retry-button:active {\n background: #4BA5D4;\n transform: scale(0.98);\n}\n\n/* -----------------------------------------------------------------------------\n Modal Dialog\n ----------------------------------------------------------------------------- */\n.modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.4);\n backdrop-filter: blur(4px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n padding: 1rem;\n animation: fadeIn 0.2s ease;\n}\n\n.modal-dialog {\n background: var(--md-surface);\n border-radius: var(--md-corner-extra-large);\n box-shadow: var(--md-elevation-5);\n max-width: 500px;\n width: 100%;\n max-height: 90vh;\n overflow: hidden;\n animation: slideUp 0.3s ease;\n}\n\n.modal-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1.5rem;\n border-bottom: 1px solid var(--md-outline-variant);\n}\n\n.modal-header .modal-title {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 1.375rem;\n font-weight: 600;\n color: var(--md-on-surface);\n margin: 0;\n}\n\n.modal-header .modal-close {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--md-on-surface-variant);\n font-size: 1.25rem;\n cursor: pointer;\n border-radius: var(--md-corner-full);\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n}\n\n.modal-header .modal-close:hover {\n background: var(--md-primary);\n color: var(--md-on-primary);\n}\n\n.modal-body {\n padding: 1.5rem;\n}\n\n.modal-body p {\n font-size: 1rem;\n color: var(--md-on-surface);\n margin: 0 0 1rem 0;\n line-height: 1.5;\n}\n\n.modal-body p:last-child {\n margin-bottom: 0;\n}\n\n.modal-body .text-muted {\n font-size: 0.875rem;\n color: var(--md-on-surface-variant);\n}\n\n.modal-footer {\n display: flex;\n justify-content: flex-start;\n gap: 0.75rem;\n padding: 1.5rem;\n border-top: 1px solid var(--md-outline-variant);\n background: var(--md-surface-container-low);\n}\n\n@media (max-width: 639px) {\n .modal-dialog {\n width: 95%;\n max-height: 85vh;\n }\n\n .modal-header {\n padding: 1rem;\n }\n\n .modal-header .modal-title {\n font-size: 1.125rem;\n }\n\n .modal-body {\n padding: 1rem;\n }\n\n .modal-body p {\n font-size: 0.9375rem;\n }\n\n .modal-footer {\n padding: 1rem;\n flex-wrap: wrap;\n }\n\n .modal-footer .mj-btn {\n flex: 1;\n min-width: 100px;\n }\n}\n\n/* Form Field in Modal */\n.form-field {\n margin-bottom: 1rem;\n}\n\n.form-field .field-label {\n display: block;\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--md-on-surface);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin-bottom: 0.5rem;\n}\n\n.form-field .field-select {\n width: 100%;\n padding: 0.75rem 1rem;\n border: 1px solid var(--md-outline);\n border-radius: var(--md-corner-small);\n background: var(--md-surface);\n font-size: 1rem;\n color: var(--md-on-surface);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.form-field .field-select:focus {\n outline: none;\n border-color: var(--md-primary);\n box-shadow: 0 0 0 3px rgba(0, 118, 182, 0.2);\n}\n\n/* -----------------------------------------------------------------------------\n MJ Button System - MD3 Compliant\n ----------------------------------------------------------------------------- */\n.mj-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.75rem 1.5rem;\n font-size: 0.875rem;\n font-weight: 600;\n border: none;\n border-radius: var(--md-corner-full);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n white-space: nowrap;\n min-height: 44px;\n}\n\n.mj-btn:disabled {\n opacity: 0.38;\n cursor: not-allowed;\n}\n\n/* Primary Button - Dark, lightens on hover */\n.mj-btn-primary {\n background: var(--md-primary);\n color: var(--md-on-primary);\n box-shadow: var(--md-elevation-1);\n}\n\n.mj-btn-primary:hover:not(:disabled) {\n background: #3395C8;\n box-shadow: var(--md-elevation-2);\n}\n\n.mj-btn-primary:active:not(:disabled) {\n background: #4BA5D4;\n transform: scale(0.98);\n}\n\n/* Secondary Button - Light, fills with primary on hover */\n.mj-btn-secondary {\n background: var(--md-surface);\n color: var(--md-primary);\n border: 1px solid var(--md-outline);\n}\n\n.mj-btn-secondary:hover:not(:disabled) {\n background: var(--md-primary);\n color: var(--md-on-primary);\n border-color: var(--md-primary);\n}\n\n.mj-btn-secondary:active:not(:disabled) {\n background: #005A8C;\n border-color: #005A8C;\n}\n\n/* Ghost Button - Transparent, fills with primary on hover */\n.mj-btn-ghost {\n background: transparent;\n color: var(--md-on-surface-variant);\n}\n\n.mj-btn-ghost:hover:not(:disabled) {\n background: var(--md-primary);\n color: var(--md-on-primary);\n}\n\n.mj-btn-ghost:active:not(:disabled) {\n background: #005A8C;\n}\n\n/* Danger Button - Dark red, lightens on hover */\n.mj-btn-danger {\n background: var(--md-error);\n color: var(--md-on-error);\n box-shadow: var(--md-elevation-1);\n}\n\n.mj-btn-danger:hover:not(:disabled) {\n background: #E57373;\n box-shadow: var(--md-elevation-2);\n}\n\n.mj-btn-danger:active:not(:disabled) {\n background: #EF9A9A;\n transform: scale(0.98);\n}\n\n/* Small Button */\n.mj-btn-sm {\n padding: 0.5rem 0.875rem;\n font-size: 0.8125rem;\n min-height: 36px;\n}\n\n/* Ghost Danger Button */\n.mj-btn-ghost.mj-btn-danger {\n background: transparent;\n color: var(--md-error);\n box-shadow: none;\n}\n\n.mj-btn-ghost.mj-btn-danger:hover:not(:disabled) {\n background: var(--md-error-container);\n color: var(--md-error);\n}\n\n/* -----------------------------------------------------------------------------\n Utility Classes\n ----------------------------------------------------------------------------- */\n.text-danger {\n color: var(--md-error);\n}\n\n.text-success {\n color: var(--md-tertiary);\n}\n\n.text-warning {\n color: var(--md-secondary);\n}\n\n/* -----------------------------------------------------------------------------\n Animations\n ----------------------------------------------------------------------------- */\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n@keyframes slideUp {\n from {\n transform: translateY(20px);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Accessibility: Focus Indicators\n ----------------------------------------------------------------------------- */\n.mj-search-input:focus-visible,\n.mj-filter-select:focus-visible,\nbutton:focus-visible,\n.checkbox:focus-visible,\n.user-header:focus-visible {\n outline: 2px solid var(--md-primary);\n outline-offset: 2px;\n}\n\n/* -----------------------------------------------------------------------------\n Accessibility: Reduced Motion\n ----------------------------------------------------------------------------- */\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Large Desktop (1440px+)\n ----------------------------------------------------------------------------- */\n@media (min-width: 1440px) {\n .user-management-container {\n max-width: 1920px;\n margin: 0 auto;\n }\n\n .action-buttons {\n padding: 1.25rem 2rem;\n }\n\n .bulk-action-toolbar {\n padding: 1.25rem 2rem;\n }\n\n .mj-grid-4 {\n gap: 1.5rem;\n padding: 0 2rem 1rem 2rem;\n }\n\n .filters-section {\n margin: 0 2rem 1rem 2rem;\n padding: 1rem 1.5rem;\n }\n\n .scrollable-content {\n padding: 2rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Mobile (< 640px) - Compact adjustments\n ----------------------------------------------------------------------------- */\n@media (max-width: 639px) {\n .sticky-header {\n padding-bottom: 0;\n }\n\n .mj-grid-4 {\n grid-template-columns: repeat(2, 1fr);\n gap: 0.5rem;\n padding: 0 0.75rem 0.5rem 0.75rem;\n }\n\n .mj-card {\n padding: 0.75rem;\n gap: 0.75rem;\n }\n\n .stat-icon {\n width: 40px;\n height: 40px;\n font-size: 1.125rem;\n }\n\n .stat-content .stat-value {\n font-size: 1.5rem;\n }\n\n .stat-content .stat-label {\n font-size: 0.6875rem;\n }\n\n .filters-section {\n margin: 0 0.75rem 0.5rem 0.75rem;\n padding: 0.75rem;\n }\n\n .scrollable-content {\n padding: 0.75rem;\n }\n\n .content-area {\n padding: 0.875rem;\n border-radius: var(--md-corner-medium);\n }\n\n .list-header {\n padding: 0.625rem 0.75rem;\n }\n\n .empty-state {\n padding: 2rem 1rem;\n }\n\n .empty-state .empty-icon {\n font-size: 3rem;\n }\n\n .empty-state .empty-text {\n font-size: 1.0625rem;\n }\n\n .empty-state .empty-subtext {\n font-size: 0.875rem;\n }\n}\n"] }]
|
|
1289
1293
|
}], () => [], null); })();
|
|
1290
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(UserManagementComponent, { className: "UserManagementComponent", filePath: "src/lib/user-management/user-management.component.ts", lineNumber:
|
|
1294
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(UserManagementComponent, { className: "UserManagementComponent", filePath: "src/lib/user-management/user-management.component.ts", lineNumber: 30 }); })();
|
|
1291
1295
|
//# sourceMappingURL=user-management.component.js.map
|