@memberjunction/ng-explorer-settings 5.38.0 → 5.39.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/application-management/application-management.component.d.ts +8 -0
- package/dist/lib/application-management/application-management.component.d.ts.map +1 -1
- package/dist/lib/application-management/application-management.component.js +162 -136
- package/dist/lib/application-management/application-management.component.js.map +1 -1
- package/dist/lib/entity-permissions/entity-permissions.component.d.ts +4 -1
- package/dist/lib/entity-permissions/entity-permissions.component.d.ts.map +1 -1
- package/dist/lib/entity-permissions/entity-permissions.component.js +144 -141
- package/dist/lib/entity-permissions/entity-permissions.component.js.map +1 -1
- package/dist/lib/role-management/role-management.component.d.ts +8 -0
- package/dist/lib/role-management/role-management.component.d.ts.map +1 -1
- package/dist/lib/role-management/role-management.component.js +147 -121
- package/dist/lib/role-management/role-management.component.js.map +1 -1
- package/dist/lib/sql-logging/sql-logging.component.js +135 -134
- package/dist/lib/sql-logging/sql-logging.component.js.map +1 -1
- package/dist/lib/user-management/user-management.component.d.ts +4 -2
- package/dist/lib/user-management/user-management.component.d.ts.map +1 -1
- package/dist/lib/user-management/user-management.component.js +264 -258
- package/dist/lib/user-management/user-management.component.js.map +1 -1
- package/package.json +19 -19
|
@@ -16,21 +16,21 @@ import * as i2 from "@memberjunction/ng-ui-components";
|
|
|
16
16
|
import * as i3 from "./role-dialog/role-dialog.component";
|
|
17
17
|
import * as i4 from "@angular/common";
|
|
18
18
|
const _forTrack0 = ($index, $item) => $item.ID;
|
|
19
|
-
function
|
|
20
|
-
i0.ɵɵelementStart(0, "div",
|
|
21
|
-
i0.ɵɵelement(1, "mj-loading",
|
|
19
|
+
function RoleManagementComponent_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
20
|
+
i0.ɵɵelementStart(0, "div", 12);
|
|
21
|
+
i0.ɵɵelement(1, "mj-loading", 17);
|
|
22
22
|
i0.ɵɵelementEnd();
|
|
23
23
|
} }
|
|
24
|
-
function
|
|
24
|
+
function RoleManagementComponent_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
25
25
|
const _r1 = i0.ɵɵgetCurrentView();
|
|
26
|
-
i0.ɵɵelementStart(0, "div",
|
|
27
|
-
i0.ɵɵelement(2, "i",
|
|
28
|
-
i0.ɵɵelementStart(3, "p",
|
|
26
|
+
i0.ɵɵelementStart(0, "div", 13)(1, "div", 18);
|
|
27
|
+
i0.ɵɵelement(2, "i", 19);
|
|
28
|
+
i0.ɵɵelementStart(3, "p", 20);
|
|
29
29
|
i0.ɵɵtext(4);
|
|
30
30
|
i0.ɵɵelementEnd();
|
|
31
|
-
i0.ɵɵelementStart(5, "button",
|
|
32
|
-
i0.ɵɵlistener("click", function
|
|
33
|
-
i0.ɵɵelement(6, "i",
|
|
31
|
+
i0.ɵɵelementStart(5, "button", 21);
|
|
32
|
+
i0.ɵɵlistener("click", function RoleManagementComponent_Conditional_15_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.loadInitialData()); });
|
|
33
|
+
i0.ɵɵelement(6, "i", 22);
|
|
34
34
|
i0.ɵɵtext(7, " Try Again ");
|
|
35
35
|
i0.ɵɵelementEnd()()();
|
|
36
36
|
} if (rf & 2) {
|
|
@@ -38,56 +38,56 @@ function RoleManagementComponent_Conditional_16_Template(rf, ctx) { if (rf & 1)
|
|
|
38
38
|
i0.ɵɵadvance(4);
|
|
39
39
|
i0.ɵɵtextInterpolate(ctx_r1.error);
|
|
40
40
|
} }
|
|
41
|
-
function
|
|
41
|
+
function RoleManagementComponent_Conditional_16_For_2_Conditional_20_Template(rf, ctx) { if (rf & 1) {
|
|
42
42
|
const _r5 = i0.ɵɵgetCurrentView();
|
|
43
|
-
i0.ɵɵelementStart(0, "div",
|
|
44
|
-
i0.ɵɵlistener("click", function
|
|
45
|
-
i0.ɵɵelementStart(2, "span",
|
|
43
|
+
i0.ɵɵelementStart(0, "div", 41)(1, "div", 42);
|
|
44
|
+
i0.ɵɵlistener("click", function RoleManagementComponent_Conditional_16_For_2_Conditional_20_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r5); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
45
|
+
i0.ɵɵelementStart(2, "span", 33);
|
|
46
46
|
i0.ɵɵtext(3);
|
|
47
47
|
i0.ɵɵelementEnd();
|
|
48
|
-
i0.ɵɵelementStart(4, "div",
|
|
49
|
-
i0.ɵɵlistener("click", function
|
|
50
|
-
i0.ɵɵelement(6, "i",
|
|
51
|
-
i0.ɵɵelementStart(7, "span",
|
|
48
|
+
i0.ɵɵelementStart(4, "div", 43)(5, "button", 35);
|
|
49
|
+
i0.ɵɵlistener("click", function RoleManagementComponent_Conditional_16_For_2_Conditional_20_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r5); const role_r4 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.editRole(role_r4)); });
|
|
50
|
+
i0.ɵɵelement(6, "i", 36);
|
|
51
|
+
i0.ɵɵelementStart(7, "span", 44);
|
|
52
52
|
i0.ɵɵtext(8, "Edit");
|
|
53
53
|
i0.ɵɵelementEnd()();
|
|
54
|
-
i0.ɵɵelementStart(9, "button",
|
|
55
|
-
i0.ɵɵlistener("click", function
|
|
56
|
-
i0.ɵɵelement(10, "i",
|
|
57
|
-
i0.ɵɵelementStart(11, "span",
|
|
54
|
+
i0.ɵɵelementStart(9, "button", 37);
|
|
55
|
+
i0.ɵɵlistener("click", function RoleManagementComponent_Conditional_16_For_2_Conditional_20_Template_button_click_9_listener() { i0.ɵɵrestoreView(_r5); const role_r4 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.confirmDeleteRole(role_r4)); });
|
|
56
|
+
i0.ɵɵelement(10, "i", 38);
|
|
57
|
+
i0.ɵɵelementStart(11, "span", 44);
|
|
58
58
|
i0.ɵɵtext(12, "Delete");
|
|
59
59
|
i0.ɵɵelementEnd()()()();
|
|
60
|
-
i0.ɵɵelementStart(13, "div",
|
|
61
|
-
i0.ɵɵelement(15, "i",
|
|
62
|
-
i0.ɵɵelementStart(16, "span",
|
|
60
|
+
i0.ɵɵelementStart(13, "div", 45)(14, "div", 46);
|
|
61
|
+
i0.ɵɵelement(15, "i", 47);
|
|
62
|
+
i0.ɵɵelementStart(16, "span", 48);
|
|
63
63
|
i0.ɵɵtext(17, "Users:");
|
|
64
64
|
i0.ɵɵelementEnd();
|
|
65
|
-
i0.ɵɵelementStart(18, "span",
|
|
65
|
+
i0.ɵɵelementStart(18, "span", 49);
|
|
66
66
|
i0.ɵɵtext(19, "0");
|
|
67
67
|
i0.ɵɵelementEnd()();
|
|
68
|
-
i0.ɵɵelementStart(20, "div",
|
|
69
|
-
i0.ɵɵelement(21, "i",
|
|
70
|
-
i0.ɵɵelementStart(22, "span",
|
|
68
|
+
i0.ɵɵelementStart(20, "div", 46);
|
|
69
|
+
i0.ɵɵelement(21, "i", 50);
|
|
70
|
+
i0.ɵɵelementStart(22, "span", 48);
|
|
71
71
|
i0.ɵɵtext(23, "Created:");
|
|
72
72
|
i0.ɵɵelementEnd();
|
|
73
|
-
i0.ɵɵelementStart(24, "span",
|
|
73
|
+
i0.ɵɵelementStart(24, "span", 49);
|
|
74
74
|
i0.ɵɵtext(25);
|
|
75
75
|
i0.ɵɵpipe(26, "date");
|
|
76
76
|
i0.ɵɵelementEnd()();
|
|
77
|
-
i0.ɵɵelementStart(27, "div",
|
|
78
|
-
i0.ɵɵelement(28, "i",
|
|
79
|
-
i0.ɵɵelementStart(29, "span",
|
|
77
|
+
i0.ɵɵelementStart(27, "div", 46);
|
|
78
|
+
i0.ɵɵelement(28, "i", 51);
|
|
79
|
+
i0.ɵɵelementStart(29, "span", 48);
|
|
80
80
|
i0.ɵɵtext(30, "Updated:");
|
|
81
81
|
i0.ɵɵelementEnd();
|
|
82
|
-
i0.ɵɵelementStart(31, "span",
|
|
82
|
+
i0.ɵɵelementStart(31, "span", 49);
|
|
83
83
|
i0.ɵɵtext(32);
|
|
84
84
|
i0.ɵɵpipe(33, "date");
|
|
85
85
|
i0.ɵɵelementEnd()()();
|
|
86
|
-
i0.ɵɵelementStart(34, "div",
|
|
87
|
-
i0.ɵɵelement(36, "i",
|
|
86
|
+
i0.ɵɵelementStart(34, "div", 52)(35, "h4", 53);
|
|
87
|
+
i0.ɵɵelement(36, "i", 54);
|
|
88
88
|
i0.ɵɵtext(37, " Permissions Preview ");
|
|
89
89
|
i0.ɵɵelementEnd();
|
|
90
|
-
i0.ɵɵelementStart(38, "p",
|
|
90
|
+
i0.ɵɵelementStart(38, "p", 55);
|
|
91
91
|
i0.ɵɵtext(39, "Full permission management available in the Permissions tab");
|
|
92
92
|
i0.ɵɵelementEnd()()();
|
|
93
93
|
} if (rf & 2) {
|
|
@@ -108,36 +108,36 @@ function RoleManagementComponent_Conditional_17_For_2_Conditional_20_Template(rf
|
|
|
108
108
|
i0.ɵɵadvance(7);
|
|
109
109
|
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(33, 12, role_r4.__mj_UpdatedAt, "short"));
|
|
110
110
|
} }
|
|
111
|
-
function
|
|
111
|
+
function RoleManagementComponent_Conditional_16_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
112
112
|
const _r3 = i0.ɵɵgetCurrentView();
|
|
113
|
-
i0.ɵɵelementStart(0, "div",
|
|
114
|
-
i0.ɵɵlistener("click", function
|
|
115
|
-
i0.ɵɵelementStart(2, "div",
|
|
113
|
+
i0.ɵɵelementStart(0, "div", 25)(1, "div", 26);
|
|
114
|
+
i0.ɵɵlistener("click", function RoleManagementComponent_Conditional_16_For_2_Template_div_click_1_listener() { const role_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.toggleRoleExpansion(role_r4.ID)); })("keydown.enter", function RoleManagementComponent_Conditional_16_For_2_Template_div_keydown_enter_1_listener() { const role_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.toggleRoleExpansion(role_r4.ID)); })("keydown.space", function RoleManagementComponent_Conditional_16_For_2_Template_div_keydown_space_1_listener($event) { const role_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); ctx_r1.toggleRoleExpansion(role_r4.ID); return i0.ɵɵresetView($event.preventDefault()); });
|
|
115
|
+
i0.ɵɵelementStart(2, "div", 27)(3, "div", 28);
|
|
116
116
|
i0.ɵɵelement(4, "i");
|
|
117
117
|
i0.ɵɵelementEnd();
|
|
118
|
-
i0.ɵɵelementStart(5, "div",
|
|
118
|
+
i0.ɵɵelementStart(5, "div", 29)(6, "h3", 30);
|
|
119
119
|
i0.ɵɵtext(7);
|
|
120
120
|
i0.ɵɵelementEnd();
|
|
121
|
-
i0.ɵɵelementStart(8, "p",
|
|
121
|
+
i0.ɵɵelementStart(8, "p", 31);
|
|
122
122
|
i0.ɵɵtext(9);
|
|
123
123
|
i0.ɵɵelementEnd()()();
|
|
124
|
-
i0.ɵɵelementStart(10, "div",
|
|
124
|
+
i0.ɵɵelementStart(10, "div", 32)(11, "span", 33);
|
|
125
125
|
i0.ɵɵtext(12);
|
|
126
126
|
i0.ɵɵelementEnd();
|
|
127
|
-
i0.ɵɵelementStart(13, "div",
|
|
128
|
-
i0.ɵɵlistener("click", function
|
|
129
|
-
i0.ɵɵelementStart(14, "button",
|
|
130
|
-
i0.ɵɵlistener("click", function
|
|
131
|
-
i0.ɵɵelement(15, "i",
|
|
127
|
+
i0.ɵɵelementStart(13, "div", 34);
|
|
128
|
+
i0.ɵɵlistener("click", function RoleManagementComponent_Conditional_16_For_2_Template_div_click_13_listener($event) { i0.ɵɵrestoreView(_r3); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
129
|
+
i0.ɵɵelementStart(14, "button", 35);
|
|
130
|
+
i0.ɵɵlistener("click", function RoleManagementComponent_Conditional_16_For_2_Template_button_click_14_listener() { const role_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.editRole(role_r4)); });
|
|
131
|
+
i0.ɵɵelement(15, "i", 36);
|
|
132
132
|
i0.ɵɵelementEnd();
|
|
133
|
-
i0.ɵɵelementStart(16, "button",
|
|
134
|
-
i0.ɵɵlistener("click", function
|
|
135
|
-
i0.ɵɵelement(17, "i",
|
|
133
|
+
i0.ɵɵelementStart(16, "button", 37);
|
|
134
|
+
i0.ɵɵlistener("click", function RoleManagementComponent_Conditional_16_For_2_Template_button_click_16_listener() { const role_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.confirmDeleteRole(role_r4)); });
|
|
135
|
+
i0.ɵɵelement(17, "i", 38);
|
|
136
136
|
i0.ɵɵelementEnd()();
|
|
137
|
-
i0.ɵɵelementStart(18, "button",
|
|
138
|
-
i0.ɵɵelement(19, "i",
|
|
137
|
+
i0.ɵɵelementStart(18, "button", 39);
|
|
138
|
+
i0.ɵɵelement(19, "i", 40);
|
|
139
139
|
i0.ɵɵelementEnd()()();
|
|
140
|
-
i0.ɵɵconditionalCreate(20,
|
|
140
|
+
i0.ɵɵconditionalCreate(20, RoleManagementComponent_Conditional_16_For_2_Conditional_20_Template, 40, 15, "div", 41);
|
|
141
141
|
i0.ɵɵelementEnd();
|
|
142
142
|
} if (rf & 2) {
|
|
143
143
|
const role_r4 = ctx.$implicit;
|
|
@@ -165,20 +165,20 @@ function RoleManagementComponent_Conditional_17_For_2_Template(rf, ctx) { if (rf
|
|
|
165
165
|
i0.ɵɵadvance(4);
|
|
166
166
|
i0.ɵɵconditional(ctx_r1.isRoleExpanded(role_r4.ID) ? 20 : -1);
|
|
167
167
|
} }
|
|
168
|
-
function
|
|
169
|
-
i0.ɵɵelementStart(0, "div",
|
|
170
|
-
i0.ɵɵelement(1, "i",
|
|
171
|
-
i0.ɵɵelementStart(2, "p",
|
|
168
|
+
function RoleManagementComponent_Conditional_16_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
169
|
+
i0.ɵɵelementStart(0, "div", 24);
|
|
170
|
+
i0.ɵɵelement(1, "i", 56);
|
|
171
|
+
i0.ɵɵelementStart(2, "p", 57);
|
|
172
172
|
i0.ɵɵtext(3, "No roles found");
|
|
173
173
|
i0.ɵɵelementEnd();
|
|
174
|
-
i0.ɵɵelementStart(4, "p",
|
|
174
|
+
i0.ɵɵelementStart(4, "p", 58);
|
|
175
175
|
i0.ɵɵtext(5, "Try adjusting your filters or create a new role");
|
|
176
176
|
i0.ɵɵelementEnd()();
|
|
177
177
|
} }
|
|
178
|
-
function
|
|
179
|
-
i0.ɵɵelementStart(0, "div",
|
|
180
|
-
i0.ɵɵrepeaterCreate(1,
|
|
181
|
-
i0.ɵɵconditionalCreate(3,
|
|
178
|
+
function RoleManagementComponent_Conditional_16_Template(rf, ctx) { if (rf & 1) {
|
|
179
|
+
i0.ɵɵelementStart(0, "div", 14);
|
|
180
|
+
i0.ɵɵrepeaterCreate(1, RoleManagementComponent_Conditional_16_For_2_Template, 21, 16, "div", 23, _forTrack0);
|
|
181
|
+
i0.ɵɵconditionalCreate(3, RoleManagementComponent_Conditional_16_Conditional_3_Template, 6, 0, "div", 24);
|
|
182
182
|
i0.ɵɵelementEnd();
|
|
183
183
|
} if (rf & 2) {
|
|
184
184
|
const ctx_r1 = i0.ɵɵnextContext();
|
|
@@ -187,45 +187,45 @@ function RoleManagementComponent_Conditional_17_Template(rf, ctx) { if (rf & 1)
|
|
|
187
187
|
i0.ɵɵadvance(2);
|
|
188
188
|
i0.ɵɵconditional(ctx_r1.filteredRoles.length === 0 ? 3 : -1);
|
|
189
189
|
} }
|
|
190
|
-
function
|
|
191
|
-
i0.ɵɵelement(0, "i",
|
|
190
|
+
function RoleManagementComponent_Conditional_18_Conditional_19_Template(rf, ctx) { if (rf & 1) {
|
|
191
|
+
i0.ɵɵelement(0, "i", 72);
|
|
192
192
|
i0.ɵɵtext(1, " Deleting... ");
|
|
193
193
|
} }
|
|
194
|
-
function
|
|
195
|
-
i0.ɵɵelement(0, "i",
|
|
194
|
+
function RoleManagementComponent_Conditional_18_Conditional_20_Template(rf, ctx) { if (rf & 1) {
|
|
195
|
+
i0.ɵɵelement(0, "i", 38);
|
|
196
196
|
i0.ɵɵtext(1, " Delete Role ");
|
|
197
197
|
} }
|
|
198
|
-
function
|
|
198
|
+
function RoleManagementComponent_Conditional_18_Template(rf, ctx) { if (rf & 1) {
|
|
199
199
|
const _r6 = i0.ɵɵgetCurrentView();
|
|
200
|
-
i0.ɵɵelementStart(0, "div",
|
|
201
|
-
i0.ɵɵlistener("click", function
|
|
202
|
-
i0.ɵɵelementStart(1, "div",
|
|
203
|
-
i0.ɵɵlistener("click", function
|
|
204
|
-
i0.ɵɵelementStart(2, "div",
|
|
205
|
-
i0.ɵɵelement(4, "i",
|
|
200
|
+
i0.ɵɵelementStart(0, "div", 59);
|
|
201
|
+
i0.ɵɵlistener("click", function RoleManagementComponent_Conditional_18_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.showDeleteConfirm = false); });
|
|
202
|
+
i0.ɵɵelementStart(1, "div", 60);
|
|
203
|
+
i0.ɵɵlistener("click", function RoleManagementComponent_Conditional_18_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r6); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
204
|
+
i0.ɵɵelementStart(2, "div", 61)(3, "h3", 62);
|
|
205
|
+
i0.ɵɵelement(4, "i", 63);
|
|
206
206
|
i0.ɵɵtext(5, " Confirm Delete ");
|
|
207
207
|
i0.ɵɵelementEnd();
|
|
208
|
-
i0.ɵɵelementStart(6, "button",
|
|
209
|
-
i0.ɵɵlistener("click", function
|
|
210
|
-
i0.ɵɵelement(7, "i",
|
|
208
|
+
i0.ɵɵelementStart(6, "button", 64);
|
|
209
|
+
i0.ɵɵlistener("click", function RoleManagementComponent_Conditional_18_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.showDeleteConfirm = false); });
|
|
210
|
+
i0.ɵɵelement(7, "i", 65);
|
|
211
211
|
i0.ɵɵelementEnd()();
|
|
212
|
-
i0.ɵɵelementStart(8, "div",
|
|
212
|
+
i0.ɵɵelementStart(8, "div", 66)(9, "p");
|
|
213
213
|
i0.ɵɵtext(10, "Are you sure you want to delete the role ");
|
|
214
214
|
i0.ɵɵelementStart(11, "strong");
|
|
215
215
|
i0.ɵɵtext(12);
|
|
216
216
|
i0.ɵɵelementEnd();
|
|
217
217
|
i0.ɵɵtext(13, "?");
|
|
218
218
|
i0.ɵɵelementEnd();
|
|
219
|
-
i0.ɵɵelementStart(14, "p",
|
|
220
|
-
i0.ɵɵelement(15, "i",
|
|
219
|
+
i0.ɵɵelementStart(14, "p", 67);
|
|
220
|
+
i0.ɵɵelement(15, "i", 68);
|
|
221
221
|
i0.ɵɵtext(16, " This will affect all users assigned to this role. ");
|
|
222
222
|
i0.ɵɵelementEnd()();
|
|
223
|
-
i0.ɵɵelementStart(17, "div",
|
|
224
|
-
i0.ɵɵlistener("click", function
|
|
225
|
-
i0.ɵɵconditionalCreate(19,
|
|
223
|
+
i0.ɵɵelementStart(17, "div", 69)(18, "button", 70);
|
|
224
|
+
i0.ɵɵlistener("click", function RoleManagementComponent_Conditional_18_Template_button_click_18_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.deleteRole()); });
|
|
225
|
+
i0.ɵɵconditionalCreate(19, RoleManagementComponent_Conditional_18_Conditional_19_Template, 2, 0)(20, RoleManagementComponent_Conditional_18_Conditional_20_Template, 2, 0);
|
|
226
226
|
i0.ɵɵelementEnd();
|
|
227
|
-
i0.ɵɵelementStart(21, "button",
|
|
228
|
-
i0.ɵɵlistener("click", function
|
|
227
|
+
i0.ɵɵelementStart(21, "button", 71);
|
|
228
|
+
i0.ɵɵlistener("click", function RoleManagementComponent_Conditional_18_Template_button_click_21_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.showDeleteConfirm = false); });
|
|
229
229
|
i0.ɵɵtext(22, " Cancel ");
|
|
230
230
|
i0.ɵɵelementEnd()()()();
|
|
231
231
|
} if (rf & 2) {
|
|
@@ -374,6 +374,37 @@ let RoleManagementComponent = class RoleManagementComponent extends BaseDashboar
|
|
|
374
374
|
this.cdr.markForCheck();
|
|
375
375
|
}
|
|
376
376
|
}
|
|
377
|
+
// -- Concise chrome: one Filter popover (Type) + applied-filter chips -------
|
|
378
|
+
get filterFields() {
|
|
379
|
+
return [
|
|
380
|
+
{
|
|
381
|
+
key: 'type',
|
|
382
|
+
type: 'chips',
|
|
383
|
+
label: 'Type',
|
|
384
|
+
chipOptions: [
|
|
385
|
+
{ text: 'All', value: 'all' },
|
|
386
|
+
{ text: 'System', value: 'system' },
|
|
387
|
+
{ text: 'Custom', value: 'custom' },
|
|
388
|
+
],
|
|
389
|
+
},
|
|
390
|
+
];
|
|
391
|
+
}
|
|
392
|
+
get filterValues() {
|
|
393
|
+
return { type: this.filters$.value.type };
|
|
394
|
+
}
|
|
395
|
+
/** Total active filters (Type) — drives the Filter button badge. */
|
|
396
|
+
get TotalActiveFilterCount() {
|
|
397
|
+
return this.filters$.value.type !== 'all' ? 1 : 0;
|
|
398
|
+
}
|
|
399
|
+
onFilterPanelChange(values) {
|
|
400
|
+
if ('type' in values) {
|
|
401
|
+
this.updateFilter({ type: values['type'] || 'all' });
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
/** Clear all filters (Type); search persists. */
|
|
405
|
+
clearAllAppliedFilters() {
|
|
406
|
+
this.updateFilter({ type: 'all' });
|
|
407
|
+
}
|
|
377
408
|
toggleRoleExpansion(roleId) {
|
|
378
409
|
this.expandedRoleId = this.expandedRoleId === roleId ? null : roleId;
|
|
379
410
|
}
|
|
@@ -451,63 +482,58 @@ let RoleManagementComponent = class RoleManagementComponent extends BaseDashboar
|
|
|
451
482
|
}
|
|
452
483
|
}
|
|
453
484
|
static ɵfac = function RoleManagementComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || RoleManagementComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i0.NgZone)); };
|
|
454
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RoleManagementComponent, selectors: [["mj-role-management"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls:
|
|
485
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RoleManagementComponent, selectors: [["mj-role-management"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 19, vars: 14, consts: [["AriaLabel", "Filter roles", "Title", "Roles", "Subtitle", "Define system and custom roles for permissions"], ["meta", ""], ["Label", "roles", 3, "Count", "Total"], ["toolbar", ""], ["Placeholder", "Search roles by name or description...", 3, "ValueChange", "Value"], ["Label", "Filters", "Icon", "fa-solid fa-filter", 3, "ClearAllRequested", "ActiveCount", "ShowClearAll"], [3, "ValuesChange", "Reset", "Fields", "Values"], ["actions", ""], [3, "Clicked", "Loading"], ["mjButton", "", "variant", "primary", "size", "sm", "aria-label", "Add new role", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "mj-action-label"], ["role", "status", "aria-live", "polite", "aria-busy", "true", 1, "loading-container"], ["role", "alert", "aria-live", "assertive", 1, "error-container"], ["role", "list", "aria-label", "Roles list", 1, "roles-list"], [3, "result", "data", "visible"], ["role", "presentation", 1, "modal-backdrop"], ["text", "Loading roles...", "size", "medium"], [1, "error-content"], ["aria-hidden", "true", 1, "fa-solid", "fa-exclamation-triangle", "error-icon"], [1, "error-message"], ["mjButton", "", "variant", "primary", "aria-label", "Retry loading roles", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-refresh"], ["role", "listitem", 1, "role-card", 3, "expanded"], ["role", "status", 1, "empty-state"], ["role", "listitem", 1, "role-card"], ["tabindex", "0", "role", "button", 1, "role-header", 3, "click", "keydown.enter", "keydown.space"], [1, "role-info"], ["aria-hidden", "true", 1, "role-icon-wrapper"], [1, "role-details"], [1, "role-name"], [1, "role-description"], [1, "role-meta"], [1, "role-type-badge"], [1, "role-actions", 3, "click"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Edit", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-edit"], ["mjButton", "", "variant", "danger", "size", "sm", "title", "Delete", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-trash"], ["aria-label", "Toggle details", 1, "expand-btn"], ["aria-hidden", "true", 1, "fa-solid", "fa-chevron-down"], [1, "role-content"], [1, "mobile-actions-bar", 3, "click"], [1, "mobile-action-buttons"], [1, "btn-label"], [1, "role-stats"], [1, "stat-item"], ["aria-hidden", "true", 1, "fa-solid", "fa-users"], [1, "stat-label"], [1, "stat-value"], ["aria-hidden", "true", 1, "fa-solid", "fa-calendar"], ["aria-hidden", "true", 1, "fa-solid", "fa-clock"], [1, "permissions-preview"], [1, "section-title"], ["aria-hidden", "true", 1, "fa-solid", "fa-key"], [1, "permissions-note"], ["aria-hidden", "true", 1, "fa-solid", "fa-user-tag", "empty-icon"], [1, "empty-text"], [1, "empty-subtext"], ["role", "presentation", 1, "modal-backdrop", 3, "click"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "delete-dialog-title", "aria-describedby", "delete-dialog-desc", 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"], ["aria-hidden", "true", 1, "fa-solid", "fa-times"], ["id", "delete-dialog-desc", 1, "modal-body"], [1, "text-warning"], ["aria-hidden", "true", 1, "fa-solid", "fa-warning"], [1, "modal-footer"], ["mjButton", "", "variant", "danger", 3, "click", "disabled"], ["mjButton", "", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-spinner", "fa-spin"]], template: function RoleManagementComponent_Template(rf, ctx) { if (rf & 1) {
|
|
455
486
|
i0.ɵɵelementStart(0, "mj-page-header-interior", 0)(1, "div", 1);
|
|
456
487
|
i0.ɵɵelement(2, "mj-stat-badge", 2);
|
|
457
488
|
i0.ɵɵelementEnd();
|
|
458
489
|
i0.ɵɵelementStart(3, "div", 3)(4, "mj-page-search", 4);
|
|
459
490
|
i0.ɵɵlistener("ValueChange", function RoleManagementComponent_Template_mj_page_search_ValueChange_4_listener($event) { return ctx.updateFilter({ search: $event }); });
|
|
460
491
|
i0.ɵɵelementEnd();
|
|
461
|
-
i0.ɵɵelementStart(5, "
|
|
462
|
-
i0.ɵɵlistener("
|
|
463
|
-
i0.ɵɵ
|
|
464
|
-
i0.ɵɵ
|
|
465
|
-
i0.ɵɵlistener("Clicked", function RoleManagementComponent_Template_mj_filter_chip_Clicked_7_listener() { return ctx.onTypeFilterChange("system"); });
|
|
466
|
-
i0.ɵɵelementEnd();
|
|
467
|
-
i0.ɵɵelementStart(8, "mj-filter-chip", 8);
|
|
468
|
-
i0.ɵɵlistener("Clicked", function RoleManagementComponent_Template_mj_filter_chip_Clicked_8_listener() { return ctx.onTypeFilterChange("custom"); });
|
|
492
|
+
i0.ɵɵelementStart(5, "mj-filter-popover", 5);
|
|
493
|
+
i0.ɵɵlistener("ClearAllRequested", function RoleManagementComponent_Template_mj_filter_popover_ClearAllRequested_5_listener() { return ctx.clearAllAppliedFilters(); });
|
|
494
|
+
i0.ɵɵelementStart(6, "mj-filter-panel", 6);
|
|
495
|
+
i0.ɵɵlistener("ValuesChange", function RoleManagementComponent_Template_mj_filter_panel_ValuesChange_6_listener($event) { return ctx.onFilterPanelChange($event); })("Reset", function RoleManagementComponent_Template_mj_filter_panel_Reset_6_listener() { return ctx.clearAllAppliedFilters(); });
|
|
469
496
|
i0.ɵɵelementEnd()()();
|
|
470
|
-
i0.ɵɵelementStart(
|
|
471
|
-
i0.ɵɵlistener("Clicked", function
|
|
497
|
+
i0.ɵɵelementStart(7, "div", 7)(8, "mj-refresh-button", 8);
|
|
498
|
+
i0.ɵɵlistener("Clicked", function RoleManagementComponent_Template_mj_refresh_button_Clicked_8_listener() { return ctx.refreshData(); });
|
|
472
499
|
i0.ɵɵelementEnd();
|
|
473
|
-
i0.ɵɵelementStart(
|
|
474
|
-
i0.ɵɵlistener("click", function
|
|
475
|
-
i0.ɵɵelement(
|
|
476
|
-
i0.ɵɵ
|
|
477
|
-
i0.ɵɵ
|
|
478
|
-
i0.ɵɵ
|
|
479
|
-
i0.ɵɵ
|
|
480
|
-
i0.ɵɵconditionalCreate(
|
|
481
|
-
i0.ɵɵconditionalCreate(
|
|
500
|
+
i0.ɵɵelementStart(9, "button", 9);
|
|
501
|
+
i0.ɵɵlistener("click", function RoleManagementComponent_Template_button_click_9_listener() { return ctx.createNewRole(); });
|
|
502
|
+
i0.ɵɵelement(10, "i", 10);
|
|
503
|
+
i0.ɵɵelementStart(11, "span", 11);
|
|
504
|
+
i0.ɵɵtext(12, "Add Role");
|
|
505
|
+
i0.ɵɵelementEnd()()()();
|
|
506
|
+
i0.ɵɵelementStart(13, "mj-page-body-interior");
|
|
507
|
+
i0.ɵɵconditionalCreate(14, RoleManagementComponent_Conditional_14_Template, 2, 0, "div", 12);
|
|
508
|
+
i0.ɵɵconditionalCreate(15, RoleManagementComponent_Conditional_15_Template, 8, 1, "div", 13);
|
|
509
|
+
i0.ɵɵconditionalCreate(16, RoleManagementComponent_Conditional_16_Template, 4, 1, "div", 14);
|
|
482
510
|
i0.ɵɵelementEnd();
|
|
483
|
-
i0.ɵɵelementStart(
|
|
484
|
-
i0.ɵɵlistener("result", function
|
|
511
|
+
i0.ɵɵelementStart(17, "mj-role-dialog", 15);
|
|
512
|
+
i0.ɵɵlistener("result", function RoleManagementComponent_Template_mj_role_dialog_result_17_listener($event) { return ctx.onRoleDialogResult($event); });
|
|
485
513
|
i0.ɵɵelementEnd();
|
|
486
|
-
i0.ɵɵconditionalCreate(
|
|
514
|
+
i0.ɵɵconditionalCreate(18, RoleManagementComponent_Conditional_18_Template, 23, 3, "div", 16);
|
|
487
515
|
} if (rf & 2) {
|
|
488
516
|
i0.ɵɵadvance(2);
|
|
489
517
|
i0.ɵɵproperty("Count", ctx.filteredRoles.length)("Total", ctx.roles.length);
|
|
490
518
|
i0.ɵɵadvance(2);
|
|
491
519
|
i0.ɵɵproperty("Value", ctx.filters$.value.search);
|
|
492
|
-
i0.ɵɵadvance(2);
|
|
493
|
-
i0.ɵɵproperty("Active", ctx.filters$.value.type === "all");
|
|
494
520
|
i0.ɵɵadvance();
|
|
495
|
-
i0.ɵɵproperty("
|
|
521
|
+
i0.ɵɵproperty("ActiveCount", ctx.TotalActiveFilterCount)("ShowClearAll", ctx.TotalActiveFilterCount > 0);
|
|
496
522
|
i0.ɵɵadvance();
|
|
497
|
-
i0.ɵɵproperty("
|
|
523
|
+
i0.ɵɵproperty("Fields", ctx.filterFields)("Values", ctx.filterValues);
|
|
498
524
|
i0.ɵɵadvance(2);
|
|
499
525
|
i0.ɵɵproperty("Loading", ctx.isLoading);
|
|
500
|
-
i0.ɵɵadvance(
|
|
501
|
-
i0.ɵɵconditional(ctx.isLoading ?
|
|
526
|
+
i0.ɵɵadvance(6);
|
|
527
|
+
i0.ɵɵconditional(ctx.isLoading ? 14 : -1);
|
|
502
528
|
i0.ɵɵadvance();
|
|
503
|
-
i0.ɵɵconditional(ctx.error && !ctx.isLoading ?
|
|
529
|
+
i0.ɵɵconditional(ctx.error && !ctx.isLoading ? 15 : -1);
|
|
504
530
|
i0.ɵɵadvance();
|
|
505
|
-
i0.ɵɵconditional(!ctx.isLoading && !ctx.error ?
|
|
531
|
+
i0.ɵɵconditional(!ctx.isLoading && !ctx.error ? 16 : -1);
|
|
506
532
|
i0.ɵɵadvance();
|
|
507
533
|
i0.ɵɵproperty("data", ctx.roleDialogData)("visible", ctx.showRoleDialog);
|
|
508
534
|
i0.ɵɵadvance();
|
|
509
|
-
i0.ɵɵconditional(ctx.showDeleteConfirm && ctx.selectedRole ?
|
|
510
|
-
} }, dependencies: [i1.LoadingComponent, i2.MJButtonDirective, i2.MJPageHeaderInteriorComponent, i2.MJPageBodyInteriorComponent, i2.MJPageSearchComponent, i2.MJFilterChipComponent, i2.MJRefreshButtonComponent, i2.MJStatBadgeComponent, i3.RoleDialogComponent, i4.DatePipe], styles: ["\n\n\n\n\n\n\n\n\n\n\n[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n overflow: hidden;\n}\n\n\n\n\n\n.sticky-header[_ngcontent-%COMP%] {\n flex-shrink: 0;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n box-shadow: var(--mj-shadow-sm);\n z-index: 10;\n}\n\n\n\n\n\n.scrollable-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n -webkit-overflow-scrolling: touch;\n padding: 1rem;\n background: var(--mj-bg-surface-sunken);\n}\n\n@media (min-width: 768px) {\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 1.5rem 2rem;\n }\n}\n\n@media (min-width: 1024px) {\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 2rem;\n }\n}\n\n@media (min-width: 1440px) {\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 2rem 2.5rem;\n }\n}\n\n\n\n\n\n.action-buttons[_ngcontent-%COMP%] {\n flex-shrink: 0;\n display: flex;\n gap: 0.75rem;\n justify-content: flex-end;\n padding: 0.75rem 1rem;\n background: var(--mj-bg-page);\n}\n\n@media (min-width: 768px) {\n .action-buttons[_ngcontent-%COMP%] {\n padding: 1rem 1.5rem;\n }\n}\n\n@media (max-width: 639px) {\n .action-buttons[_ngcontent-%COMP%] {\n justify-content: center;\n flex-wrap: wrap;\n }\n}\n\n\n\n\n\n.mj-grid-4[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 0.5rem;\n padding: 0 1rem 0.75rem 1rem;\n background: var(--mj-bg-page);\n}\n\n@media (min-width: 768px) {\n .mj-grid-4[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n gap: 1rem;\n }\n}\n\n\n\n.mj-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-lg);\n padding: 1rem;\n box-shadow: var(--mj-shadow-sm);\n display: flex;\n align-items: center;\n gap: 1rem;\n border: 1px solid var(--mj-border-default);\n cursor: default;\n pointer-events: none;\n}\n\n@media (min-width: 768px) {\n .mj-card[_ngcontent-%COMP%] {\n padding: 1rem 1.25rem;\n }\n}\n\n\n\n\n\n.stat-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n border-radius: var(--mj-radius-lg);\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[_ngcontent-%COMP%] {\n width: 52px;\n height: 52px;\n font-size: 1.375rem;\n }\n}\n\n.stat-icon-total[_ngcontent-%COMP%] {\n background: var(--mj-color-accent-300);\n color: var(--mj-brand-primary);\n}\n\n.stat-icon-active[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-status-success);\n}\n\n\n\n\n\n.stat-content[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.stat-content[_ngcontent-%COMP%] .stat-value[_ngcontent-%COMP%] {\n font-size: 1.75rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 768px) {\n .stat-content[_ngcontent-%COMP%] .stat-value[_ngcontent-%COMP%] {\n font-size: 2rem;\n }\n}\n\n.stat-content[_ngcontent-%COMP%] .stat-label[_ngcontent-%COMP%] {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin-top: 0.25rem;\n}\n\n@media (min-width: 768px) {\n .stat-content[_ngcontent-%COMP%] .stat-label[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n }\n}\n\n\n\n\n\n.filters-section[_ngcontent-%COMP%] {\n flex-shrink: 0;\n background: var(--mj-bg-surface-card);\n margin: 0 1rem 0.75rem 1rem;\n padding: 0.75rem;\n border-radius: var(--mj-radius-lg);\n border: 1px solid var(--mj-border-default);\n}\n\n@media (min-width: 768px) {\n .filters-section[_ngcontent-%COMP%] {\n margin: 0 1.5rem 1rem 1.5rem;\n padding: 1rem 1.25rem;\n }\n}\n\n.filters-row[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n gap: 1.5rem;\n }\n}\n\n.mj-filter-group[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.mj-filter-label[_ngcontent-%COMP%] {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.mj-filter-buttons[_ngcontent-%COMP%] {\n display: flex;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-full);\n padding: 4px;\n gap: 2px;\n}\n\n\n\n\n\n.mj-search[_ngcontent-%COMP%] {\n position: relative;\n flex: 1;\n min-width: 200px;\n}\n\n@media (min-width: 640px) {\n .mj-search[_ngcontent-%COMP%] {\n min-width: 280px;\n }\n}\n\n@media (min-width: 1024px) {\n .mj-search[_ngcontent-%COMP%] {\n min-width: 400px;\n max-width: 600px;\n }\n}\n\n@media (min-width: 1440px) {\n .mj-search[_ngcontent-%COMP%] {\n min-width: 500px;\n max-width: 800px;\n }\n}\n\n.mj-search[_ngcontent-%COMP%] .mj-search-icon[_ngcontent-%COMP%] {\n position: absolute;\n left: 1rem;\n top: 50%;\n transform: translateY(-50%);\n color: var(--mj-text-secondary);\n font-size: 1rem;\n pointer-events: none;\n transition: color 0.2s ease;\n}\n\n@media (min-width: 1024px) {\n .mj-search[_ngcontent-%COMP%] .mj-search-icon[_ngcontent-%COMP%] {\n left: 1.25rem;\n font-size: 1.125rem;\n }\n}\n\n.mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%] {\n width: 100%;\n padding: 0.875rem 1rem 0.875rem 2.75rem;\n border: 2px solid var(--mj-border-default);\n border-radius: var(--mj-radius-full);\n font-size: 1rem;\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n}\n\n@media (min-width: 1024px) {\n .mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%] {\n padding: 1rem 1.25rem 1rem 3.25rem;\n font-size: 1.0625rem;\n }\n}\n\n@media (min-width: 1440px) {\n .mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%] {\n padding: 1.125rem 1.5rem 1.125rem 3.5rem;\n }\n}\n\n.mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%]::placeholder {\n color: var(--mj-text-secondary);\n}\n\n.mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n}\n\n.mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px rgba(0, 118, 182, 0.2);\n background: var(--mj-bg-page);\n}\n\n.mj-search[_ngcontent-%COMP%]:focus-within .mj-search-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n\n\n\n\n.content-area[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n overflow: visible;\n position: relative;\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-xl);\n box-shadow: var(--mj-shadow-sm);\n padding: 1rem;\n border: 1px solid var(--mj-border-default);\n}\n\n@media (min-width: 768px) {\n .content-area[_ngcontent-%COMP%] {\n padding: 1.5rem;\n }\n}\n\n\n\n\n\n.expand-btn[_ngcontent-%COMP%] {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1rem;\n cursor: pointer;\n transition: all 0.2s ease;\n border-radius: var(--mj-radius-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[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.expand-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1);\n}\n\n\n\n\n\n.status-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.875rem;\n border-radius: var(--mj-radius-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[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n border: 1px solid var(--mj-status-success);\n}\n\n.status-badge.status-inactive[_ngcontent-%COMP%] {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n border: 1px solid var(--mj-status-error);\n}\n\n.status-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 0.625rem;\n}\n\n\n\n\n\n.mobile-actions-bar[_ngcontent-%COMP%] {\n display: none;\n}\n\n.mobile-action-buttons[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.5rem;\n}\n\n\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n text-align: center;\n padding: 4rem 2rem;\n}\n\n.empty-state[_ngcontent-%COMP%] .empty-icon[_ngcontent-%COMP%] {\n font-size: 4rem;\n color: var(--mj-border-default);\n margin-bottom: 1.5rem;\n}\n\n.empty-state[_ngcontent-%COMP%] .empty-text[_ngcontent-%COMP%] {\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.5rem 0;\n}\n\n.empty-state[_ngcontent-%COMP%] .empty-subtext[_ngcontent-%COMP%] {\n font-size: 1rem;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n\n\n\n\n.loading-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 4rem 2rem;\n}\n\n\n\n\n\n.error-container[_ngcontent-%COMP%] {\n text-align: center;\n padding: 4rem 2rem;\n}\n\n.error-container[_ngcontent-%COMP%] .error-content[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1rem;\n}\n\n.error-container[_ngcontent-%COMP%] .error-icon[_ngcontent-%COMP%] {\n font-size: 3.5rem;\n color: var(--mj-status-error);\n margin-bottom: 1rem;\n}\n\n.error-container[_ngcontent-%COMP%] .error-message[_ngcontent-%COMP%] {\n font-size: 1.0625rem;\n color: var(--mj-text-primary);\n margin: 0 0 1.5rem 0;\n}\n\n\n\n\n\n.modal-backdrop[_ngcontent-%COMP%] {\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: _ngcontent-%COMP%_fadeIn 0.2s cubic-bezier(0, 0, 0.2, 1);\n}\n\n.modal-dialog[_ngcontent-%COMP%] {\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-2xl);\n box-shadow: var(--mj-shadow-2xl);\n max-width: 500px;\n width: 100%;\n max-height: 90vh;\n overflow: hidden;\n animation: _ngcontent-%COMP%_slideUp 0.3s cubic-bezier(0, 0, 0.2, 1);\n}\n\n.modal-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1.5rem;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.modal-header[_ngcontent-%COMP%] .modal-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 1.375rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.modal-header[_ngcontent-%COMP%] .modal-close[_ngcontent-%COMP%] {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1.25rem;\n cursor: pointer;\n border-radius: var(--mj-radius-full);\n transition: all 0.2s ease;\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.modal-header[_ngcontent-%COMP%] .modal-close[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.modal-body[_ngcontent-%COMP%] {\n padding: 1.5rem;\n}\n\n.modal-body[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n font-size: 1rem;\n color: var(--mj-text-primary);\n margin: 0 0 1rem 0;\n line-height: 1.5;\n}\n\n.modal-body[_ngcontent-%COMP%] p[_ngcontent-%COMP%]:last-child {\n margin-bottom: 0;\n}\n\n.modal-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-start;\n gap: 0.75rem;\n padding: 1.5rem;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n}\n\n@media (max-width: 639px) {\n .modal-dialog[_ngcontent-%COMP%] {\n width: 95%;\n max-height: 85vh;\n }\n\n .modal-header[_ngcontent-%COMP%] {\n padding: 1rem;\n }\n\n .modal-header[_ngcontent-%COMP%] .modal-title[_ngcontent-%COMP%] {\n font-size: 1.125rem;\n }\n\n .modal-body[_ngcontent-%COMP%] {\n padding: 1rem;\n }\n\n .modal-footer[_ngcontent-%COMP%] {\n padding: 1rem;\n flex-direction: column;\n }\n\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n.filter-button[_ngcontent-%COMP%] {\n display: none;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.75rem 1.25rem;\n border: 2px solid var(--mj-border-default);\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n border-radius: var(--mj-radius-full);\n font-size: 0.875rem;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n min-width: 44px;\n}\n\n.filter-button[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n}\n\n.filter-button[_ngcontent-%COMP%]:active {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.filter-button[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1rem;\n}\n\n\n\n\n\n.filter-modal-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n backdrop-filter: blur(4px);\n display: flex;\n align-items: flex-end;\n justify-content: center;\n z-index: 1000;\n animation: _ngcontent-%COMP%_fadeIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n padding: 0;\n}\n\n.filter-modal[_ngcontent-%COMP%] {\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-2xl) var(--mj-radius-2xl) 0 0;\n box-shadow: var(--mj-shadow-2xl);\n width: 100%;\n max-height: 80vh;\n overflow: hidden;\n animation: _ngcontent-%COMP%_slideUpFromBottom 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex;\n flex-direction: column;\n}\n\n.filter-modal-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1.25rem 1.5rem;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.filter-modal-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.filter-modal-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 1.375rem;\n}\n\n.filter-modal-close[_ngcontent-%COMP%] {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1.25rem;\n cursor: pointer;\n border-radius: var(--mj-radius-full);\n transition: all 0.2s ease;\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.filter-modal-close[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.filter-modal-body[_ngcontent-%COMP%] {\n padding: 1.5rem;\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n flex: 1;\n}\n\n.filter-modal-footer[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.75rem;\n padding: 1.25rem 1.5rem;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n flex-shrink: 0;\n}\n\n.filter-modal-footer[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n flex: 1;\n margin: 0;\n}\n\n\n\n.filter-options-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n}\n\n.filter-group[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.filter-group-label[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin: 0;\n}\n\n.filter-group-options[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.filter-option[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.875rem 1rem;\n background: var(--mj-bg-surface-sunken);\n border: 2px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 56px;\n}\n\n.filter-option[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface-sunken);\n}\n\n.filter-option.selected[_ngcontent-%COMP%] {\n background: var(--mj-color-accent-300);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.filter-option[_ngcontent-%COMP%] input[type=\"checkbox\"][_ngcontent-%COMP%], \n.filter-option[_ngcontent-%COMP%] input[type=\"radio\"][_ngcontent-%COMP%] {\n width: 20px;\n height: 20px;\n cursor: pointer;\n accent-color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n.filter-option-label[_ngcontent-%COMP%] {\n flex: 1;\n font-size: 1rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.filter-option.selected[_ngcontent-%COMP%] .filter-option-label[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n\n\n\n\n.text-danger[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n}\n\n.text-warning[_ngcontent-%COMP%] {\n color: var(--mj-color-warning-500);\n}\n\n.text-success[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n}\n\n\n\n\n\n@keyframes _ngcontent-%COMP%_fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n@keyframes _ngcontent-%COMP%_slideUp {\n from {\n transform: translateY(20px);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n\n@keyframes _ngcontent-%COMP%_slideDown {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes _ngcontent-%COMP%_slideUpFromBottom {\n from {\n transform: translateY(100%);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n\n\n\n\n\n.mj-search-input[_ngcontent-%COMP%]:focus-visible, \nbutton[_ngcontent-%COMP%]:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n\n\n\n\n@media (prefers-reduced-motion: reduce) {\n *[_ngcontent-%COMP%], \n *[_ngcontent-%COMP%]::before, \n *[_ngcontent-%COMP%]::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n}", "\n\n\n\n\n\n\n\n\n\n\n\n\n.status-chips[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: var(--mj-space-2);\n flex-shrink: 0;\n}\n\n\n\n\n\n.mj-grid-4[_ngcontent-%COMP%] {\n gap: 0.75rem;\n padding: 0 1rem 1rem 1rem;\n}\n\n@media (min-width: 768px) {\n .mj-grid-4[_ngcontent-%COMP%] {\n padding: 0 1.5rem 1.25rem 1.5rem;\n gap: 1rem;\n }\n}\n\n@media (min-width: 1024px) {\n .mj-grid-4[_ngcontent-%COMP%] {\n gap: 1.25rem;\n }\n}\n\n\n\n.mj-card[_ngcontent-%COMP%] {\n box-shadow: none;\n}\n\n@media (min-width: 768px) {\n .mj-card[_ngcontent-%COMP%] {\n padding: 1.125rem 1.25rem;\n }\n}\n\n\n\n.stat-icon-system[_ngcontent-%COMP%] {\n background: var(--mj-color-warning-50);\n color: var(--mj-color-warning-700);\n}\n\n.stat-icon-custom[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-status-success);\n}\n\n.stat-icon-active[_ngcontent-%COMP%] {\n background: var(--mj-color-accent-300);\n color: var(--mj-brand-primary);\n}\n\n\n\n\n\n.roles-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n}\n\n\n\n\n\n.role-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-xl);\n overflow: hidden;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.role-card[_ngcontent-%COMP%]:hover {\n box-shadow: var(--mj-shadow-md);\n border-color: var(--mj-brand-primary);\n}\n\n.role-card.expanded[_ngcontent-%COMP%] {\n box-shadow: var(--mj-shadow-lg);\n border-color: var(--mj-brand-primary);\n}\n\n.role-card.expanded[_ngcontent-%COMP%] .expand-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n transform: rotate(180deg);\n}\n\n.role-header[_ngcontent-%COMP%] {\n padding: 1.25rem 1.5rem;\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n background: var(--mj-bg-surface);\n transition: background-color 0.2s ease;\n}\n\n.role-header[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.role-info[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 1rem;\n flex: 1;\n min-width: 0;\n}\n\n.role-icon-wrapper[_ngcontent-%COMP%] {\n width: 52px;\n height: 52px;\n border-radius: var(--mj-radius-lg);\n background: var(--mj-color-accent-300);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-brand-primary);\n font-size: 1.375rem;\n flex-shrink: 0;\n}\n\n.role-details[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.role-details[_ngcontent-%COMP%] .role-name[_ngcontent-%COMP%] {\n font-size: 1.125rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.25rem 0;\n word-break: break-word;\n overflow-wrap: break-word;\n}\n\n.role-details[_ngcontent-%COMP%] .role-description[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n color: var(--mj-text-secondary);\n margin: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n@media (max-width: 639px) {\n .role-details[_ngcontent-%COMP%] .role-description[_ngcontent-%COMP%] {\n white-space: normal;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n }\n}\n\n.role-meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 1rem;\n flex-shrink: 0;\n}\n\n\n\n\n\n.role-type-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.875rem;\n border-radius: var(--mj-radius-full);\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.role-type-badge.badge-system[_ngcontent-%COMP%] {\n background: var(--mj-color-warning-50);\n color: var(--mj-color-warning-800);\n border: 1px solid var(--mj-color-warning-500);\n}\n\n.role-type-badge.badge-custom[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n border: 1px solid var(--mj-status-success);\n}\n\n\n\n\n\n.role-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.375rem;\n}\n\n\n\n.expand-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n\n\n\n\n.role-content[_ngcontent-%COMP%] {\n padding: 1.5rem;\n background: var(--mj-bg-surface);\n border-top: 1px solid var(--mj-border-default);\n animation: slideDown 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.role-stats[_ngcontent-%COMP%] {\n display: flex;\n gap: 2rem;\n margin-bottom: 1.5rem;\n flex-wrap: wrap;\n}\n\n.role-stats[_ngcontent-%COMP%] .stat-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-size: 0.9375rem;\n padding: 0.5rem 1rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-md);\n}\n\n.role-stats[_ngcontent-%COMP%] .stat-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 1rem;\n}\n\n.role-stats[_ngcontent-%COMP%] .stat-item[_ngcontent-%COMP%] .stat-label[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n}\n\n.role-stats[_ngcontent-%COMP%] .stat-item[_ngcontent-%COMP%] .stat-value[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n\n\n\n\n\n.permissions-preview[_ngcontent-%COMP%] {\n padding: 1.25rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n border: 1px solid var(--mj-border-default);\n}\n\n.permissions-preview[_ngcontent-%COMP%] .section-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.5rem 0;\n}\n\n.permissions-preview[_ngcontent-%COMP%] .section-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 1.125rem;\n}\n\n.permissions-preview[_ngcontent-%COMP%] .permissions-note[_ngcontent-%COMP%] {\n font-size: 0.9375rem;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n\n\n.modal-body[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n line-height: 1.6;\n}\n\n.modal-body[_ngcontent-%COMP%] .text-warning[_ngcontent-%COMP%] {\n color: var(--mj-color-warning-500);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n background: var(--mj-color-warning-50);\n border-radius: var(--mj-radius-md);\n font-weight: 500;\n}\n\n\n\n.modal-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n}\n\n\n\n\n\n@media (min-width: 1440px) {\n .role-management-container[_ngcontent-%COMP%] {\n margin: 0 auto;\n }\n\n .action-buttons[_ngcontent-%COMP%] {\n padding: 1rem 2rem;\n }\n\n .mj-grid-4[_ngcontent-%COMP%] {\n padding: 0 2rem 1.25rem 2rem;\n gap: 1.5rem;\n }\n\n}\n\n\n\n\n\n@media (max-width: 639px) {\n .mj-grid-4[_ngcontent-%COMP%] {\n display: none !important;\n }\n\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 0.75rem;\n }\n\n .modal-backdrop[_ngcontent-%COMP%] {\n padding: 0.5rem;\n }\n\n .modal-dialog[_ngcontent-%COMP%] {\n border-radius: var(--mj-radius-xl);\n max-height: 95vh;\n }\n\n .roles-list[_ngcontent-%COMP%] {\n gap: 0.75rem;\n }\n\n .role-card[_ngcontent-%COMP%] {\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n }\n\n .role-header[_ngcontent-%COMP%] {\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n padding: 1rem;\n }\n\n .role-info[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n flex-direction: row;\n align-items: flex-start;\n gap: 0.875rem;\n }\n\n .role-icon-wrapper[_ngcontent-%COMP%] {\n width: 40px;\n height: 40px;\n font-size: 1rem;\n flex-shrink: 0;\n }\n\n .role-details[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n }\n\n .role-details[_ngcontent-%COMP%] .role-name[_ngcontent-%COMP%] {\n font-size: 1rem;\n line-height: 1.3;\n margin-bottom: 0.125rem;\n }\n\n .role-details[_ngcontent-%COMP%] .role-description[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n -webkit-line-clamp: 1;\n line-clamp: 1;\n }\n\n .role-meta[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: flex-end;\n padding-top: 0;\n border-top: none;\n width: auto;\n }\n\n .role-meta[_ngcontent-%COMP%] .role-type-badge[_ngcontent-%COMP%] {\n display: none;\n }\n\n .role-meta[_ngcontent-%COMP%] .role-actions[_ngcontent-%COMP%] {\n display: none;\n }\n\n .expand-btn[_ngcontent-%COMP%] {\n flex-shrink: 0;\n min-width: 40px;\n min-height: 40px;\n padding: 0.5rem;\n }\n\n .expand-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1rem;\n }\n\n .mobile-actions-bar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n padding: 0.875rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n margin-bottom: 1rem;\n }\n\n .mobile-actions-bar[_ngcontent-%COMP%] .role-type-badge[_ngcontent-%COMP%] {\n padding: 0.375rem 0.75rem;\n font-size: 0.6875rem;\n }\n\n .mobile-action-buttons[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.5rem;\n }\n\n .mobile-action-buttons[_ngcontent-%COMP%] .mj-btn-sm[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.5rem 0.875rem;\n min-height: 36px;\n }\n\n .mobile-action-buttons[_ngcontent-%COMP%] .mj-btn-sm[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n }\n\n .mobile-action-buttons[_ngcontent-%COMP%] .btn-label[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n font-weight: 500;\n }\n\n .role-stats[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 0.5rem;\n margin-bottom: 1rem;\n }\n\n .role-content[_ngcontent-%COMP%] {\n padding: 1rem;\n }\n\n .permissions-preview[_ngcontent-%COMP%] {\n padding: 1rem;\n }\n\n .empty-state[_ngcontent-%COMP%] {\n padding: 3rem 1.5rem;\n }\n\n .empty-state[_ngcontent-%COMP%] .empty-icon[_ngcontent-%COMP%] {\n font-size: 3rem;\n }\n\n .loading-container[_ngcontent-%COMP%] {\n padding: 3rem 1.5rem;\n }\n}\n\n\n\n@media (max-width: 374px) {\n .action-buttons[_ngcontent-%COMP%] {\n padding: 0.375rem 0.5rem;\n }\n\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 0.5rem;\n }\n\n .role-header[_ngcontent-%COMP%] {\n padding: 0.75rem;\n }\n}"] });
|
|
535
|
+
i0.ɵɵconditional(ctx.showDeleteConfirm && ctx.selectedRole ? 18 : -1);
|
|
536
|
+
} }, dependencies: [i1.LoadingComponent, i2.MJButtonDirective, i2.MJPageHeaderInteriorComponent, i2.MJPageBodyInteriorComponent, i2.MJPageSearchComponent, i2.MJFilterPopoverComponent, i2.MJFilterPanelComponent, i2.MJRefreshButtonComponent, i2.MJStatBadgeComponent, i3.RoleDialogComponent, i4.DatePipe], styles: ["\n\n\n\n\n\n\n\n\n\n\n[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n overflow: hidden;\n}\n\n\n\n\n\n.sticky-header[_ngcontent-%COMP%] {\n flex-shrink: 0;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n box-shadow: var(--mj-shadow-sm);\n z-index: 10;\n}\n\n\n\n\n\n.scrollable-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n -webkit-overflow-scrolling: touch;\n padding: 1rem;\n background: var(--mj-bg-surface-sunken);\n}\n\n@media (min-width: 768px) {\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 1.5rem 2rem;\n }\n}\n\n@media (min-width: 1024px) {\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 2rem;\n }\n}\n\n@media (min-width: 1440px) {\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 2rem 2.5rem;\n }\n}\n\n\n\n\n\n.action-buttons[_ngcontent-%COMP%] {\n flex-shrink: 0;\n display: flex;\n gap: 0.75rem;\n justify-content: flex-end;\n padding: 0.75rem 1rem;\n background: var(--mj-bg-page);\n}\n\n@media (min-width: 768px) {\n .action-buttons[_ngcontent-%COMP%] {\n padding: 1rem 1.5rem;\n }\n}\n\n@media (max-width: 639px) {\n .action-buttons[_ngcontent-%COMP%] {\n justify-content: center;\n flex-wrap: wrap;\n }\n}\n\n\n\n\n\n.mj-grid-4[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 0.5rem;\n padding: 0 1rem 0.75rem 1rem;\n background: var(--mj-bg-page);\n}\n\n@media (min-width: 768px) {\n .mj-grid-4[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n gap: 1rem;\n }\n}\n\n\n\n.mj-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-lg);\n padding: 1rem;\n box-shadow: var(--mj-shadow-sm);\n display: flex;\n align-items: center;\n gap: 1rem;\n border: 1px solid var(--mj-border-default);\n cursor: default;\n pointer-events: none;\n}\n\n@media (min-width: 768px) {\n .mj-card[_ngcontent-%COMP%] {\n padding: 1rem 1.25rem;\n }\n}\n\n\n\n\n\n.stat-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n border-radius: var(--mj-radius-lg);\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[_ngcontent-%COMP%] {\n width: 52px;\n height: 52px;\n font-size: 1.375rem;\n }\n}\n\n.stat-icon-total[_ngcontent-%COMP%] {\n background: var(--mj-color-accent-300);\n color: var(--mj-brand-primary);\n}\n\n.stat-icon-active[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-status-success);\n}\n\n\n\n\n\n.stat-content[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.stat-content[_ngcontent-%COMP%] .stat-value[_ngcontent-%COMP%] {\n font-size: 1.75rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 768px) {\n .stat-content[_ngcontent-%COMP%] .stat-value[_ngcontent-%COMP%] {\n font-size: 2rem;\n }\n}\n\n.stat-content[_ngcontent-%COMP%] .stat-label[_ngcontent-%COMP%] {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin-top: 0.25rem;\n}\n\n@media (min-width: 768px) {\n .stat-content[_ngcontent-%COMP%] .stat-label[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n }\n}\n\n\n\n\n\n.filters-section[_ngcontent-%COMP%] {\n flex-shrink: 0;\n background: var(--mj-bg-surface-card);\n margin: 0 1rem 0.75rem 1rem;\n padding: 0.75rem;\n border-radius: var(--mj-radius-lg);\n border: 1px solid var(--mj-border-default);\n}\n\n@media (min-width: 768px) {\n .filters-section[_ngcontent-%COMP%] {\n margin: 0 1.5rem 1rem 1.5rem;\n padding: 1rem 1.25rem;\n }\n}\n\n.filters-row[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n gap: 1.5rem;\n }\n}\n\n.mj-filter-group[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.mj-filter-label[_ngcontent-%COMP%] {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.mj-filter-buttons[_ngcontent-%COMP%] {\n display: flex;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-full);\n padding: 4px;\n gap: 2px;\n}\n\n\n\n\n\n.mj-search[_ngcontent-%COMP%] {\n position: relative;\n flex: 1;\n min-width: 200px;\n}\n\n@media (min-width: 640px) {\n .mj-search[_ngcontent-%COMP%] {\n min-width: 280px;\n }\n}\n\n@media (min-width: 1024px) {\n .mj-search[_ngcontent-%COMP%] {\n min-width: 400px;\n max-width: 600px;\n }\n}\n\n@media (min-width: 1440px) {\n .mj-search[_ngcontent-%COMP%] {\n min-width: 500px;\n max-width: 800px;\n }\n}\n\n.mj-search[_ngcontent-%COMP%] .mj-search-icon[_ngcontent-%COMP%] {\n position: absolute;\n left: 1rem;\n top: 50%;\n transform: translateY(-50%);\n color: var(--mj-text-secondary);\n font-size: 1rem;\n pointer-events: none;\n transition: color 0.2s ease;\n}\n\n@media (min-width: 1024px) {\n .mj-search[_ngcontent-%COMP%] .mj-search-icon[_ngcontent-%COMP%] {\n left: 1.25rem;\n font-size: 1.125rem;\n }\n}\n\n.mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%] {\n width: 100%;\n padding: 0.875rem 1rem 0.875rem 2.75rem;\n border: 2px solid var(--mj-border-default);\n border-radius: var(--mj-radius-full);\n font-size: 1rem;\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n}\n\n@media (min-width: 1024px) {\n .mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%] {\n padding: 1rem 1.25rem 1rem 3.25rem;\n font-size: 1.0625rem;\n }\n}\n\n@media (min-width: 1440px) {\n .mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%] {\n padding: 1.125rem 1.5rem 1.125rem 3.5rem;\n }\n}\n\n.mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%]::placeholder {\n color: var(--mj-text-secondary);\n}\n\n.mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n}\n\n.mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px rgba(0, 118, 182, 0.2);\n background: var(--mj-bg-page);\n}\n\n.mj-search[_ngcontent-%COMP%]:focus-within .mj-search-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n\n\n\n\n.content-area[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n overflow: visible;\n position: relative;\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-xl);\n box-shadow: var(--mj-shadow-sm);\n padding: 1rem;\n border: 1px solid var(--mj-border-default);\n}\n\n@media (min-width: 768px) {\n .content-area[_ngcontent-%COMP%] {\n padding: 1.5rem;\n }\n}\n\n\n\n\n\n.expand-btn[_ngcontent-%COMP%] {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1rem;\n cursor: pointer;\n transition: all 0.2s ease;\n border-radius: var(--mj-radius-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[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.expand-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1);\n}\n\n\n\n\n\n.status-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.875rem;\n border-radius: var(--mj-radius-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[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n border: 1px solid var(--mj-status-success);\n}\n\n.status-badge.status-inactive[_ngcontent-%COMP%] {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n border: 1px solid var(--mj-status-error);\n}\n\n.status-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 0.625rem;\n}\n\n\n\n\n\n.mobile-actions-bar[_ngcontent-%COMP%] {\n display: none;\n}\n\n.mobile-action-buttons[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.5rem;\n}\n\n\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n text-align: center;\n padding: 4rem 2rem;\n}\n\n.empty-state[_ngcontent-%COMP%] .empty-icon[_ngcontent-%COMP%] {\n font-size: 4rem;\n color: var(--mj-border-default);\n margin-bottom: 1.5rem;\n}\n\n.empty-state[_ngcontent-%COMP%] .empty-text[_ngcontent-%COMP%] {\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.5rem 0;\n}\n\n.empty-state[_ngcontent-%COMP%] .empty-subtext[_ngcontent-%COMP%] {\n font-size: 1rem;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n\n\n\n\n.loading-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 4rem 2rem;\n}\n\n\n\n\n\n.error-container[_ngcontent-%COMP%] {\n text-align: center;\n padding: 4rem 2rem;\n}\n\n.error-container[_ngcontent-%COMP%] .error-content[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1rem;\n}\n\n.error-container[_ngcontent-%COMP%] .error-icon[_ngcontent-%COMP%] {\n font-size: 3.5rem;\n color: var(--mj-status-error);\n margin-bottom: 1rem;\n}\n\n.error-container[_ngcontent-%COMP%] .error-message[_ngcontent-%COMP%] {\n font-size: 1.0625rem;\n color: var(--mj-text-primary);\n margin: 0 0 1.5rem 0;\n}\n\n\n\n\n\n.modal-backdrop[_ngcontent-%COMP%] {\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: _ngcontent-%COMP%_fadeIn 0.2s cubic-bezier(0, 0, 0.2, 1);\n}\n\n.modal-dialog[_ngcontent-%COMP%] {\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-2xl);\n box-shadow: var(--mj-shadow-2xl);\n max-width: 500px;\n width: 100%;\n max-height: 90vh;\n overflow: hidden;\n animation: _ngcontent-%COMP%_slideUp 0.3s cubic-bezier(0, 0, 0.2, 1);\n}\n\n.modal-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1.5rem;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.modal-header[_ngcontent-%COMP%] .modal-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 1.375rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.modal-header[_ngcontent-%COMP%] .modal-close[_ngcontent-%COMP%] {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1.25rem;\n cursor: pointer;\n border-radius: var(--mj-radius-full);\n transition: all 0.2s ease;\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.modal-header[_ngcontent-%COMP%] .modal-close[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.modal-body[_ngcontent-%COMP%] {\n padding: 1.5rem;\n}\n\n.modal-body[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n font-size: 1rem;\n color: var(--mj-text-primary);\n margin: 0 0 1rem 0;\n line-height: 1.5;\n}\n\n.modal-body[_ngcontent-%COMP%] p[_ngcontent-%COMP%]:last-child {\n margin-bottom: 0;\n}\n\n.modal-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-start;\n gap: 0.75rem;\n padding: 1.5rem;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n}\n\n@media (max-width: 639px) {\n .modal-dialog[_ngcontent-%COMP%] {\n width: 95%;\n max-height: 85vh;\n }\n\n .modal-header[_ngcontent-%COMP%] {\n padding: 1rem;\n }\n\n .modal-header[_ngcontent-%COMP%] .modal-title[_ngcontent-%COMP%] {\n font-size: 1.125rem;\n }\n\n .modal-body[_ngcontent-%COMP%] {\n padding: 1rem;\n }\n\n .modal-footer[_ngcontent-%COMP%] {\n padding: 1rem;\n flex-direction: column;\n }\n\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n.filter-button[_ngcontent-%COMP%] {\n display: none;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.75rem 1.25rem;\n border: 2px solid var(--mj-border-default);\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n border-radius: var(--mj-radius-full);\n font-size: 0.875rem;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n min-width: 44px;\n}\n\n.filter-button[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n}\n\n.filter-button[_ngcontent-%COMP%]:active {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.filter-button[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1rem;\n}\n\n\n\n\n\n.filter-modal-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n backdrop-filter: blur(4px);\n display: flex;\n align-items: flex-end;\n justify-content: center;\n z-index: 1000;\n animation: _ngcontent-%COMP%_fadeIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n padding: 0;\n}\n\n.filter-modal[_ngcontent-%COMP%] {\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-2xl) var(--mj-radius-2xl) 0 0;\n box-shadow: var(--mj-shadow-2xl);\n width: 100%;\n max-height: 80vh;\n overflow: hidden;\n animation: _ngcontent-%COMP%_slideUpFromBottom 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex;\n flex-direction: column;\n}\n\n.filter-modal-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1.25rem 1.5rem;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.filter-modal-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.filter-modal-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 1.375rem;\n}\n\n.filter-modal-close[_ngcontent-%COMP%] {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1.25rem;\n cursor: pointer;\n border-radius: var(--mj-radius-full);\n transition: all 0.2s ease;\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.filter-modal-close[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.filter-modal-body[_ngcontent-%COMP%] {\n padding: 1.5rem;\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n flex: 1;\n}\n\n.filter-modal-footer[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.75rem;\n padding: 1.25rem 1.5rem;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n flex-shrink: 0;\n}\n\n.filter-modal-footer[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n flex: 1;\n margin: 0;\n}\n\n\n\n.filter-options-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n}\n\n.filter-group[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.filter-group-label[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin: 0;\n}\n\n.filter-group-options[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.filter-option[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.875rem 1rem;\n background: var(--mj-bg-surface-sunken);\n border: 2px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 56px;\n}\n\n.filter-option[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface-sunken);\n}\n\n.filter-option.selected[_ngcontent-%COMP%] {\n background: var(--mj-color-accent-300);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.filter-option[_ngcontent-%COMP%] input[type=\"checkbox\"][_ngcontent-%COMP%], \n.filter-option[_ngcontent-%COMP%] input[type=\"radio\"][_ngcontent-%COMP%] {\n width: 20px;\n height: 20px;\n cursor: pointer;\n accent-color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n.filter-option-label[_ngcontent-%COMP%] {\n flex: 1;\n font-size: 1rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.filter-option.selected[_ngcontent-%COMP%] .filter-option-label[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n\n\n\n\n.text-danger[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n}\n\n.text-warning[_ngcontent-%COMP%] {\n color: var(--mj-color-warning-500);\n}\n\n.text-success[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n}\n\n\n\n\n\n@keyframes _ngcontent-%COMP%_fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n@keyframes _ngcontent-%COMP%_slideUp {\n from {\n transform: translateY(20px);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n\n@keyframes _ngcontent-%COMP%_slideDown {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes _ngcontent-%COMP%_slideUpFromBottom {\n from {\n transform: translateY(100%);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n\n\n\n\n\n.mj-search-input[_ngcontent-%COMP%]:focus-visible, \nbutton[_ngcontent-%COMP%]:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n\n\n\n\n@media (prefers-reduced-motion: reduce) {\n *[_ngcontent-%COMP%], \n *[_ngcontent-%COMP%]::before, \n *[_ngcontent-%COMP%]::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n}", "\n\n\n\n\n\n\n\n\n\n\n\n\n.status-chips[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: var(--mj-space-2);\n flex-shrink: 0;\n}\n\n\n\n\n\n.mj-grid-4[_ngcontent-%COMP%] {\n gap: 0.75rem;\n padding: 0 1rem 1rem 1rem;\n}\n\n@media (min-width: 768px) {\n .mj-grid-4[_ngcontent-%COMP%] {\n padding: 0 1.5rem 1.25rem 1.5rem;\n gap: 1rem;\n }\n}\n\n@media (min-width: 1024px) {\n .mj-grid-4[_ngcontent-%COMP%] {\n gap: 1.25rem;\n }\n}\n\n\n\n.mj-card[_ngcontent-%COMP%] {\n box-shadow: none;\n}\n\n@media (min-width: 768px) {\n .mj-card[_ngcontent-%COMP%] {\n padding: 1.125rem 1.25rem;\n }\n}\n\n\n\n.stat-icon-system[_ngcontent-%COMP%] {\n background: var(--mj-color-warning-50);\n color: var(--mj-color-warning-700);\n}\n\n.stat-icon-custom[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-status-success);\n}\n\n.stat-icon-active[_ngcontent-%COMP%] {\n background: var(--mj-color-accent-300);\n color: var(--mj-brand-primary);\n}\n\n\n\n\n\n.roles-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n}\n\n\n\n\n\n.role-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-xl);\n overflow: hidden;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.role-card[_ngcontent-%COMP%]:hover {\n box-shadow: var(--mj-shadow-md);\n border-color: var(--mj-brand-primary);\n}\n\n.role-card.expanded[_ngcontent-%COMP%] {\n box-shadow: var(--mj-shadow-lg);\n border-color: var(--mj-brand-primary);\n}\n\n.role-card.expanded[_ngcontent-%COMP%] .expand-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n transform: rotate(180deg);\n}\n\n.role-header[_ngcontent-%COMP%] {\n padding: 1.25rem 1.5rem;\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n background: var(--mj-bg-surface);\n transition: background-color 0.2s ease;\n}\n\n.role-header[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.role-info[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 1rem;\n flex: 1;\n min-width: 0;\n}\n\n.role-icon-wrapper[_ngcontent-%COMP%] {\n width: 52px;\n height: 52px;\n border-radius: var(--mj-radius-lg);\n background: var(--mj-color-accent-300);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-brand-primary);\n font-size: 1.375rem;\n flex-shrink: 0;\n}\n\n.role-details[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.role-details[_ngcontent-%COMP%] .role-name[_ngcontent-%COMP%] {\n font-size: 1.125rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.25rem 0;\n word-break: break-word;\n overflow-wrap: break-word;\n}\n\n.role-details[_ngcontent-%COMP%] .role-description[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n color: var(--mj-text-secondary);\n margin: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n@media (max-width: 639px) {\n .role-details[_ngcontent-%COMP%] .role-description[_ngcontent-%COMP%] {\n white-space: normal;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n }\n}\n\n.role-meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 1rem;\n flex-shrink: 0;\n}\n\n\n\n\n\n.role-type-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.875rem;\n border-radius: var(--mj-radius-full);\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.role-type-badge.badge-system[_ngcontent-%COMP%] {\n background: var(--mj-color-warning-50);\n color: var(--mj-color-warning-800);\n border: 1px solid var(--mj-color-warning-500);\n}\n\n.role-type-badge.badge-custom[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n border: 1px solid var(--mj-status-success);\n}\n\n\n\n\n\n.role-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.375rem;\n}\n\n\n\n.expand-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n\n\n\n\n.role-content[_ngcontent-%COMP%] {\n padding: 1.5rem;\n background: var(--mj-bg-surface);\n border-top: 1px solid var(--mj-border-default);\n animation: slideDown 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.role-stats[_ngcontent-%COMP%] {\n display: flex;\n gap: 2rem;\n margin-bottom: 1.5rem;\n flex-wrap: wrap;\n}\n\n.role-stats[_ngcontent-%COMP%] .stat-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-size: 0.9375rem;\n padding: 0.5rem 1rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-md);\n}\n\n.role-stats[_ngcontent-%COMP%] .stat-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 1rem;\n}\n\n.role-stats[_ngcontent-%COMP%] .stat-item[_ngcontent-%COMP%] .stat-label[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n}\n\n.role-stats[_ngcontent-%COMP%] .stat-item[_ngcontent-%COMP%] .stat-value[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n\n\n\n\n\n.permissions-preview[_ngcontent-%COMP%] {\n padding: 1.25rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n border: 1px solid var(--mj-border-default);\n}\n\n.permissions-preview[_ngcontent-%COMP%] .section-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.5rem 0;\n}\n\n.permissions-preview[_ngcontent-%COMP%] .section-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 1.125rem;\n}\n\n.permissions-preview[_ngcontent-%COMP%] .permissions-note[_ngcontent-%COMP%] {\n font-size: 0.9375rem;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n\n\n.modal-body[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n line-height: 1.6;\n}\n\n.modal-body[_ngcontent-%COMP%] .text-warning[_ngcontent-%COMP%] {\n color: var(--mj-color-warning-500);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n background: var(--mj-color-warning-50);\n border-radius: var(--mj-radius-md);\n font-weight: 500;\n}\n\n\n\n.modal-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n}\n\n\n\n\n\n@media (min-width: 1440px) {\n .role-management-container[_ngcontent-%COMP%] {\n margin: 0 auto;\n }\n\n .action-buttons[_ngcontent-%COMP%] {\n padding: 1rem 2rem;\n }\n\n .mj-grid-4[_ngcontent-%COMP%] {\n padding: 0 2rem 1.25rem 2rem;\n gap: 1.5rem;\n }\n\n}\n\n\n\n\n\n@media (max-width: 639px) {\n .mj-grid-4[_ngcontent-%COMP%] {\n display: none !important;\n }\n\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 0.75rem;\n }\n\n .modal-backdrop[_ngcontent-%COMP%] {\n padding: 0.5rem;\n }\n\n .modal-dialog[_ngcontent-%COMP%] {\n border-radius: var(--mj-radius-xl);\n max-height: 95vh;\n }\n\n .roles-list[_ngcontent-%COMP%] {\n gap: 0.75rem;\n }\n\n .role-card[_ngcontent-%COMP%] {\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n }\n\n .role-header[_ngcontent-%COMP%] {\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n padding: 1rem;\n }\n\n .role-info[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n flex-direction: row;\n align-items: flex-start;\n gap: 0.875rem;\n }\n\n .role-icon-wrapper[_ngcontent-%COMP%] {\n width: 40px;\n height: 40px;\n font-size: 1rem;\n flex-shrink: 0;\n }\n\n .role-details[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n }\n\n .role-details[_ngcontent-%COMP%] .role-name[_ngcontent-%COMP%] {\n font-size: 1rem;\n line-height: 1.3;\n margin-bottom: 0.125rem;\n }\n\n .role-details[_ngcontent-%COMP%] .role-description[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n -webkit-line-clamp: 1;\n line-clamp: 1;\n }\n\n .role-meta[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: flex-end;\n padding-top: 0;\n border-top: none;\n width: auto;\n }\n\n .role-meta[_ngcontent-%COMP%] .role-type-badge[_ngcontent-%COMP%] {\n display: none;\n }\n\n .role-meta[_ngcontent-%COMP%] .role-actions[_ngcontent-%COMP%] {\n display: none;\n }\n\n .expand-btn[_ngcontent-%COMP%] {\n flex-shrink: 0;\n min-width: 40px;\n min-height: 40px;\n padding: 0.5rem;\n }\n\n .expand-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1rem;\n }\n\n .mobile-actions-bar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n padding: 0.875rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n margin-bottom: 1rem;\n }\n\n .mobile-actions-bar[_ngcontent-%COMP%] .role-type-badge[_ngcontent-%COMP%] {\n padding: 0.375rem 0.75rem;\n font-size: 0.6875rem;\n }\n\n .mobile-action-buttons[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.5rem;\n }\n\n .mobile-action-buttons[_ngcontent-%COMP%] .mj-btn-sm[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.5rem 0.875rem;\n min-height: 36px;\n }\n\n .mobile-action-buttons[_ngcontent-%COMP%] .mj-btn-sm[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n }\n\n .mobile-action-buttons[_ngcontent-%COMP%] .btn-label[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n font-weight: 500;\n }\n\n .role-stats[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 0.5rem;\n margin-bottom: 1rem;\n }\n\n .role-content[_ngcontent-%COMP%] {\n padding: 1rem;\n }\n\n .permissions-preview[_ngcontent-%COMP%] {\n padding: 1rem;\n }\n\n .empty-state[_ngcontent-%COMP%] {\n padding: 3rem 1.5rem;\n }\n\n .empty-state[_ngcontent-%COMP%] .empty-icon[_ngcontent-%COMP%] {\n font-size: 3rem;\n }\n\n .loading-container[_ngcontent-%COMP%] {\n padding: 3rem 1.5rem;\n }\n}\n\n\n\n@media (max-width: 374px) {\n .action-buttons[_ngcontent-%COMP%] {\n padding: 0.375rem 0.5rem;\n }\n\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 0.5rem;\n }\n\n .role-header[_ngcontent-%COMP%] {\n padding: 0.75rem;\n }\n}"] });
|
|
511
537
|
};
|
|
512
538
|
RoleManagementComponent = __decorate([
|
|
513
539
|
RegisterClass(BaseDashboard, 'RoleManagement')
|
|
@@ -515,7 +541,7 @@ RoleManagementComponent = __decorate([
|
|
|
515
541
|
export { RoleManagementComponent };
|
|
516
542
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RoleManagementComponent, [{
|
|
517
543
|
type: Component,
|
|
518
|
-
args: [{ standalone: false, selector: 'mj-role-management', template: "<!--\n Interior chrome \u2014 <mj-page-header-interior> is the body-level sibling of\n <mj-page-header>. Same slot conventions ([toolbar] / [meta] / [actions]);\n different visual shape (single-row card). See Section 10 of\n explorer-chrome-conventions.\n-->\n<mj-page-header-interior\n AriaLabel=\"Filter roles\"\n Title=\"Roles\"\n Subtitle=\"Define system and custom roles for permissions\">\n <!-- X-of-Y filtered count earns its meta spot per chrome conventions \u00A72.\n Partition badges (system/custom) stay dropped \u2014 they mirror the\n System/Custom filter chips in [toolbar]. -->\n <div meta>\n <mj-stat-badge\n [Count]=\"filteredRoles.length\"\n [Total]=\"roles.length\"\n Label=\"roles\">\n </mj-stat-badge>\n </div>\n <div toolbar>\n <mj-page-search\n Placeholder=\"Search roles by name or description...\"\n [Value]=\"filters$.value.search\"\n (ValueChange)=\"updateFilter({ search: $event })\">\n </mj-page-search>\n <div class=\"status-chips\" role=\"group\" aria-label=\"Filter by type\">\n <mj-filter-chip\n Label=\"All\"\n [Active]=\"filters$.value.type === 'all'\"\n (Clicked)=\"onTypeFilterChange('all')\">\n </mj-filter-chip>\n <mj-filter-chip\n Label=\"System\"\n [Active]=\"filters$.value.type === 'system'\"\n (Clicked)=\"onTypeFilterChange('system')\">\n </mj-filter-chip>\n <mj-filter-chip\n Label=\"Custom\"\n [Active]=\"filters$.value.type === 'custom'\"\n (Clicked)=\"onTypeFilterChange('custom')\">\n </mj-filter-chip>\n </div>\n </div>\n <div actions>\n <mj-refresh-button [Loading]=\"isLoading\" (Clicked)=\"refreshData()\"></mj-refresh-button>\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"createNewRole()\" aria-label=\"Add new role\">\n <i class=\"fa-solid fa-plus\"></i> Add Role\n </button>\n </div>\n</mj-page-header-interior>\n\n<!-- Scrollable Content Section \u2014 owned by <mj-page-body-interior>. -->\n<mj-page-body-interior>\n <!-- Loading State -->\n @if (isLoading) {\n <div class=\"loading-container\" role=\"status\" aria-live=\"polite\" aria-busy=\"true\">\n <mj-loading text=\"Loading roles...\" 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 mjButton variant=\"primary\" (click)=\"loadInitialData()\" aria-label=\"Retry loading roles\">\n <i class=\"fa-solid fa-refresh\" aria-hidden=\"true\"></i>\n Try Again\n </button>\n </div>\n </div>\n }\n\n <!-- Roles List -->\n @if (!isLoading && !error) {\n <div class=\"roles-list\" role=\"list\" aria-label=\"Roles list\">\n @for (role of filteredRoles; track role.ID) {\n <div\n class=\"role-card\"\n [class.expanded]=\"isRoleExpanded(role.ID)\"\n role=\"listitem\"\n [attr.aria-expanded]=\"isRoleExpanded(role.ID)\"\n >\n <div\n class=\"role-header\"\n (click)=\"toggleRoleExpansion(role.ID)\"\n tabindex=\"0\"\n (keydown.enter)=\"toggleRoleExpansion(role.ID)\"\n (keydown.space)=\"toggleRoleExpansion(role.ID); $event.preventDefault()\"\n role=\"button\"\n [attr.aria-label]=\"'Toggle details for ' + role.Name\"\n >\n <div class=\"role-info\">\n <div class=\"role-icon-wrapper\" aria-hidden=\"true\">\n <i [class]=\"'fa-solid ' + getRoleIcon(role)\"></i>\n </div>\n <div class=\"role-details\">\n <h3 class=\"role-name\">{{ role.Name }}</h3>\n <p class=\"role-description\">{{ role.Description || 'No description available' }}</p>\n </div>\n </div>\n\n <div class=\"role-meta\">\n <span class=\"role-type-badge\" [class]=\"getRoleTypeClass(role)\">\n {{ getRoleTypeLabel(role) }}\n </span>\n <div class=\"role-actions\" (click)=\"$event.stopPropagation()\">\n <button\n mjButton variant=\"flat\" size=\"sm\"\n (click)=\"editRole(role)\"\n title=\"Edit\"\n [attr.aria-label]=\"'Edit ' + role.Name\"\n [disabled]=\"isSystemRole(role)\"\n >\n <i class=\"fa-solid fa-edit\" aria-hidden=\"true\"></i>\n </button>\n <button\n mjButton variant=\"danger\" size=\"sm\"\n (click)=\"confirmDeleteRole(role)\"\n title=\"Delete\"\n [attr.aria-label]=\"'Delete ' + role.Name\"\n [disabled]=\"isSystemRole(role)\"\n >\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <button class=\"expand-btn\" aria-label=\"Toggle details\">\n <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n\n @if (isRoleExpanded(role.ID)) {\n <div class=\"role-content\">\n <!-- Mobile-only actions bar -->\n <div class=\"mobile-actions-bar\" (click)=\"$event.stopPropagation()\">\n <span class=\"role-type-badge\" [class]=\"getRoleTypeClass(role)\">\n {{ getRoleTypeLabel(role) }}\n </span>\n <div class=\"mobile-action-buttons\">\n <button\n mjButton variant=\"flat\" size=\"sm\"\n (click)=\"editRole(role)\"\n title=\"Edit\"\n [attr.aria-label]=\"'Edit ' + role.Name\"\n [disabled]=\"isSystemRole(role)\"\n >\n <i class=\"fa-solid fa-edit\" aria-hidden=\"true\"></i>\n <span class=\"btn-label\">Edit</span>\n </button>\n <button\n mjButton variant=\"danger\" size=\"sm\"\n (click)=\"confirmDeleteRole(role)\"\n title=\"Delete\"\n [attr.aria-label]=\"'Delete ' + role.Name\"\n [disabled]=\"isSystemRole(role)\"\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 <div class=\"role-stats\">\n <div class=\"stat-item\">\n <i class=\"fa-solid fa-users\" aria-hidden=\"true\"></i>\n <span class=\"stat-label\">Users:</span>\n <span class=\"stat-value\">0</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\">{{ role.__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\">{{ role.__mj_UpdatedAt | date:'short' }}</span>\n </div>\n </div>\n\n <div class=\"permissions-preview\">\n <h4 class=\"section-title\">\n <i class=\"fa-solid fa-key\" aria-hidden=\"true\"></i>\n Permissions Preview\n </h4>\n <p class=\"permissions-note\">Full permission management available in the Permissions tab</p>\n </div>\n </div>\n }\n </div>\n }\n\n @if (filteredRoles.length === 0) {\n <div class=\"empty-state\" role=\"status\">\n <i class=\"fa-solid fa-user-tag empty-icon\" aria-hidden=\"true\"></i>\n <p class=\"empty-text\">No roles found</p>\n <p class=\"empty-subtext\">Try adjusting your filters or create a new role</p>\n </div>\n }\n </div>\n }\n </mj-page-body-interior>\n\n <!-- Role Create/Edit Dialog -->\n <mj-role-dialog\n [data]=\"roleDialogData\"\n [visible]=\"showRoleDialog\"\n (result)=\"onRoleDialogResult($event)\">\n </mj-role-dialog>\n\n <!-- Delete Confirmation Dialog -->\n @if (showDeleteConfirm && selectedRole) {\n <div\n class=\"modal-backdrop\"\n (click)=\"showDeleteConfirm = false\"\n role=\"presentation\"\n >\n <div\n class=\"modal-dialog\"\n (click)=\"$event.stopPropagation()\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"delete-dialog-title\"\n aria-describedby=\"delete-dialog-desc\"\n >\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 the role <strong>{{ selectedRole.Name }}</strong>?</p>\n <p class=\"text-warning\">\n <i class=\"fa-solid fa-warning\" aria-hidden=\"true\"></i>\n This will affect all users assigned to this role.\n </p>\n </div>\n <div class=\"modal-footer\">\n <!-- Primary action (Delete) on LEFT per MD3 design guide -->\n <button mjButton variant=\"danger\" (click)=\"deleteRole()\" [disabled]=\"isLoading\">\n @if (isLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n Deleting...\n } @else {\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n Delete Role\n }\n </button>\n <button mjButton (click)=\"showDeleteConfirm = false\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n }\n\n", styles: ["/* =============================================================================\n Shared Admin Patterns - MJ Design System\n Common styles shared across all admin settings components.\n Component-specific CSS files override these values where needed.\n ============================================================================= */\n\n/* -----------------------------------------------------------------------------\n Host & Container\n ----------------------------------------------------------------------------- */\n:host {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n overflow: hidden;\n}\n\n/* -----------------------------------------------------------------------------\n Sticky Header\n ----------------------------------------------------------------------------- */\n.sticky-header {\n flex-shrink: 0;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n box-shadow: var(--mj-shadow-sm);\n z-index: 10;\n}\n\n/* -----------------------------------------------------------------------------\n Scrollable Content\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(--mj-bg-surface-sunken);\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@media (min-width: 1440px) {\n .scrollable-content {\n padding: 2rem 2.5rem;\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(--mj-bg-page);\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\n/* -----------------------------------------------------------------------------\n Statistics Cards Grid - Static Display (Non-interactive)\n ----------------------------------------------------------------------------- */\n.mj-grid-4 {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 0.5rem;\n padding: 0 1rem 0.75rem 1rem;\n background: var(--mj-bg-page);\n}\n\n@media (min-width: 768px) {\n .mj-grid-4 {\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(--mj-bg-surface-card);\n border-radius: var(--mj-radius-lg);\n padding: 1rem;\n box-shadow: var(--mj-shadow-sm);\n display: flex;\n align-items: center;\n gap: 1rem;\n border: 1px solid var(--mj-border-default);\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/* -----------------------------------------------------------------------------\n Stat Icon Containers\n ----------------------------------------------------------------------------- */\n.stat-icon {\n width: 48px;\n height: 48px;\n border-radius: var(--mj-radius-lg);\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: 52px;\n height: 52px;\n font-size: 1.375rem;\n }\n}\n\n.stat-icon-total {\n background: var(--mj-color-accent-300);\n color: var(--mj-brand-primary);\n}\n\n.stat-icon-active {\n background: var(--mj-color-success-100);\n color: var(--mj-status-success);\n}\n\n/* -----------------------------------------------------------------------------\n Stat Content Typography\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(--mj-text-primary);\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(--mj-text-secondary);\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(--mj-bg-surface-card);\n margin: 0 1rem 0.75rem 1rem;\n padding: 0.75rem;\n border-radius: var(--mj-radius-lg);\n border: 1px solid var(--mj-border-default);\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.mj-filter-group {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.mj-filter-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.mj-filter-buttons {\n display: flex;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-full);\n padding: 4px;\n gap: 2px;\n}\n\n/* -----------------------------------------------------------------------------\n Search Input\n ----------------------------------------------------------------------------- */\n.mj-search {\n position: relative;\n flex: 1;\n min-width: 200px;\n}\n\n@media (min-width: 640px) {\n .mj-search {\n min-width: 280px;\n }\n}\n\n@media (min-width: 1024px) {\n .mj-search {\n min-width: 400px;\n max-width: 600px;\n }\n}\n\n@media (min-width: 1440px) {\n .mj-search {\n min-width: 500px;\n max-width: 800px;\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(--mj-text-secondary);\n font-size: 1rem;\n pointer-events: none;\n transition: color 0.2s ease;\n}\n\n@media (min-width: 1024px) {\n .mj-search .mj-search-icon {\n left: 1.25rem;\n font-size: 1.125rem;\n }\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(--mj-border-default);\n border-radius: var(--mj-radius-full);\n font-size: 1rem;\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n}\n\n@media (min-width: 1024px) {\n .mj-search .mj-search-input {\n padding: 1rem 1.25rem 1rem 3.25rem;\n font-size: 1.0625rem;\n }\n}\n\n@media (min-width: 1440px) {\n .mj-search .mj-search-input {\n padding: 1.125rem 1.5rem 1.125rem 3.5rem;\n }\n}\n\n.mj-search .mj-search-input::placeholder {\n color: var(--mj-text-secondary);\n}\n\n.mj-search .mj-search-input:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n}\n\n.mj-search .mj-search-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px rgba(0, 118, 182, 0.2);\n background: var(--mj-bg-page);\n}\n\n.mj-search:focus-within .mj-search-icon {\n color: var(--mj-brand-primary);\n}\n\n/* -----------------------------------------------------------------------------\n Content Area\n ----------------------------------------------------------------------------- */\n.content-area {\n flex: 1 1 auto;\n overflow: visible;\n position: relative;\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-xl);\n box-shadow: var(--mj-shadow-sm);\n padding: 1rem;\n border: 1px solid var(--mj-border-default);\n}\n\n@media (min-width: 768px) {\n .content-area {\n padding: 1.5rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Expand Button\n ----------------------------------------------------------------------------- */\n.expand-btn {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1rem;\n cursor: pointer;\n transition: all 0.2s ease;\n border-radius: var(--mj-radius-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(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.expand-btn i {\n transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1);\n}\n\n/* -----------------------------------------------------------------------------\n Status Badge\n ----------------------------------------------------------------------------- */\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(--mj-radius-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(--mj-color-success-100);\n color: var(--mj-color-success-800);\n border: 1px solid var(--mj-status-success);\n}\n\n.status-badge.status-inactive {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n border: 1px solid var(--mj-status-error);\n}\n\n.status-badge i {\n font-size: 0.625rem;\n}\n\n/* -----------------------------------------------------------------------------\n Mobile Actions Bar\n ----------------------------------------------------------------------------- */\n.mobile-actions-bar {\n display: none;\n}\n\n.mobile-action-buttons {\n display: flex;\n gap: 0.5rem;\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(--mj-border-default);\n margin-bottom: 1.5rem;\n}\n\n.empty-state .empty-text {\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.5rem 0;\n}\n\n.empty-state .empty-subtext {\n font-size: 1rem;\n color: var(--mj-text-secondary);\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(--mj-status-error);\n margin-bottom: 1rem;\n}\n\n.error-container .error-message {\n font-size: 1.0625rem;\n color: var(--mj-text-primary);\n margin: 0 0 1.5rem 0;\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 cubic-bezier(0, 0, 0.2, 1);\n}\n\n.modal-dialog {\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-2xl);\n box-shadow: var(--mj-shadow-2xl);\n max-width: 500px;\n width: 100%;\n max-height: 90vh;\n overflow: hidden;\n animation: slideUp 0.3s cubic-bezier(0, 0, 0.2, 1);\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(--mj-border-default);\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(--mj-text-primary);\n margin: 0;\n}\n\n.modal-header .modal-close {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1.25rem;\n cursor: pointer;\n border-radius: var(--mj-radius-full);\n transition: all 0.2s ease;\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.modal-header .modal-close:hover {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.modal-body {\n padding: 1.5rem;\n}\n\n.modal-body p {\n font-size: 1rem;\n color: var(--mj-text-primary);\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-footer {\n display: flex;\n justify-content: flex-start;\n gap: 0.75rem;\n padding: 1.5rem;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\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-footer {\n padding: 1rem;\n flex-direction: column;\n }\n\n}\n\n/* The legacy \"MJ Button System\" rule block (.mj-btn / .mj-btn-primary /\n .mj-btn-secondary / .mj-btn-ghost / .mj-btn-danger / .mj-btn-sm /\n .mj-btn-icon-only / .mj-btn-icon-mobile) was removed 2026-05-19. These\n rules pre-dated the mjButton directive standardization (the directive\n uses BEM modifiers like .mj-btn--primary, single-dash names like\n .mj-btn-primary don't match it anyway). The base .mj-btn rule WAS active\n though \u2014 its component-scoped specificity outranked button.scss's global\n directive styles, so admin sub-page buttons rendered pill-shaped + 44px\n regardless of the variant/size requested via mjButton inputs. Removing the\n rule lets every sub-page fall back to the directive's intended look. */\n\n/* -----------------------------------------------------------------------------\n Filter Button (Mobile Only)\n ----------------------------------------------------------------------------- */\n.filter-button {\n display: none;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.75rem 1.25rem;\n border: 2px solid var(--mj-border-default);\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n border-radius: var(--mj-radius-full);\n font-size: 0.875rem;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n min-width: 44px;\n}\n\n.filter-button:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n}\n\n.filter-button:active {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.filter-button i {\n font-size: 1rem;\n}\n\n/* -----------------------------------------------------------------------------\n Filter Modal (Mobile Bottom Sheet)\n ----------------------------------------------------------------------------- */\n.filter-modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n backdrop-filter: blur(4px);\n display: flex;\n align-items: flex-end;\n justify-content: center;\n z-index: 1000;\n animation: fadeIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n padding: 0;\n}\n\n.filter-modal {\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-2xl) var(--mj-radius-2xl) 0 0;\n box-shadow: var(--mj-shadow-2xl);\n width: 100%;\n max-height: 80vh;\n overflow: hidden;\n animation: slideUpFromBottom 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex;\n flex-direction: column;\n}\n\n.filter-modal-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1.25rem 1.5rem;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.filter-modal-title {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.filter-modal-title i {\n color: var(--mj-brand-primary);\n font-size: 1.375rem;\n}\n\n.filter-modal-close {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1.25rem;\n cursor: pointer;\n border-radius: var(--mj-radius-full);\n transition: all 0.2s ease;\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.filter-modal-close:hover {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.filter-modal-body {\n padding: 1.5rem;\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n flex: 1;\n}\n\n.filter-modal-footer {\n display: flex;\n gap: 0.75rem;\n padding: 1.25rem 1.5rem;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n flex-shrink: 0;\n}\n\n.filter-modal-footer button {\n flex: 1;\n margin: 0;\n}\n\n/* Filter Options Container (Inside Modal) */\n.filter-options-container {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n}\n\n.filter-group {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.filter-group-label {\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin: 0;\n}\n\n.filter-group-options {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.filter-option {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.875rem 1rem;\n background: var(--mj-bg-surface-sunken);\n border: 2px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 56px;\n}\n\n.filter-option:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface-sunken);\n}\n\n.filter-option.selected {\n background: var(--mj-color-accent-300);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.filter-option input[type=\"checkbox\"],\n.filter-option input[type=\"radio\"] {\n width: 20px;\n height: 20px;\n cursor: pointer;\n accent-color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n.filter-option-label {\n flex: 1;\n font-size: 1rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.filter-option.selected .filter-option-label {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n/* -----------------------------------------------------------------------------\n Utility Classes\n ----------------------------------------------------------------------------- */\n.text-danger {\n color: var(--mj-status-error);\n}\n\n.text-warning {\n color: var(--mj-color-warning-500);\n}\n\n.text-success {\n color: var(--mj-status-success);\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@keyframes slideDown {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes slideUpFromBottom {\n from {\n transform: translateY(100%);\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,\nbutton:focus-visible {\n outline: 2px solid var(--mj-brand-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 Role Management Component - Component-Specific Styles\n Shared patterns are in ../shared/styles/_admin-patterns.css\n ============================================================================= */\n\n/* :host already provides flex column / 100% height / overflow hidden via\n _admin-patterns.css. No bespoke container wrapper \u2014 see Section 10 of\n explorer-chrome-conventions for the standardized sub-page layout contract. */\n\n/* Status / type chip group inside <mj-page-header-interior>'s [toolbar] slot. */\n.status-chips {\n display: inline-flex;\n align-items: center;\n gap: var(--mj-space-2);\n flex-shrink: 0;\n}\n\n/* -----------------------------------------------------------------------------\n Stats Grid Override - Different gap/padding values\n ----------------------------------------------------------------------------- */\n.mj-grid-4 {\n gap: 0.75rem;\n padding: 0 1rem 1rem 1rem;\n}\n\n@media (min-width: 768px) {\n .mj-grid-4 {\n padding: 0 1.5rem 1.25rem 1.5rem;\n gap: 1rem;\n }\n}\n\n@media (min-width: 1024px) {\n .mj-grid-4 {\n gap: 1.25rem;\n }\n}\n\n/* Stat Card Override - no box-shadow, different 768px padding */\n.mj-card {\n box-shadow: none;\n}\n\n@media (min-width: 768px) {\n .mj-card {\n padding: 1.125rem 1.25rem;\n }\n}\n\n/* Stat Icon Override - Component-specific colors */\n.stat-icon-system {\n background: var(--mj-color-warning-50);\n color: var(--mj-color-warning-700);\n}\n\n.stat-icon-custom {\n background: var(--mj-color-success-100);\n color: var(--mj-status-success);\n}\n\n.stat-icon-active {\n background: var(--mj-color-accent-300);\n color: var(--mj-brand-primary);\n}\n\n/* -----------------------------------------------------------------------------\n Roles List\n ----------------------------------------------------------------------------- */\n.roles-list {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n}\n\n/* -----------------------------------------------------------------------------\n Role Card\n ----------------------------------------------------------------------------- */\n.role-card {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-xl);\n overflow: hidden;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.role-card:hover {\n box-shadow: var(--mj-shadow-md);\n border-color: var(--mj-brand-primary);\n}\n\n.role-card.expanded {\n box-shadow: var(--mj-shadow-lg);\n border-color: var(--mj-brand-primary);\n}\n\n.role-card.expanded .expand-btn i {\n transform: rotate(180deg);\n}\n\n.role-header {\n padding: 1.25rem 1.5rem;\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n background: var(--mj-bg-surface);\n transition: background-color 0.2s ease;\n}\n\n.role-header:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.role-info {\n display: flex;\n align-items: center;\n gap: 1rem;\n flex: 1;\n min-width: 0;\n}\n\n.role-icon-wrapper {\n width: 52px;\n height: 52px;\n border-radius: var(--mj-radius-lg);\n background: var(--mj-color-accent-300);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-brand-primary);\n font-size: 1.375rem;\n flex-shrink: 0;\n}\n\n.role-details {\n flex: 1;\n min-width: 0;\n}\n\n.role-details .role-name {\n font-size: 1.125rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.25rem 0;\n word-break: break-word;\n overflow-wrap: break-word;\n}\n\n.role-details .role-description {\n font-size: 0.875rem;\n color: var(--mj-text-secondary);\n margin: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n@media (max-width: 639px) {\n .role-details .role-description {\n white-space: normal;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n }\n}\n\n.role-meta {\n display: flex;\n align-items: center;\n gap: 1rem;\n flex-shrink: 0;\n}\n\n/* -----------------------------------------------------------------------------\n Role Type Badge\n ----------------------------------------------------------------------------- */\n.role-type-badge {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.875rem;\n border-radius: var(--mj-radius-full);\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.role-type-badge.badge-system {\n background: var(--mj-color-warning-50);\n color: var(--mj-color-warning-800);\n border: 1px solid var(--mj-color-warning-500);\n}\n\n.role-type-badge.badge-custom {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n border: 1px solid var(--mj-status-success);\n}\n\n/* -----------------------------------------------------------------------------\n Role Actions\n ----------------------------------------------------------------------------- */\n.role-actions {\n display: flex;\n gap: 0.375rem;\n}\n\n/* Expand Button Override - different transition easing */\n.expand-btn i {\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n/* -----------------------------------------------------------------------------\n Role Content (Expanded)\n ----------------------------------------------------------------------------- */\n.role-content {\n padding: 1.5rem;\n background: var(--mj-bg-surface);\n border-top: 1px solid var(--mj-border-default);\n animation: slideDown 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.role-stats {\n display: flex;\n gap: 2rem;\n margin-bottom: 1.5rem;\n flex-wrap: wrap;\n}\n\n.role-stats .stat-item {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-size: 0.9375rem;\n padding: 0.5rem 1rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-md);\n}\n\n.role-stats .stat-item i {\n color: var(--mj-brand-primary);\n font-size: 1rem;\n}\n\n.role-stats .stat-item .stat-label {\n color: var(--mj-text-secondary);\n}\n\n.role-stats .stat-item .stat-value {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n\n/* -----------------------------------------------------------------------------\n Permissions Preview\n ----------------------------------------------------------------------------- */\n.permissions-preview {\n padding: 1.25rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n border: 1px solid var(--mj-border-default);\n}\n\n.permissions-preview .section-title {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.5rem 0;\n}\n\n.permissions-preview .section-title i {\n color: var(--mj-brand-primary);\n font-size: 1.125rem;\n}\n\n.permissions-preview .permissions-note {\n font-size: 0.9375rem;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n/* Modal Body Override - line-height and warning callout */\n.modal-body p {\n line-height: 1.6;\n}\n\n.modal-body .text-warning {\n color: var(--mj-color-warning-500);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n background: var(--mj-color-warning-50);\n border-radius: var(--mj-radius-md);\n font-weight: 500;\n}\n\n/* Modal Backdrop Override - uses top/left/right/bottom instead of inset */\n.modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n}\n\n/* -----------------------------------------------------------------------------\n Large Desktop (1440px+)\n ----------------------------------------------------------------------------- */\n@media (min-width: 1440px) {\n .role-management-container {\n margin: 0 auto;\n }\n\n .action-buttons {\n padding: 1rem 2rem;\n }\n\n .mj-grid-4 {\n padding: 0 2rem 1.25rem 2rem;\n gap: 1.5rem;\n }\n\n}\n\n/* -----------------------------------------------------------------------------\n Mobile (< 640px)\n ----------------------------------------------------------------------------- */\n@media (max-width: 639px) {\n .mj-grid-4 {\n display: none !important;\n }\n\n .scrollable-content {\n padding: 0.75rem;\n }\n\n .modal-backdrop {\n padding: 0.5rem;\n }\n\n .modal-dialog {\n border-radius: var(--mj-radius-xl);\n max-height: 95vh;\n }\n\n .roles-list {\n gap: 0.75rem;\n }\n\n .role-card {\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n }\n\n .role-header {\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n padding: 1rem;\n }\n\n .role-info {\n flex: 1;\n min-width: 0;\n flex-direction: row;\n align-items: flex-start;\n gap: 0.875rem;\n }\n\n .role-icon-wrapper {\n width: 40px;\n height: 40px;\n font-size: 1rem;\n flex-shrink: 0;\n }\n\n .role-details {\n flex: 1;\n min-width: 0;\n }\n\n .role-details .role-name {\n font-size: 1rem;\n line-height: 1.3;\n margin-bottom: 0.125rem;\n }\n\n .role-details .role-description {\n font-size: 0.8125rem;\n -webkit-line-clamp: 1;\n line-clamp: 1;\n }\n\n .role-meta {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: flex-end;\n padding-top: 0;\n border-top: none;\n width: auto;\n }\n\n .role-meta .role-type-badge {\n display: none;\n }\n\n .role-meta .role-actions {\n display: none;\n }\n\n .expand-btn {\n flex-shrink: 0;\n min-width: 40px;\n min-height: 40px;\n padding: 0.5rem;\n }\n\n .expand-btn i {\n font-size: 1rem;\n }\n\n .mobile-actions-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n padding: 0.875rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n margin-bottom: 1rem;\n }\n\n .mobile-actions-bar .role-type-badge {\n padding: 0.375rem 0.75rem;\n font-size: 0.6875rem;\n }\n\n .mobile-action-buttons {\n display: flex;\n gap: 0.5rem;\n }\n\n .mobile-action-buttons .mj-btn-sm {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.5rem 0.875rem;\n min-height: 36px;\n }\n\n .mobile-action-buttons .mj-btn-sm i {\n font-size: 0.875rem;\n }\n\n .mobile-action-buttons .btn-label {\n font-size: 0.8125rem;\n font-weight: 500;\n }\n\n .role-stats {\n flex-direction: column;\n gap: 0.5rem;\n margin-bottom: 1rem;\n }\n\n .role-content {\n padding: 1rem;\n }\n\n .permissions-preview {\n padding: 1rem;\n }\n\n .empty-state {\n padding: 3rem 1.5rem;\n }\n\n .empty-state .empty-icon {\n font-size: 3rem;\n }\n\n .loading-container {\n padding: 3rem 1.5rem;\n }\n}\n\n/* Small Mobile (< 375px) */\n@media (max-width: 374px) {\n .action-buttons {\n padding: 0.375rem 0.5rem;\n }\n\n .scrollable-content {\n padding: 0.5rem;\n }\n\n .role-header {\n padding: 0.75rem;\n }\n}\n"] }]
|
|
544
|
+
args: [{ standalone: false, selector: 'mj-role-management', template: "<!--\n Interior chrome \u2014 <mj-page-header-interior> is the body-level sibling of\n <mj-page-header>. Same slot conventions ([toolbar] / [meta] / [actions]);\n different visual shape (single-row card). See Section 10 of\n explorer-chrome-conventions.\n-->\n<mj-page-header-interior\n AriaLabel=\"Filter roles\"\n Title=\"Roles\"\n Subtitle=\"Define system and custom roles for permissions\">\n <!-- X-of-Y filtered count earns its meta spot per chrome conventions \u00A72.\n Partition badges (system/custom) stay dropped \u2014 they mirror the\n System/Custom filter chips in [toolbar]. -->\n <div meta>\n <mj-stat-badge\n [Count]=\"filteredRoles.length\"\n [Total]=\"roles.length\"\n Label=\"roles\">\n </mj-stat-badge>\n </div>\n <div toolbar>\n <!-- Concise-chrome control bar: search \u00B7 Filter. Type lives behind the one\n Filter button; applied filters show as removable chips below the card.\n On mobile the popover docks as a bottom sheet. -->\n <mj-page-search\n Placeholder=\"Search roles by name or description...\"\n [Value]=\"filters$.value.search\"\n (ValueChange)=\"updateFilter({ search: $event })\">\n </mj-page-search>\n <mj-filter-popover\n Label=\"Filters\"\n Icon=\"fa-solid fa-filter\"\n [ActiveCount]=\"TotalActiveFilterCount\"\n [ShowClearAll]=\"TotalActiveFilterCount > 0\"\n (ClearAllRequested)=\"clearAllAppliedFilters()\">\n <mj-filter-panel\n [Fields]=\"filterFields\"\n [Values]=\"filterValues\"\n (ValuesChange)=\"onFilterPanelChange($event)\"\n (Reset)=\"clearAllAppliedFilters()\">\n </mj-filter-panel>\n </mj-filter-popover>\n </div>\n <div actions>\n <mj-refresh-button [Loading]=\"isLoading\" (Clicked)=\"refreshData()\"></mj-refresh-button>\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"createNewRole()\" aria-label=\"Add new role\">\n <i class=\"fa-solid fa-plus\"></i> <span class=\"mj-action-label\">Add Role</span>\n </button>\n </div>\n</mj-page-header-interior>\n\n<!-- Scrollable Content Section \u2014 owned by <mj-page-body-interior>. -->\n<mj-page-body-interior>\n <!-- Loading State -->\n @if (isLoading) {\n <div class=\"loading-container\" role=\"status\" aria-live=\"polite\" aria-busy=\"true\">\n <mj-loading text=\"Loading roles...\" 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 mjButton variant=\"primary\" (click)=\"loadInitialData()\" aria-label=\"Retry loading roles\">\n <i class=\"fa-solid fa-refresh\" aria-hidden=\"true\"></i>\n Try Again\n </button>\n </div>\n </div>\n }\n\n <!-- Roles List -->\n @if (!isLoading && !error) {\n <div class=\"roles-list\" role=\"list\" aria-label=\"Roles list\">\n @for (role of filteredRoles; track role.ID) {\n <div\n class=\"role-card\"\n [class.expanded]=\"isRoleExpanded(role.ID)\"\n role=\"listitem\"\n [attr.aria-expanded]=\"isRoleExpanded(role.ID)\"\n >\n <div\n class=\"role-header\"\n (click)=\"toggleRoleExpansion(role.ID)\"\n tabindex=\"0\"\n (keydown.enter)=\"toggleRoleExpansion(role.ID)\"\n (keydown.space)=\"toggleRoleExpansion(role.ID); $event.preventDefault()\"\n role=\"button\"\n [attr.aria-label]=\"'Toggle details for ' + role.Name\"\n >\n <div class=\"role-info\">\n <div class=\"role-icon-wrapper\" aria-hidden=\"true\">\n <i [class]=\"'fa-solid ' + getRoleIcon(role)\"></i>\n </div>\n <div class=\"role-details\">\n <h3 class=\"role-name\">{{ role.Name }}</h3>\n <p class=\"role-description\">{{ role.Description || 'No description available' }}</p>\n </div>\n </div>\n\n <div class=\"role-meta\">\n <span class=\"role-type-badge\" [class]=\"getRoleTypeClass(role)\">\n {{ getRoleTypeLabel(role) }}\n </span>\n <div class=\"role-actions\" (click)=\"$event.stopPropagation()\">\n <button\n mjButton variant=\"flat\" size=\"sm\"\n (click)=\"editRole(role)\"\n title=\"Edit\"\n [attr.aria-label]=\"'Edit ' + role.Name\"\n [disabled]=\"isSystemRole(role)\"\n >\n <i class=\"fa-solid fa-edit\" aria-hidden=\"true\"></i>\n </button>\n <button\n mjButton variant=\"danger\" size=\"sm\"\n (click)=\"confirmDeleteRole(role)\"\n title=\"Delete\"\n [attr.aria-label]=\"'Delete ' + role.Name\"\n [disabled]=\"isSystemRole(role)\"\n >\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <button class=\"expand-btn\" aria-label=\"Toggle details\">\n <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n\n @if (isRoleExpanded(role.ID)) {\n <div class=\"role-content\">\n <!-- Mobile-only actions bar -->\n <div class=\"mobile-actions-bar\" (click)=\"$event.stopPropagation()\">\n <span class=\"role-type-badge\" [class]=\"getRoleTypeClass(role)\">\n {{ getRoleTypeLabel(role) }}\n </span>\n <div class=\"mobile-action-buttons\">\n <button\n mjButton variant=\"flat\" size=\"sm\"\n (click)=\"editRole(role)\"\n title=\"Edit\"\n [attr.aria-label]=\"'Edit ' + role.Name\"\n [disabled]=\"isSystemRole(role)\"\n >\n <i class=\"fa-solid fa-edit\" aria-hidden=\"true\"></i>\n <span class=\"btn-label\">Edit</span>\n </button>\n <button\n mjButton variant=\"danger\" size=\"sm\"\n (click)=\"confirmDeleteRole(role)\"\n title=\"Delete\"\n [attr.aria-label]=\"'Delete ' + role.Name\"\n [disabled]=\"isSystemRole(role)\"\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 <div class=\"role-stats\">\n <div class=\"stat-item\">\n <i class=\"fa-solid fa-users\" aria-hidden=\"true\"></i>\n <span class=\"stat-label\">Users:</span>\n <span class=\"stat-value\">0</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\">{{ role.__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\">{{ role.__mj_UpdatedAt | date:'short' }}</span>\n </div>\n </div>\n\n <div class=\"permissions-preview\">\n <h4 class=\"section-title\">\n <i class=\"fa-solid fa-key\" aria-hidden=\"true\"></i>\n Permissions Preview\n </h4>\n <p class=\"permissions-note\">Full permission management available in the Permissions tab</p>\n </div>\n </div>\n }\n </div>\n }\n\n @if (filteredRoles.length === 0) {\n <div class=\"empty-state\" role=\"status\">\n <i class=\"fa-solid fa-user-tag empty-icon\" aria-hidden=\"true\"></i>\n <p class=\"empty-text\">No roles found</p>\n <p class=\"empty-subtext\">Try adjusting your filters or create a new role</p>\n </div>\n }\n </div>\n }\n </mj-page-body-interior>\n\n <!-- Role Create/Edit Dialog -->\n <mj-role-dialog\n [data]=\"roleDialogData\"\n [visible]=\"showRoleDialog\"\n (result)=\"onRoleDialogResult($event)\">\n </mj-role-dialog>\n\n <!-- Delete Confirmation Dialog -->\n @if (showDeleteConfirm && selectedRole) {\n <div\n class=\"modal-backdrop\"\n (click)=\"showDeleteConfirm = false\"\n role=\"presentation\"\n >\n <div\n class=\"modal-dialog\"\n (click)=\"$event.stopPropagation()\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"delete-dialog-title\"\n aria-describedby=\"delete-dialog-desc\"\n >\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 the role <strong>{{ selectedRole.Name }}</strong>?</p>\n <p class=\"text-warning\">\n <i class=\"fa-solid fa-warning\" aria-hidden=\"true\"></i>\n This will affect all users assigned to this role.\n </p>\n </div>\n <div class=\"modal-footer\">\n <!-- Primary action (Delete) on LEFT per MD3 design guide -->\n <button mjButton variant=\"danger\" (click)=\"deleteRole()\" [disabled]=\"isLoading\">\n @if (isLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n Deleting...\n } @else {\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n Delete Role\n }\n </button>\n <button mjButton (click)=\"showDeleteConfirm = false\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n }\n\n", styles: ["/* =============================================================================\n Shared Admin Patterns - MJ Design System\n Common styles shared across all admin settings components.\n Component-specific CSS files override these values where needed.\n ============================================================================= */\n\n/* -----------------------------------------------------------------------------\n Host & Container\n ----------------------------------------------------------------------------- */\n:host {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n overflow: hidden;\n}\n\n/* -----------------------------------------------------------------------------\n Sticky Header\n ----------------------------------------------------------------------------- */\n.sticky-header {\n flex-shrink: 0;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n box-shadow: var(--mj-shadow-sm);\n z-index: 10;\n}\n\n/* -----------------------------------------------------------------------------\n Scrollable Content\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(--mj-bg-surface-sunken);\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@media (min-width: 1440px) {\n .scrollable-content {\n padding: 2rem 2.5rem;\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(--mj-bg-page);\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\n/* -----------------------------------------------------------------------------\n Statistics Cards Grid - Static Display (Non-interactive)\n ----------------------------------------------------------------------------- */\n.mj-grid-4 {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 0.5rem;\n padding: 0 1rem 0.75rem 1rem;\n background: var(--mj-bg-page);\n}\n\n@media (min-width: 768px) {\n .mj-grid-4 {\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(--mj-bg-surface-card);\n border-radius: var(--mj-radius-lg);\n padding: 1rem;\n box-shadow: var(--mj-shadow-sm);\n display: flex;\n align-items: center;\n gap: 1rem;\n border: 1px solid var(--mj-border-default);\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/* -----------------------------------------------------------------------------\n Stat Icon Containers\n ----------------------------------------------------------------------------- */\n.stat-icon {\n width: 48px;\n height: 48px;\n border-radius: var(--mj-radius-lg);\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: 52px;\n height: 52px;\n font-size: 1.375rem;\n }\n}\n\n.stat-icon-total {\n background: var(--mj-color-accent-300);\n color: var(--mj-brand-primary);\n}\n\n.stat-icon-active {\n background: var(--mj-color-success-100);\n color: var(--mj-status-success);\n}\n\n/* -----------------------------------------------------------------------------\n Stat Content Typography\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(--mj-text-primary);\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(--mj-text-secondary);\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(--mj-bg-surface-card);\n margin: 0 1rem 0.75rem 1rem;\n padding: 0.75rem;\n border-radius: var(--mj-radius-lg);\n border: 1px solid var(--mj-border-default);\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.mj-filter-group {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.mj-filter-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.mj-filter-buttons {\n display: flex;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-full);\n padding: 4px;\n gap: 2px;\n}\n\n/* -----------------------------------------------------------------------------\n Search Input\n ----------------------------------------------------------------------------- */\n.mj-search {\n position: relative;\n flex: 1;\n min-width: 200px;\n}\n\n@media (min-width: 640px) {\n .mj-search {\n min-width: 280px;\n }\n}\n\n@media (min-width: 1024px) {\n .mj-search {\n min-width: 400px;\n max-width: 600px;\n }\n}\n\n@media (min-width: 1440px) {\n .mj-search {\n min-width: 500px;\n max-width: 800px;\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(--mj-text-secondary);\n font-size: 1rem;\n pointer-events: none;\n transition: color 0.2s ease;\n}\n\n@media (min-width: 1024px) {\n .mj-search .mj-search-icon {\n left: 1.25rem;\n font-size: 1.125rem;\n }\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(--mj-border-default);\n border-radius: var(--mj-radius-full);\n font-size: 1rem;\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n}\n\n@media (min-width: 1024px) {\n .mj-search .mj-search-input {\n padding: 1rem 1.25rem 1rem 3.25rem;\n font-size: 1.0625rem;\n }\n}\n\n@media (min-width: 1440px) {\n .mj-search .mj-search-input {\n padding: 1.125rem 1.5rem 1.125rem 3.5rem;\n }\n}\n\n.mj-search .mj-search-input::placeholder {\n color: var(--mj-text-secondary);\n}\n\n.mj-search .mj-search-input:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n}\n\n.mj-search .mj-search-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px rgba(0, 118, 182, 0.2);\n background: var(--mj-bg-page);\n}\n\n.mj-search:focus-within .mj-search-icon {\n color: var(--mj-brand-primary);\n}\n\n/* -----------------------------------------------------------------------------\n Content Area\n ----------------------------------------------------------------------------- */\n.content-area {\n flex: 1 1 auto;\n overflow: visible;\n position: relative;\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-xl);\n box-shadow: var(--mj-shadow-sm);\n padding: 1rem;\n border: 1px solid var(--mj-border-default);\n}\n\n@media (min-width: 768px) {\n .content-area {\n padding: 1.5rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Expand Button\n ----------------------------------------------------------------------------- */\n.expand-btn {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1rem;\n cursor: pointer;\n transition: all 0.2s ease;\n border-radius: var(--mj-radius-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(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.expand-btn i {\n transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1);\n}\n\n/* -----------------------------------------------------------------------------\n Status Badge\n ----------------------------------------------------------------------------- */\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(--mj-radius-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(--mj-color-success-100);\n color: var(--mj-color-success-800);\n border: 1px solid var(--mj-status-success);\n}\n\n.status-badge.status-inactive {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n border: 1px solid var(--mj-status-error);\n}\n\n.status-badge i {\n font-size: 0.625rem;\n}\n\n/* -----------------------------------------------------------------------------\n Mobile Actions Bar\n ----------------------------------------------------------------------------- */\n.mobile-actions-bar {\n display: none;\n}\n\n.mobile-action-buttons {\n display: flex;\n gap: 0.5rem;\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(--mj-border-default);\n margin-bottom: 1.5rem;\n}\n\n.empty-state .empty-text {\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.5rem 0;\n}\n\n.empty-state .empty-subtext {\n font-size: 1rem;\n color: var(--mj-text-secondary);\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(--mj-status-error);\n margin-bottom: 1rem;\n}\n\n.error-container .error-message {\n font-size: 1.0625rem;\n color: var(--mj-text-primary);\n margin: 0 0 1.5rem 0;\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 cubic-bezier(0, 0, 0.2, 1);\n}\n\n.modal-dialog {\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-2xl);\n box-shadow: var(--mj-shadow-2xl);\n max-width: 500px;\n width: 100%;\n max-height: 90vh;\n overflow: hidden;\n animation: slideUp 0.3s cubic-bezier(0, 0, 0.2, 1);\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(--mj-border-default);\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(--mj-text-primary);\n margin: 0;\n}\n\n.modal-header .modal-close {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1.25rem;\n cursor: pointer;\n border-radius: var(--mj-radius-full);\n transition: all 0.2s ease;\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.modal-header .modal-close:hover {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.modal-body {\n padding: 1.5rem;\n}\n\n.modal-body p {\n font-size: 1rem;\n color: var(--mj-text-primary);\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-footer {\n display: flex;\n justify-content: flex-start;\n gap: 0.75rem;\n padding: 1.5rem;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\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-footer {\n padding: 1rem;\n flex-direction: column;\n }\n\n}\n\n/* The legacy \"MJ Button System\" rule block (.mj-btn / .mj-btn-primary /\n .mj-btn-secondary / .mj-btn-ghost / .mj-btn-danger / .mj-btn-sm /\n .mj-btn-icon-only / .mj-btn-icon-mobile) was removed 2026-05-19. These\n rules pre-dated the mjButton directive standardization (the directive\n uses BEM modifiers like .mj-btn--primary, single-dash names like\n .mj-btn-primary don't match it anyway). The base .mj-btn rule WAS active\n though \u2014 its component-scoped specificity outranked button.scss's global\n directive styles, so admin sub-page buttons rendered pill-shaped + 44px\n regardless of the variant/size requested via mjButton inputs. Removing the\n rule lets every sub-page fall back to the directive's intended look. */\n\n/* -----------------------------------------------------------------------------\n Filter Button (Mobile Only)\n ----------------------------------------------------------------------------- */\n.filter-button {\n display: none;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.75rem 1.25rem;\n border: 2px solid var(--mj-border-default);\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n border-radius: var(--mj-radius-full);\n font-size: 0.875rem;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n min-width: 44px;\n}\n\n.filter-button:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n}\n\n.filter-button:active {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.filter-button i {\n font-size: 1rem;\n}\n\n/* -----------------------------------------------------------------------------\n Filter Modal (Mobile Bottom Sheet)\n ----------------------------------------------------------------------------- */\n.filter-modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n backdrop-filter: blur(4px);\n display: flex;\n align-items: flex-end;\n justify-content: center;\n z-index: 1000;\n animation: fadeIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n padding: 0;\n}\n\n.filter-modal {\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-2xl) var(--mj-radius-2xl) 0 0;\n box-shadow: var(--mj-shadow-2xl);\n width: 100%;\n max-height: 80vh;\n overflow: hidden;\n animation: slideUpFromBottom 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex;\n flex-direction: column;\n}\n\n.filter-modal-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1.25rem 1.5rem;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.filter-modal-title {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.filter-modal-title i {\n color: var(--mj-brand-primary);\n font-size: 1.375rem;\n}\n\n.filter-modal-close {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1.25rem;\n cursor: pointer;\n border-radius: var(--mj-radius-full);\n transition: all 0.2s ease;\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.filter-modal-close:hover {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.filter-modal-body {\n padding: 1.5rem;\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n flex: 1;\n}\n\n.filter-modal-footer {\n display: flex;\n gap: 0.75rem;\n padding: 1.25rem 1.5rem;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n flex-shrink: 0;\n}\n\n.filter-modal-footer button {\n flex: 1;\n margin: 0;\n}\n\n/* Filter Options Container (Inside Modal) */\n.filter-options-container {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n}\n\n.filter-group {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.filter-group-label {\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin: 0;\n}\n\n.filter-group-options {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.filter-option {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.875rem 1rem;\n background: var(--mj-bg-surface-sunken);\n border: 2px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 56px;\n}\n\n.filter-option:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface-sunken);\n}\n\n.filter-option.selected {\n background: var(--mj-color-accent-300);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.filter-option input[type=\"checkbox\"],\n.filter-option input[type=\"radio\"] {\n width: 20px;\n height: 20px;\n cursor: pointer;\n accent-color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n.filter-option-label {\n flex: 1;\n font-size: 1rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.filter-option.selected .filter-option-label {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n/* -----------------------------------------------------------------------------\n Utility Classes\n ----------------------------------------------------------------------------- */\n.text-danger {\n color: var(--mj-status-error);\n}\n\n.text-warning {\n color: var(--mj-color-warning-500);\n}\n\n.text-success {\n color: var(--mj-status-success);\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@keyframes slideDown {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes slideUpFromBottom {\n from {\n transform: translateY(100%);\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,\nbutton:focus-visible {\n outline: 2px solid var(--mj-brand-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 Role Management Component - Component-Specific Styles\n Shared patterns are in ../shared/styles/_admin-patterns.css\n ============================================================================= */\n\n/* :host already provides flex column / 100% height / overflow hidden via\n _admin-patterns.css. No bespoke container wrapper \u2014 see Section 10 of\n explorer-chrome-conventions for the standardized sub-page layout contract. */\n\n/* Status / type chip group inside <mj-page-header-interior>'s [toolbar] slot. */\n.status-chips {\n display: inline-flex;\n align-items: center;\n gap: var(--mj-space-2);\n flex-shrink: 0;\n}\n\n/* -----------------------------------------------------------------------------\n Stats Grid Override - Different gap/padding values\n ----------------------------------------------------------------------------- */\n.mj-grid-4 {\n gap: 0.75rem;\n padding: 0 1rem 1rem 1rem;\n}\n\n@media (min-width: 768px) {\n .mj-grid-4 {\n padding: 0 1.5rem 1.25rem 1.5rem;\n gap: 1rem;\n }\n}\n\n@media (min-width: 1024px) {\n .mj-grid-4 {\n gap: 1.25rem;\n }\n}\n\n/* Stat Card Override - no box-shadow, different 768px padding */\n.mj-card {\n box-shadow: none;\n}\n\n@media (min-width: 768px) {\n .mj-card {\n padding: 1.125rem 1.25rem;\n }\n}\n\n/* Stat Icon Override - Component-specific colors */\n.stat-icon-system {\n background: var(--mj-color-warning-50);\n color: var(--mj-color-warning-700);\n}\n\n.stat-icon-custom {\n background: var(--mj-color-success-100);\n color: var(--mj-status-success);\n}\n\n.stat-icon-active {\n background: var(--mj-color-accent-300);\n color: var(--mj-brand-primary);\n}\n\n/* -----------------------------------------------------------------------------\n Roles List\n ----------------------------------------------------------------------------- */\n.roles-list {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n}\n\n/* -----------------------------------------------------------------------------\n Role Card\n ----------------------------------------------------------------------------- */\n.role-card {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-xl);\n overflow: hidden;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.role-card:hover {\n box-shadow: var(--mj-shadow-md);\n border-color: var(--mj-brand-primary);\n}\n\n.role-card.expanded {\n box-shadow: var(--mj-shadow-lg);\n border-color: var(--mj-brand-primary);\n}\n\n.role-card.expanded .expand-btn i {\n transform: rotate(180deg);\n}\n\n.role-header {\n padding: 1.25rem 1.5rem;\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n background: var(--mj-bg-surface);\n transition: background-color 0.2s ease;\n}\n\n.role-header:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.role-info {\n display: flex;\n align-items: center;\n gap: 1rem;\n flex: 1;\n min-width: 0;\n}\n\n.role-icon-wrapper {\n width: 52px;\n height: 52px;\n border-radius: var(--mj-radius-lg);\n background: var(--mj-color-accent-300);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-brand-primary);\n font-size: 1.375rem;\n flex-shrink: 0;\n}\n\n.role-details {\n flex: 1;\n min-width: 0;\n}\n\n.role-details .role-name {\n font-size: 1.125rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.25rem 0;\n word-break: break-word;\n overflow-wrap: break-word;\n}\n\n.role-details .role-description {\n font-size: 0.875rem;\n color: var(--mj-text-secondary);\n margin: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n@media (max-width: 639px) {\n .role-details .role-description {\n white-space: normal;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n }\n}\n\n.role-meta {\n display: flex;\n align-items: center;\n gap: 1rem;\n flex-shrink: 0;\n}\n\n/* -----------------------------------------------------------------------------\n Role Type Badge\n ----------------------------------------------------------------------------- */\n.role-type-badge {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.875rem;\n border-radius: var(--mj-radius-full);\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.role-type-badge.badge-system {\n background: var(--mj-color-warning-50);\n color: var(--mj-color-warning-800);\n border: 1px solid var(--mj-color-warning-500);\n}\n\n.role-type-badge.badge-custom {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n border: 1px solid var(--mj-status-success);\n}\n\n/* -----------------------------------------------------------------------------\n Role Actions\n ----------------------------------------------------------------------------- */\n.role-actions {\n display: flex;\n gap: 0.375rem;\n}\n\n/* Expand Button Override - different transition easing */\n.expand-btn i {\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n/* -----------------------------------------------------------------------------\n Role Content (Expanded)\n ----------------------------------------------------------------------------- */\n.role-content {\n padding: 1.5rem;\n background: var(--mj-bg-surface);\n border-top: 1px solid var(--mj-border-default);\n animation: slideDown 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.role-stats {\n display: flex;\n gap: 2rem;\n margin-bottom: 1.5rem;\n flex-wrap: wrap;\n}\n\n.role-stats .stat-item {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-size: 0.9375rem;\n padding: 0.5rem 1rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-md);\n}\n\n.role-stats .stat-item i {\n color: var(--mj-brand-primary);\n font-size: 1rem;\n}\n\n.role-stats .stat-item .stat-label {\n color: var(--mj-text-secondary);\n}\n\n.role-stats .stat-item .stat-value {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n\n/* -----------------------------------------------------------------------------\n Permissions Preview\n ----------------------------------------------------------------------------- */\n.permissions-preview {\n padding: 1.25rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n border: 1px solid var(--mj-border-default);\n}\n\n.permissions-preview .section-title {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.5rem 0;\n}\n\n.permissions-preview .section-title i {\n color: var(--mj-brand-primary);\n font-size: 1.125rem;\n}\n\n.permissions-preview .permissions-note {\n font-size: 0.9375rem;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n/* Modal Body Override - line-height and warning callout */\n.modal-body p {\n line-height: 1.6;\n}\n\n.modal-body .text-warning {\n color: var(--mj-color-warning-500);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n background: var(--mj-color-warning-50);\n border-radius: var(--mj-radius-md);\n font-weight: 500;\n}\n\n/* Modal Backdrop Override - uses top/left/right/bottom instead of inset */\n.modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n}\n\n/* -----------------------------------------------------------------------------\n Large Desktop (1440px+)\n ----------------------------------------------------------------------------- */\n@media (min-width: 1440px) {\n .role-management-container {\n margin: 0 auto;\n }\n\n .action-buttons {\n padding: 1rem 2rem;\n }\n\n .mj-grid-4 {\n padding: 0 2rem 1.25rem 2rem;\n gap: 1.5rem;\n }\n\n}\n\n/* -----------------------------------------------------------------------------\n Mobile (< 640px)\n ----------------------------------------------------------------------------- */\n@media (max-width: 639px) {\n .mj-grid-4 {\n display: none !important;\n }\n\n .scrollable-content {\n padding: 0.75rem;\n }\n\n .modal-backdrop {\n padding: 0.5rem;\n }\n\n .modal-dialog {\n border-radius: var(--mj-radius-xl);\n max-height: 95vh;\n }\n\n .roles-list {\n gap: 0.75rem;\n }\n\n .role-card {\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n }\n\n .role-header {\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n padding: 1rem;\n }\n\n .role-info {\n flex: 1;\n min-width: 0;\n flex-direction: row;\n align-items: flex-start;\n gap: 0.875rem;\n }\n\n .role-icon-wrapper {\n width: 40px;\n height: 40px;\n font-size: 1rem;\n flex-shrink: 0;\n }\n\n .role-details {\n flex: 1;\n min-width: 0;\n }\n\n .role-details .role-name {\n font-size: 1rem;\n line-height: 1.3;\n margin-bottom: 0.125rem;\n }\n\n .role-details .role-description {\n font-size: 0.8125rem;\n -webkit-line-clamp: 1;\n line-clamp: 1;\n }\n\n .role-meta {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: flex-end;\n padding-top: 0;\n border-top: none;\n width: auto;\n }\n\n .role-meta .role-type-badge {\n display: none;\n }\n\n .role-meta .role-actions {\n display: none;\n }\n\n .expand-btn {\n flex-shrink: 0;\n min-width: 40px;\n min-height: 40px;\n padding: 0.5rem;\n }\n\n .expand-btn i {\n font-size: 1rem;\n }\n\n .mobile-actions-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n padding: 0.875rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n margin-bottom: 1rem;\n }\n\n .mobile-actions-bar .role-type-badge {\n padding: 0.375rem 0.75rem;\n font-size: 0.6875rem;\n }\n\n .mobile-action-buttons {\n display: flex;\n gap: 0.5rem;\n }\n\n .mobile-action-buttons .mj-btn-sm {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.5rem 0.875rem;\n min-height: 36px;\n }\n\n .mobile-action-buttons .mj-btn-sm i {\n font-size: 0.875rem;\n }\n\n .mobile-action-buttons .btn-label {\n font-size: 0.8125rem;\n font-weight: 500;\n }\n\n .role-stats {\n flex-direction: column;\n gap: 0.5rem;\n margin-bottom: 1rem;\n }\n\n .role-content {\n padding: 1rem;\n }\n\n .permissions-preview {\n padding: 1rem;\n }\n\n .empty-state {\n padding: 3rem 1.5rem;\n }\n\n .empty-state .empty-icon {\n font-size: 3rem;\n }\n\n .loading-container {\n padding: 3rem 1.5rem;\n }\n}\n\n/* Small Mobile (< 375px) */\n@media (max-width: 374px) {\n .action-buttons {\n padding: 0.375rem 0.5rem;\n }\n\n .scrollable-content {\n padding: 0.5rem;\n }\n\n .role-header {\n padding: 0.75rem;\n }\n}\n"] }]
|
|
519
545
|
}], () => [{ type: i0.ChangeDetectorRef }, { type: i0.NgZone }], null); })();
|
|
520
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RoleManagementComponent, { className: "RoleManagementComponent", filePath: "src/lib/role-management/role-management.component.ts", lineNumber:
|
|
546
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RoleManagementComponent, { className: "RoleManagementComponent", filePath: "src/lib/role-management/role-management.component.ts", lineNumber: 30 }); })();
|
|
521
547
|
//# sourceMappingURL=role-management.component.js.map
|