@memberjunction/ng-explorer-settings 5.35.0 → 5.37.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 +0 -1
- package/dist/lib/application-management/application-management.component.d.ts.map +1 -1
- package/dist/lib/application-management/application-management.component.js +151 -206
- package/dist/lib/application-management/application-management.component.js.map +1 -1
- package/dist/lib/entity-permissions/entity-permissions.component.d.ts +8 -3
- package/dist/lib/entity-permissions/entity-permissions.component.d.ts.map +1 -1
- package/dist/lib/entity-permissions/entity-permissions.component.js +247 -411
- package/dist/lib/entity-permissions/entity-permissions.component.js.map +1 -1
- package/dist/lib/module.d.ts +1 -1
- package/dist/lib/module.d.ts.map +1 -1
- package/dist/lib/module.js +31 -4
- package/dist/lib/module.js.map +1 -1
- package/dist/lib/role-management/role-management.component.d.ts +0 -2
- package/dist/lib/role-management/role-management.component.d.ts.map +1 -1
- package/dist/lib/role-management/role-management.component.js +136 -259
- package/dist/lib/role-management/role-management.component.js.map +1 -1
- package/dist/lib/sql-logging/sql-logging.component.js +221 -224
- package/dist/lib/sql-logging/sql-logging.component.js.map +1 -1
- package/dist/lib/user-management/user-management.component.d.ts +12 -3
- package/dist/lib/user-management/user-management.component.d.ts.map +1 -1
- package/dist/lib/user-management/user-management.component.js +356 -463
- package/dist/lib/user-management/user-management.component.js.map +1 -1
- package/package.json +19 -19
|
@@ -11,407 +11,290 @@ import { RunView } from '@memberjunction/core';
|
|
|
11
11
|
import { BaseDashboard } from '@memberjunction/ng-shared';
|
|
12
12
|
import { RegisterClass, UUIDsEqual } from '@memberjunction/global';
|
|
13
13
|
import * as i0 from "@angular/core";
|
|
14
|
-
import * as i1 from "@
|
|
15
|
-
import * as i2 from "@memberjunction/ng-
|
|
16
|
-
import * as i3 from "
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
i0.ɵɵelementStart(0, "option", 48);
|
|
22
|
-
i0.ɵɵtext(1);
|
|
23
|
-
i0.ɵɵelementEnd();
|
|
24
|
-
} if (rf & 2) {
|
|
25
|
-
const role_r1 = ctx.$implicit;
|
|
26
|
-
i0.ɵɵproperty("value", role_r1.ID);
|
|
27
|
-
i0.ɵɵadvance();
|
|
28
|
-
i0.ɵɵtextInterpolate(role_r1.Name);
|
|
29
|
-
} }
|
|
30
|
-
function EntityPermissionsComponent_Conditional_76_Template(rf, ctx) { if (rf & 1) {
|
|
31
|
-
i0.ɵɵelementStart(0, "div", 50);
|
|
32
|
-
i0.ɵɵelement(1, "mj-loading", 55);
|
|
14
|
+
import * as i1 from "@memberjunction/ng-shared-generic";
|
|
15
|
+
import * as i2 from "@memberjunction/ng-ui-components";
|
|
16
|
+
import * as i3 from "./permission-dialog/permission-dialog.component";
|
|
17
|
+
const _forTrack0 = ($index, $item) => $item.entity.ID;
|
|
18
|
+
function EntityPermissionsComponent_Conditional_16_Template(rf, ctx) { if (rf & 1) {
|
|
19
|
+
i0.ɵɵelementStart(0, "div", 15);
|
|
20
|
+
i0.ɵɵelement(1, "mj-loading", 18);
|
|
33
21
|
i0.ɵɵelementEnd();
|
|
34
22
|
} }
|
|
35
|
-
function
|
|
36
|
-
const
|
|
37
|
-
i0.ɵɵelementStart(0, "div",
|
|
38
|
-
i0.ɵɵelement(2, "i",
|
|
39
|
-
i0.ɵɵelementStart(3, "p",
|
|
23
|
+
function EntityPermissionsComponent_Conditional_17_Template(rf, ctx) { if (rf & 1) {
|
|
24
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
25
|
+
i0.ɵɵelementStart(0, "div", 16)(1, "div", 19);
|
|
26
|
+
i0.ɵɵelement(2, "i", 20);
|
|
27
|
+
i0.ɵɵelementStart(3, "p", 21);
|
|
40
28
|
i0.ɵɵtext(4);
|
|
41
29
|
i0.ɵɵelementEnd();
|
|
42
|
-
i0.ɵɵelementStart(5, "button",
|
|
43
|
-
i0.ɵɵlistener("click", function
|
|
44
|
-
i0.ɵɵelement(6, "i",
|
|
30
|
+
i0.ɵɵelementStart(5, "button", 22);
|
|
31
|
+
i0.ɵɵlistener("click", function EntityPermissionsComponent_Conditional_17_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.loadInitialData()); });
|
|
32
|
+
i0.ɵɵelement(6, "i", 23);
|
|
45
33
|
i0.ɵɵtext(7, " Try Again ");
|
|
46
34
|
i0.ɵɵelementEnd()()();
|
|
47
35
|
} if (rf & 2) {
|
|
48
|
-
const
|
|
36
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
49
37
|
i0.ɵɵadvance(4);
|
|
50
|
-
i0.ɵɵtextInterpolate(
|
|
38
|
+
i0.ɵɵtextInterpolate(ctx_r1.error);
|
|
51
39
|
} }
|
|
52
|
-
function
|
|
53
|
-
i0.ɵɵelementStart(0, "tr",
|
|
40
|
+
function EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Conditional_19_Conditional_10_For_15_Template(rf, ctx) { if (rf & 1) {
|
|
41
|
+
i0.ɵɵelementStart(0, "tr", 55)(1, "td", 56);
|
|
54
42
|
i0.ɵɵtext(2);
|
|
55
43
|
i0.ɵɵelementEnd();
|
|
56
|
-
i0.ɵɵelementStart(3, "td",
|
|
44
|
+
i0.ɵɵelementStart(3, "td", 57);
|
|
57
45
|
i0.ɵɵelement(4, "i");
|
|
58
46
|
i0.ɵɵelementEnd();
|
|
59
|
-
i0.ɵɵelementStart(5, "td",
|
|
47
|
+
i0.ɵɵelementStart(5, "td", 57);
|
|
60
48
|
i0.ɵɵelement(6, "i");
|
|
61
49
|
i0.ɵɵelementEnd();
|
|
62
|
-
i0.ɵɵelementStart(7, "td",
|
|
50
|
+
i0.ɵɵelementStart(7, "td", 57);
|
|
63
51
|
i0.ɵɵelement(8, "i");
|
|
64
52
|
i0.ɵɵelementEnd();
|
|
65
|
-
i0.ɵɵelementStart(9, "td",
|
|
53
|
+
i0.ɵɵelementStart(9, "td", 57);
|
|
66
54
|
i0.ɵɵelement(10, "i");
|
|
67
55
|
i0.ɵɵelementEnd()();
|
|
68
56
|
} if (rf & 2) {
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
const
|
|
57
|
+
const roleId_r6 = ctx.$implicit;
|
|
58
|
+
const ea_r4 = i0.ɵɵnextContext(3).$implicit;
|
|
59
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
72
60
|
i0.ɵɵadvance(2);
|
|
73
|
-
i0.ɵɵtextInterpolate(
|
|
61
|
+
i0.ɵɵtextInterpolate(ctx_r1.getRoleName(roleId_r6));
|
|
74
62
|
i0.ɵɵadvance();
|
|
75
|
-
i0.ɵɵattribute("aria-label", "Create: " + (
|
|
63
|
+
i0.ɵɵattribute("aria-label", "Create: " + (ctx_r1.hasPermission(ea_r4, roleId_r6, "canCreate") ? "allowed" : "denied"));
|
|
76
64
|
i0.ɵɵadvance();
|
|
77
|
-
i0.ɵɵclassMap(
|
|
65
|
+
i0.ɵɵclassMap(ctx_r1.hasPermission(ea_r4, roleId_r6, "canCreate") ? "fa-solid fa-check text-success" : "fa-solid fa-times text-muted");
|
|
78
66
|
i0.ɵɵattribute("aria-hidden", true);
|
|
79
67
|
i0.ɵɵadvance();
|
|
80
|
-
i0.ɵɵattribute("aria-label", "Read: " + (
|
|
68
|
+
i0.ɵɵattribute("aria-label", "Read: " + (ctx_r1.hasPermission(ea_r4, roleId_r6, "canRead") ? "allowed" : "denied"));
|
|
81
69
|
i0.ɵɵadvance();
|
|
82
|
-
i0.ɵɵclassMap(
|
|
70
|
+
i0.ɵɵclassMap(ctx_r1.hasPermission(ea_r4, roleId_r6, "canRead") ? "fa-solid fa-check text-success" : "fa-solid fa-times text-muted");
|
|
83
71
|
i0.ɵɵattribute("aria-hidden", true);
|
|
84
72
|
i0.ɵɵadvance();
|
|
85
|
-
i0.ɵɵattribute("aria-label", "Update: " + (
|
|
73
|
+
i0.ɵɵattribute("aria-label", "Update: " + (ctx_r1.hasPermission(ea_r4, roleId_r6, "canUpdate") ? "allowed" : "denied"));
|
|
86
74
|
i0.ɵɵadvance();
|
|
87
|
-
i0.ɵɵclassMap(
|
|
75
|
+
i0.ɵɵclassMap(ctx_r1.hasPermission(ea_r4, roleId_r6, "canUpdate") ? "fa-solid fa-check text-success" : "fa-solid fa-times text-muted");
|
|
88
76
|
i0.ɵɵattribute("aria-hidden", true);
|
|
89
77
|
i0.ɵɵadvance();
|
|
90
|
-
i0.ɵɵattribute("aria-label", "Delete: " + (
|
|
78
|
+
i0.ɵɵattribute("aria-label", "Delete: " + (ctx_r1.hasPermission(ea_r4, roleId_r6, "canDelete") ? "allowed" : "denied"));
|
|
91
79
|
i0.ɵɵadvance();
|
|
92
|
-
i0.ɵɵclassMap(
|
|
80
|
+
i0.ɵɵclassMap(ctx_r1.hasPermission(ea_r4, roleId_r6, "canDelete") ? "fa-solid fa-check text-success" : "fa-solid fa-times text-muted");
|
|
93
81
|
i0.ɵɵattribute("aria-hidden", true);
|
|
94
82
|
} }
|
|
95
|
-
function
|
|
96
|
-
i0.ɵɵelementStart(0, "table",
|
|
83
|
+
function EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Conditional_19_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
84
|
+
i0.ɵɵelementStart(0, "table", 48)(1, "thead", 50)(2, "tr", 51)(3, "th", 52);
|
|
97
85
|
i0.ɵɵtext(4, "Role");
|
|
98
86
|
i0.ɵɵelementEnd();
|
|
99
|
-
i0.ɵɵelementStart(5, "th",
|
|
87
|
+
i0.ɵɵelementStart(5, "th", 53);
|
|
100
88
|
i0.ɵɵtext(6, "Create");
|
|
101
89
|
i0.ɵɵelementEnd();
|
|
102
|
-
i0.ɵɵelementStart(7, "th",
|
|
90
|
+
i0.ɵɵelementStart(7, "th", 53);
|
|
103
91
|
i0.ɵɵtext(8, "Read");
|
|
104
92
|
i0.ɵɵelementEnd();
|
|
105
|
-
i0.ɵɵelementStart(9, "th",
|
|
93
|
+
i0.ɵɵelementStart(9, "th", 53);
|
|
106
94
|
i0.ɵɵtext(10, "Update");
|
|
107
95
|
i0.ɵɵelementEnd();
|
|
108
|
-
i0.ɵɵelementStart(11, "th",
|
|
96
|
+
i0.ɵɵelementStart(11, "th", 53);
|
|
109
97
|
i0.ɵɵtext(12, "Delete");
|
|
110
98
|
i0.ɵɵelementEnd()()();
|
|
111
|
-
i0.ɵɵelementStart(13, "tbody",
|
|
112
|
-
i0.ɵɵrepeaterCreate(14,
|
|
99
|
+
i0.ɵɵelementStart(13, "tbody", 54);
|
|
100
|
+
i0.ɵɵrepeaterCreate(14, EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Conditional_19_Conditional_10_For_15_Template, 11, 17, "tr", 55, i0.ɵɵrepeaterTrackByIdentity);
|
|
113
101
|
i0.ɵɵelementEnd()();
|
|
114
102
|
} if (rf & 2) {
|
|
115
|
-
const
|
|
103
|
+
const ea_r4 = i0.ɵɵnextContext(2).$implicit;
|
|
116
104
|
i0.ɵɵadvance(14);
|
|
117
|
-
i0.ɵɵrepeater(
|
|
105
|
+
i0.ɵɵrepeater(ea_r4.rolePermissions.keys());
|
|
118
106
|
} }
|
|
119
|
-
function
|
|
107
|
+
function EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Conditional_19_Conditional_11_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
120
108
|
i0.ɵɵtext(0, " This entity is publicly accessible by all users. ");
|
|
121
109
|
} }
|
|
122
|
-
function
|
|
110
|
+
function EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Conditional_19_Conditional_11_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
123
111
|
i0.ɵɵtext(0, " No specific role permissions configured. Access is restricted to system administrators. ");
|
|
124
112
|
} }
|
|
125
|
-
function
|
|
126
|
-
i0.ɵɵelementStart(0, "p",
|
|
127
|
-
i0.ɵɵconditionalCreate(1,
|
|
113
|
+
function EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Conditional_19_Conditional_11_Template(rf, ctx) { if (rf & 1) {
|
|
114
|
+
i0.ɵɵelementStart(0, "p", 49);
|
|
115
|
+
i0.ɵɵconditionalCreate(1, EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Conditional_19_Conditional_11_Conditional_1_Template, 1, 0)(2, EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Conditional_19_Conditional_11_Conditional_2_Template, 1, 0);
|
|
128
116
|
i0.ɵɵelementEnd();
|
|
129
117
|
} if (rf & 2) {
|
|
130
|
-
const
|
|
118
|
+
const ea_r4 = i0.ɵɵnextContext(2).$implicit;
|
|
131
119
|
i0.ɵɵadvance();
|
|
132
|
-
i0.ɵɵconditional(
|
|
120
|
+
i0.ɵɵconditional(ea_r4.isPublic ? 1 : 2);
|
|
133
121
|
} }
|
|
134
|
-
function
|
|
135
|
-
const
|
|
136
|
-
i0.ɵɵelementStart(0, "div",
|
|
137
|
-
i0.ɵɵlistener("click", function
|
|
138
|
-
i0.ɵɵelementStart(2, "span",
|
|
139
|
-
i0.ɵɵelement(3, "i",
|
|
122
|
+
function EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Conditional_19_Template(rf, ctx) { if (rf & 1) {
|
|
123
|
+
const _r5 = i0.ɵɵgetCurrentView();
|
|
124
|
+
i0.ɵɵelementStart(0, "div", 44)(1, "div", 45);
|
|
125
|
+
i0.ɵɵlistener("click", function EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Conditional_19_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r5); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
126
|
+
i0.ɵɵelementStart(2, "span", 37);
|
|
127
|
+
i0.ɵɵelement(3, "i", 38);
|
|
140
128
|
i0.ɵɵtext(4);
|
|
141
129
|
i0.ɵɵelementEnd();
|
|
142
|
-
i0.ɵɵelementStart(5, "div",
|
|
143
|
-
i0.ɵɵlistener("click", function
|
|
144
|
-
i0.ɵɵelement(7, "i",
|
|
145
|
-
i0.ɵɵelementStart(8, "span",
|
|
130
|
+
i0.ɵɵelementStart(5, "div", 46)(6, "button", 40);
|
|
131
|
+
i0.ɵɵlistener("click", function EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Conditional_19_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r5); const ea_r4 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.editEntityPermissions(ea_r4)); });
|
|
132
|
+
i0.ɵɵelement(7, "i", 41);
|
|
133
|
+
i0.ɵɵelementStart(8, "span", 47);
|
|
146
134
|
i0.ɵɵtext(9, "Edit");
|
|
147
135
|
i0.ɵɵelementEnd()()()();
|
|
148
|
-
i0.ɵɵconditionalCreate(10,
|
|
136
|
+
i0.ɵɵconditionalCreate(10, EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Conditional_19_Conditional_10_Template, 16, 0, "table", 48)(11, EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Conditional_19_Conditional_11_Template, 3, 1, "p", 49);
|
|
149
137
|
i0.ɵɵelementEnd();
|
|
150
138
|
} if (rf & 2) {
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
i0.ɵɵattribute("aria-label", "Permissions for " +
|
|
139
|
+
const ea_r4 = i0.ɵɵnextContext().$implicit;
|
|
140
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
141
|
+
i0.ɵɵattribute("aria-label", "Permissions for " + ea_r4.entity.Name);
|
|
154
142
|
i0.ɵɵadvance(2);
|
|
155
|
-
i0.ɵɵclassMap(
|
|
143
|
+
i0.ɵɵclassMap(ctx_r1.getAccessLevelClass(ea_r4));
|
|
156
144
|
i0.ɵɵadvance();
|
|
157
|
-
i0.ɵɵclassMap(
|
|
145
|
+
i0.ɵɵclassMap(ctx_r1.getAccessLevelClass(ea_r4) === "access-public" ? "fa-solid fa-globe" : ctx_r1.getAccessLevelClass(ea_r4) === "access-restricted" ? "fa-solid fa-lock" : "fa-solid fa-key");
|
|
158
146
|
i0.ɵɵadvance();
|
|
159
|
-
i0.ɵɵtextInterpolate1(" ",
|
|
147
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.getAccessLevelLabel(ea_r4), " ");
|
|
160
148
|
i0.ɵɵadvance(2);
|
|
161
|
-
i0.ɵɵattribute("aria-label", "Edit permissions for " +
|
|
149
|
+
i0.ɵɵattribute("aria-label", "Edit permissions for " + ea_r4.entity.Name);
|
|
162
150
|
i0.ɵɵadvance(4);
|
|
163
|
-
i0.ɵɵconditional(
|
|
151
|
+
i0.ɵɵconditional(ea_r4.permissions.length > 0 ? 10 : 11);
|
|
164
152
|
} }
|
|
165
|
-
function
|
|
166
|
-
const
|
|
167
|
-
i0.ɵɵelementStart(0, "div",
|
|
168
|
-
i0.ɵɵlistener("click", function
|
|
169
|
-
i0.ɵɵelementStart(2, "div",
|
|
170
|
-
i0.ɵɵelement(4, "i",
|
|
171
|
-
i0.ɵɵelementEnd();
|
|
172
|
-
i0.ɵɵelementStart(5, "div",
|
|
153
|
+
function EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
154
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
155
|
+
i0.ɵɵelementStart(0, "div", 28)(1, "div", 29);
|
|
156
|
+
i0.ɵɵlistener("click", function EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Template_div_click_1_listener() { const ea_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.toggleEntityExpansion(ea_r4.entity.ID)); })("keydown.enter", function EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Template_div_keydown_enter_1_listener() { const ea_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.toggleEntityExpansion(ea_r4.entity.ID)); })("keydown.space", function EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Template_div_keydown_space_1_listener($event) { const ea_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); ctx_r1.toggleEntityExpansion(ea_r4.entity.ID); return i0.ɵɵresetView($event.preventDefault()); });
|
|
157
|
+
i0.ɵɵelementStart(2, "div", 30)(3, "div", 31);
|
|
158
|
+
i0.ɵɵelement(4, "i", 32);
|
|
159
|
+
i0.ɵɵelementEnd();
|
|
160
|
+
i0.ɵɵelementStart(5, "div", 33)(6, "h3", 34);
|
|
173
161
|
i0.ɵɵtext(7);
|
|
174
162
|
i0.ɵɵelementEnd();
|
|
175
|
-
i0.ɵɵelementStart(8, "p",
|
|
163
|
+
i0.ɵɵelementStart(8, "p", 35);
|
|
176
164
|
i0.ɵɵtext(9);
|
|
177
165
|
i0.ɵɵelementEnd()()();
|
|
178
|
-
i0.ɵɵelementStart(10, "div",
|
|
179
|
-
i0.ɵɵelement(12, "i",
|
|
166
|
+
i0.ɵɵelementStart(10, "div", 36)(11, "span", 37);
|
|
167
|
+
i0.ɵɵelement(12, "i", 38);
|
|
180
168
|
i0.ɵɵtext(13);
|
|
181
169
|
i0.ɵɵelementEnd();
|
|
182
|
-
i0.ɵɵelementStart(14, "div",
|
|
183
|
-
i0.ɵɵlistener("click", function
|
|
184
|
-
i0.ɵɵelementStart(15, "button",
|
|
185
|
-
i0.ɵɵlistener("click", function
|
|
186
|
-
i0.ɵɵelement(16, "i",
|
|
170
|
+
i0.ɵɵelementStart(14, "div", 39);
|
|
171
|
+
i0.ɵɵlistener("click", function EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Template_div_click_14_listener($event) { i0.ɵɵrestoreView(_r3); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
172
|
+
i0.ɵɵelementStart(15, "button", 40);
|
|
173
|
+
i0.ɵɵlistener("click", function EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Template_button_click_15_listener() { const ea_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.editEntityPermissions(ea_r4)); });
|
|
174
|
+
i0.ɵɵelement(16, "i", 41);
|
|
187
175
|
i0.ɵɵelementEnd()();
|
|
188
|
-
i0.ɵɵelementStart(17, "button",
|
|
189
|
-
i0.ɵɵelement(18, "i",
|
|
176
|
+
i0.ɵɵelementStart(17, "button", 42);
|
|
177
|
+
i0.ɵɵelement(18, "i", 43);
|
|
190
178
|
i0.ɵɵelementEnd()()();
|
|
191
|
-
i0.ɵɵconditionalCreate(19,
|
|
179
|
+
i0.ɵɵconditionalCreate(19, EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Conditional_19_Template, 12, 8, "div", 44);
|
|
192
180
|
i0.ɵɵelementEnd();
|
|
193
181
|
} if (rf & 2) {
|
|
194
|
-
const
|
|
195
|
-
const
|
|
196
|
-
i0.ɵɵclassProp("expanded",
|
|
197
|
-
i0.ɵɵattribute("aria-expanded",
|
|
182
|
+
const ea_r4 = ctx.$implicit;
|
|
183
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
184
|
+
i0.ɵɵclassProp("expanded", ctx_r1.isEntityExpanded(ea_r4.entity.ID));
|
|
185
|
+
i0.ɵɵattribute("aria-expanded", ctx_r1.isEntityExpanded(ea_r4.entity.ID));
|
|
198
186
|
i0.ɵɵadvance();
|
|
199
|
-
i0.ɵɵattribute("aria-label", "Expand permissions for " +
|
|
187
|
+
i0.ɵɵattribute("aria-label", "Expand permissions for " + ea_r4.entity.Name);
|
|
200
188
|
i0.ɵɵadvance(6);
|
|
201
|
-
i0.ɵɵtextInterpolate(
|
|
189
|
+
i0.ɵɵtextInterpolate(ea_r4.entity.Name);
|
|
202
190
|
i0.ɵɵadvance(2);
|
|
203
|
-
i0.ɵɵtextInterpolate(
|
|
191
|
+
i0.ɵɵtextInterpolate(ea_r4.entity.Description || "No description available");
|
|
204
192
|
i0.ɵɵadvance(2);
|
|
205
|
-
i0.ɵɵclassMap(
|
|
206
|
-
i0.ɵɵattribute("aria-label", "Access level: " +
|
|
193
|
+
i0.ɵɵclassMap(ctx_r1.getAccessLevelClass(ea_r4));
|
|
194
|
+
i0.ɵɵattribute("aria-label", "Access level: " + ctx_r1.getAccessLevelLabel(ea_r4));
|
|
207
195
|
i0.ɵɵadvance();
|
|
208
|
-
i0.ɵɵclassMap(
|
|
196
|
+
i0.ɵɵclassMap(ctx_r1.getAccessLevelClass(ea_r4) === "access-public" ? "fa-solid fa-globe" : ctx_r1.getAccessLevelClass(ea_r4) === "access-restricted" ? "fa-solid fa-lock" : "fa-solid fa-key");
|
|
209
197
|
i0.ɵɵadvance();
|
|
210
|
-
i0.ɵɵtextInterpolate1(" ",
|
|
198
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.getAccessLevelLabel(ea_r4), " ");
|
|
211
199
|
i0.ɵɵadvance(2);
|
|
212
|
-
i0.ɵɵattribute("aria-label", "Edit permissions for " +
|
|
200
|
+
i0.ɵɵattribute("aria-label", "Edit permissions for " + ea_r4.entity.Name);
|
|
213
201
|
i0.ɵɵadvance(2);
|
|
214
|
-
i0.ɵɵattribute("aria-label", (
|
|
202
|
+
i0.ɵɵattribute("aria-label", (ctx_r1.isEntityExpanded(ea_r4.entity.ID) ? "Collapse" : "Expand") + " permissions for " + ea_r4.entity.Name);
|
|
215
203
|
i0.ɵɵadvance(2);
|
|
216
|
-
i0.ɵɵconditional(
|
|
204
|
+
i0.ɵɵconditional(ctx_r1.isEntityExpanded(ea_r4.entity.ID) ? 19 : -1);
|
|
217
205
|
} }
|
|
218
|
-
function
|
|
219
|
-
i0.ɵɵelementStart(0, "div",
|
|
220
|
-
i0.ɵɵrepeaterCreate(1,
|
|
206
|
+
function EntityPermissionsComponent_Conditional_18_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
207
|
+
i0.ɵɵelementStart(0, "div", 24);
|
|
208
|
+
i0.ɵɵrepeaterCreate(1, EntityPermissionsComponent_Conditional_18_Conditional_0_For_2_Template, 20, 15, "div", 27, _forTrack0);
|
|
221
209
|
i0.ɵɵelementEnd();
|
|
222
210
|
} if (rf & 2) {
|
|
223
|
-
const
|
|
211
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
224
212
|
i0.ɵɵadvance();
|
|
225
|
-
i0.ɵɵrepeater(
|
|
213
|
+
i0.ɵɵrepeater(ctx_r1.filteredEntityAccess);
|
|
226
214
|
} }
|
|
227
|
-
function
|
|
228
|
-
i0.ɵɵelementStart(0, "span",
|
|
215
|
+
function EntityPermissionsComponent_Conditional_18_Conditional_1_For_2_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
216
|
+
i0.ɵɵelementStart(0, "span", 66);
|
|
229
217
|
i0.ɵɵtext(1);
|
|
230
218
|
i0.ɵɵelementEnd();
|
|
231
219
|
} if (rf & 2) {
|
|
232
|
-
const
|
|
233
|
-
i0.ɵɵattribute("aria-label",
|
|
220
|
+
const ea_r8 = i0.ɵɵnextContext().$implicit;
|
|
221
|
+
i0.ɵɵattribute("aria-label", ea_r8.permissions.length + " permission" + (ea_r8.permissions.length === 1 ? "" : "s"));
|
|
234
222
|
i0.ɵɵadvance();
|
|
235
|
-
i0.ɵɵtextInterpolate2(" ",
|
|
223
|
+
i0.ɵɵtextInterpolate2(" ", ea_r8.permissions.length, " permission", ea_r8.permissions.length === 1 ? "" : "s", " ");
|
|
236
224
|
} }
|
|
237
|
-
function
|
|
238
|
-
const
|
|
239
|
-
i0.ɵɵelementStart(0, "article",
|
|
240
|
-
i0.ɵɵelement(2, "i",
|
|
241
|
-
i0.ɵɵelementStart(3, "button",
|
|
242
|
-
i0.ɵɵlistener("click", function
|
|
243
|
-
i0.ɵɵelement(4, "i",
|
|
225
|
+
function EntityPermissionsComponent_Conditional_18_Conditional_1_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
226
|
+
const _r7 = i0.ɵɵgetCurrentView();
|
|
227
|
+
i0.ɵɵelementStart(0, "article", 59)(1, "div", 60);
|
|
228
|
+
i0.ɵɵelement(2, "i", 61);
|
|
229
|
+
i0.ɵɵelementStart(3, "button", 40);
|
|
230
|
+
i0.ɵɵlistener("click", function EntityPermissionsComponent_Conditional_18_Conditional_1_For_2_Template_button_click_3_listener() { const ea_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.editEntityPermissions(ea_r8)); });
|
|
231
|
+
i0.ɵɵelement(4, "i", 41);
|
|
244
232
|
i0.ɵɵelementEnd()();
|
|
245
|
-
i0.ɵɵelementStart(5, "h4",
|
|
233
|
+
i0.ɵɵelementStart(5, "h4", 62);
|
|
246
234
|
i0.ɵɵtext(6);
|
|
247
235
|
i0.ɵɵelementEnd();
|
|
248
|
-
i0.ɵɵelementStart(7, "p",
|
|
236
|
+
i0.ɵɵelementStart(7, "p", 63);
|
|
249
237
|
i0.ɵɵtext(8);
|
|
250
238
|
i0.ɵɵelementEnd();
|
|
251
|
-
i0.ɵɵelementStart(9, "div",
|
|
252
|
-
i0.ɵɵelement(11, "i",
|
|
239
|
+
i0.ɵɵelementStart(9, "div", 64)(10, "span", 65);
|
|
240
|
+
i0.ɵɵelement(11, "i", 38);
|
|
253
241
|
i0.ɵɵtext(12);
|
|
254
242
|
i0.ɵɵelementEnd();
|
|
255
|
-
i0.ɵɵconditionalCreate(13,
|
|
243
|
+
i0.ɵɵconditionalCreate(13, EntityPermissionsComponent_Conditional_18_Conditional_1_For_2_Conditional_13_Template, 2, 3, "span", 66);
|
|
256
244
|
i0.ɵɵelementEnd()();
|
|
257
245
|
} if (rf & 2) {
|
|
258
|
-
const
|
|
259
|
-
const
|
|
260
|
-
i0.ɵɵclassMap(
|
|
261
|
-
i0.ɵɵattribute("aria-label", "Entity: " +
|
|
246
|
+
const ea_r8 = ctx.$implicit;
|
|
247
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
248
|
+
i0.ɵɵclassMap(ctx_r1.getAccessLevelClass(ea_r8));
|
|
249
|
+
i0.ɵɵattribute("aria-label", "Entity: " + ea_r8.entity.Name);
|
|
262
250
|
i0.ɵɵadvance(3);
|
|
263
|
-
i0.ɵɵattribute("aria-label", "Edit permissions for " +
|
|
251
|
+
i0.ɵɵattribute("aria-label", "Edit permissions for " + ea_r8.entity.Name);
|
|
264
252
|
i0.ɵɵadvance(3);
|
|
265
|
-
i0.ɵɵtextInterpolate(
|
|
253
|
+
i0.ɵɵtextInterpolate(ea_r8.entity.Name);
|
|
266
254
|
i0.ɵɵadvance(2);
|
|
267
|
-
i0.ɵɵtextInterpolate(
|
|
255
|
+
i0.ɵɵtextInterpolate(ea_r8.entity.Description || "No description");
|
|
268
256
|
i0.ɵɵadvance(2);
|
|
269
|
-
i0.ɵɵattribute("aria-label", "Access level: " +
|
|
257
|
+
i0.ɵɵattribute("aria-label", "Access level: " + ctx_r1.getAccessLevelLabel(ea_r8));
|
|
270
258
|
i0.ɵɵadvance();
|
|
271
|
-
i0.ɵɵclassMap(
|
|
259
|
+
i0.ɵɵclassMap(ctx_r1.getAccessLevelClass(ea_r8) === "access-public" ? "fa-solid fa-globe" : ctx_r1.getAccessLevelClass(ea_r8) === "access-restricted" ? "fa-solid fa-lock" : "fa-solid fa-key");
|
|
272
260
|
i0.ɵɵadvance();
|
|
273
|
-
i0.ɵɵtextInterpolate1(" ",
|
|
261
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.getAccessLevelLabel(ea_r8), " ");
|
|
274
262
|
i0.ɵɵadvance();
|
|
275
|
-
i0.ɵɵconditional(
|
|
263
|
+
i0.ɵɵconditional(ea_r8.permissions.length > 0 ? 13 : -1);
|
|
276
264
|
} }
|
|
277
|
-
function
|
|
278
|
-
i0.ɵɵelementStart(0, "div",
|
|
279
|
-
i0.ɵɵrepeaterCreate(1,
|
|
265
|
+
function EntityPermissionsComponent_Conditional_18_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
266
|
+
i0.ɵɵelementStart(0, "div", 25);
|
|
267
|
+
i0.ɵɵrepeaterCreate(1, EntityPermissionsComponent_Conditional_18_Conditional_1_For_2_Template, 14, 11, "article", 58, _forTrack0);
|
|
280
268
|
i0.ɵɵelementEnd();
|
|
281
269
|
} if (rf & 2) {
|
|
282
|
-
const
|
|
270
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
283
271
|
i0.ɵɵadvance();
|
|
284
|
-
i0.ɵɵrepeater(
|
|
272
|
+
i0.ɵɵrepeater(ctx_r1.filteredEntityAccess);
|
|
285
273
|
} }
|
|
286
|
-
function
|
|
287
|
-
const
|
|
288
|
-
i0.ɵɵelementStart(0, "div",
|
|
289
|
-
i0.ɵɵelement(1, "i",
|
|
290
|
-
i0.ɵɵelementStart(2, "h3",
|
|
274
|
+
function EntityPermissionsComponent_Conditional_18_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
275
|
+
const _r9 = i0.ɵɵgetCurrentView();
|
|
276
|
+
i0.ɵɵelementStart(0, "div", 26);
|
|
277
|
+
i0.ɵɵelement(1, "i", 67);
|
|
278
|
+
i0.ɵɵelementStart(2, "h3", 68);
|
|
291
279
|
i0.ɵɵtext(3, "No Permissions Found");
|
|
292
280
|
i0.ɵɵelementEnd();
|
|
293
|
-
i0.ɵɵelementStart(4, "p",
|
|
281
|
+
i0.ɵɵelementStart(4, "p", 69);
|
|
294
282
|
i0.ɵɵtext(5, "Try adjusting your filters or refresh the data.");
|
|
295
283
|
i0.ɵɵelementEnd();
|
|
296
|
-
i0.ɵɵelementStart(6, "button",
|
|
297
|
-
i0.ɵɵlistener("click", function
|
|
298
|
-
i0.ɵɵelement(7, "i",
|
|
284
|
+
i0.ɵɵelementStart(6, "button", 70);
|
|
285
|
+
i0.ɵɵlistener("click", function EntityPermissionsComponent_Conditional_18_Conditional_2_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.refreshData()); });
|
|
286
|
+
i0.ɵɵelement(7, "i", 23);
|
|
299
287
|
i0.ɵɵtext(8, " Refresh ");
|
|
300
288
|
i0.ɵɵelementEnd()();
|
|
301
289
|
} }
|
|
302
|
-
function
|
|
303
|
-
i0.ɵɵ
|
|
304
|
-
i0.ɵɵconditionalCreate(
|
|
305
|
-
i0.ɵɵconditionalCreate(3, EntityPermissionsComponent_Conditional_78_Conditional_3_Template, 9, 0, "div", 62);
|
|
306
|
-
i0.ɵɵelementEnd();
|
|
290
|
+
function EntityPermissionsComponent_Conditional_18_Template(rf, ctx) { if (rf & 1) {
|
|
291
|
+
i0.ɵɵconditionalCreate(0, EntityPermissionsComponent_Conditional_18_Conditional_0_Template, 3, 0, "div", 24)(1, EntityPermissionsComponent_Conditional_18_Conditional_1_Template, 3, 0, "div", 25);
|
|
292
|
+
i0.ɵɵconditionalCreate(2, EntityPermissionsComponent_Conditional_18_Conditional_2_Template, 9, 0, "div", 26);
|
|
307
293
|
} if (rf & 2) {
|
|
308
|
-
const
|
|
309
|
-
i0.ɵɵ
|
|
310
|
-
i0.ɵɵconditional(ctx_r2.viewMode === "list" ? 1 : 2);
|
|
294
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
295
|
+
i0.ɵɵconditional(ctx_r1.viewMode === "list" ? 0 : 1);
|
|
311
296
|
i0.ɵɵadvance(2);
|
|
312
|
-
i0.ɵɵconditional(
|
|
313
|
-
} }
|
|
314
|
-
function EntityPermissionsComponent_Conditional_80_For_39_Template(rf, ctx) { if (rf & 1) {
|
|
315
|
-
const _r12 = i0.ɵɵgetCurrentView();
|
|
316
|
-
i0.ɵɵelementStart(0, "label", 118)(1, "input", 129);
|
|
317
|
-
i0.ɵɵlistener("change", function EntityPermissionsComponent_Conditional_80_For_39_Template_input_change_1_listener($event) { i0.ɵɵrestoreView(_r12); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onRoleFilterChange($event)); });
|
|
318
|
-
i0.ɵɵelementEnd();
|
|
319
|
-
i0.ɵɵelementStart(2, "span", 120);
|
|
320
|
-
i0.ɵɵtext(3);
|
|
321
|
-
i0.ɵɵelementEnd()();
|
|
322
|
-
} if (rf & 2) {
|
|
323
|
-
const role_r13 = ctx.$implicit;
|
|
324
|
-
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
325
|
-
i0.ɵɵclassProp("selected", ctx_r2.filters$.value.roleId === role_r13.ID);
|
|
326
|
-
i0.ɵɵadvance();
|
|
327
|
-
i0.ɵɵproperty("value", role_r13.ID)("checked", ctx_r2.filters$.value.roleId === role_r13.ID);
|
|
328
|
-
i0.ɵɵadvance(2);
|
|
329
|
-
i0.ɵɵtextInterpolate(role_r13.Name);
|
|
330
|
-
} }
|
|
331
|
-
function EntityPermissionsComponent_Conditional_80_Template(rf, ctx) { if (rf & 1) {
|
|
332
|
-
const _r11 = i0.ɵɵgetCurrentView();
|
|
333
|
-
i0.ɵɵelementStart(0, "div", 107);
|
|
334
|
-
i0.ɵɵlistener("click", function EntityPermissionsComponent_Conditional_80_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r11); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.showMobileFilters = false); });
|
|
335
|
-
i0.ɵɵelementStart(1, "div", 108);
|
|
336
|
-
i0.ɵɵlistener("click", function EntityPermissionsComponent_Conditional_80_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r11); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
337
|
-
i0.ɵɵelementStart(2, "div", 109)(3, "h3", 110);
|
|
338
|
-
i0.ɵɵelement(4, "i", 37);
|
|
339
|
-
i0.ɵɵtext(5, " Filters ");
|
|
340
|
-
i0.ɵɵelementEnd();
|
|
341
|
-
i0.ɵɵelementStart(6, "button", 111);
|
|
342
|
-
i0.ɵɵlistener("click", function EntityPermissionsComponent_Conditional_80_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r11); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.showMobileFilters = false); });
|
|
343
|
-
i0.ɵɵelement(7, "i", 112);
|
|
344
|
-
i0.ɵɵelementEnd()();
|
|
345
|
-
i0.ɵɵelementStart(8, "div", 113)(9, "div", 114)(10, "div", 115)(11, "h4", 116);
|
|
346
|
-
i0.ɵɵtext(12, "Access Level");
|
|
347
|
-
i0.ɵɵelementEnd();
|
|
348
|
-
i0.ɵɵelementStart(13, "div", 117)(14, "label", 118)(15, "input", 119);
|
|
349
|
-
i0.ɵɵlistener("change", function EntityPermissionsComponent_Conditional_80_Template_input_change_15_listener() { i0.ɵɵrestoreView(_r11); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAccessLevelChange("all")); });
|
|
350
|
-
i0.ɵɵelementEnd();
|
|
351
|
-
i0.ɵɵelementStart(16, "span", 120);
|
|
352
|
-
i0.ɵɵtext(17, "All Levels");
|
|
353
|
-
i0.ɵɵelementEnd()();
|
|
354
|
-
i0.ɵɵelementStart(18, "label", 118)(19, "input", 121);
|
|
355
|
-
i0.ɵɵlistener("change", function EntityPermissionsComponent_Conditional_80_Template_input_change_19_listener() { i0.ɵɵrestoreView(_r11); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAccessLevelChange("public")); });
|
|
356
|
-
i0.ɵɵelementEnd();
|
|
357
|
-
i0.ɵɵelementStart(20, "span", 120);
|
|
358
|
-
i0.ɵɵtext(21, "Public Entities");
|
|
359
|
-
i0.ɵɵelementEnd()();
|
|
360
|
-
i0.ɵɵelementStart(22, "label", 118)(23, "input", 122);
|
|
361
|
-
i0.ɵɵlistener("change", function EntityPermissionsComponent_Conditional_80_Template_input_change_23_listener() { i0.ɵɵrestoreView(_r11); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAccessLevelChange("restricted")); });
|
|
362
|
-
i0.ɵɵelementEnd();
|
|
363
|
-
i0.ɵɵelementStart(24, "span", 120);
|
|
364
|
-
i0.ɵɵtext(25, "Restricted Entities");
|
|
365
|
-
i0.ɵɵelementEnd()();
|
|
366
|
-
i0.ɵɵelementStart(26, "label", 118)(27, "input", 123);
|
|
367
|
-
i0.ɵɵlistener("change", function EntityPermissionsComponent_Conditional_80_Template_input_change_27_listener() { i0.ɵɵrestoreView(_r11); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAccessLevelChange("custom")); });
|
|
368
|
-
i0.ɵɵelementEnd();
|
|
369
|
-
i0.ɵɵelementStart(28, "span", 120);
|
|
370
|
-
i0.ɵɵtext(29, "Custom Access Entities");
|
|
371
|
-
i0.ɵɵelementEnd()()()();
|
|
372
|
-
i0.ɵɵelementStart(30, "div", 115)(31, "h4", 116);
|
|
373
|
-
i0.ɵɵtext(32, "Filter by Role");
|
|
374
|
-
i0.ɵɵelementEnd();
|
|
375
|
-
i0.ɵɵelementStart(33, "div", 117)(34, "label", 118)(35, "input", 124);
|
|
376
|
-
i0.ɵɵlistener("change", function EntityPermissionsComponent_Conditional_80_Template_input_change_35_listener($event) { i0.ɵɵrestoreView(_r11); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onRoleFilterChange($event)); });
|
|
377
|
-
i0.ɵɵelementEnd();
|
|
378
|
-
i0.ɵɵelementStart(36, "span", 120);
|
|
379
|
-
i0.ɵɵtext(37, "All Roles");
|
|
380
|
-
i0.ɵɵelementEnd()();
|
|
381
|
-
i0.ɵɵrepeaterCreate(38, EntityPermissionsComponent_Conditional_80_For_39_Template, 4, 5, "label", 125, _forTrack0);
|
|
382
|
-
i0.ɵɵelementEnd()()()();
|
|
383
|
-
i0.ɵɵelementStart(40, "div", 126)(41, "button", 127);
|
|
384
|
-
i0.ɵɵlistener("click", function EntityPermissionsComponent_Conditional_80_Template_button_click_41_listener() { i0.ɵɵrestoreView(_r11); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.showMobileFilters = false); });
|
|
385
|
-
i0.ɵɵtext(42, " Apply Filters ");
|
|
386
|
-
i0.ɵɵelementEnd();
|
|
387
|
-
i0.ɵɵelementStart(43, "button", 128);
|
|
388
|
-
i0.ɵɵlistener("click", function EntityPermissionsComponent_Conditional_80_Template_button_click_43_listener() { i0.ɵɵrestoreView(_r11); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.showMobileFilters = false); });
|
|
389
|
-
i0.ɵɵtext(44, " Cancel ");
|
|
390
|
-
i0.ɵɵelementEnd()()()();
|
|
391
|
-
} if (rf & 2) {
|
|
392
|
-
const ctx_r2 = i0.ɵɵnextContext();
|
|
393
|
-
i0.ɵɵadvance(14);
|
|
394
|
-
i0.ɵɵclassProp("selected", ctx_r2.filters$.value.accessLevel === "all");
|
|
395
|
-
i0.ɵɵadvance();
|
|
396
|
-
i0.ɵɵproperty("checked", ctx_r2.filters$.value.accessLevel === "all");
|
|
397
|
-
i0.ɵɵadvance(3);
|
|
398
|
-
i0.ɵɵclassProp("selected", ctx_r2.filters$.value.accessLevel === "public");
|
|
399
|
-
i0.ɵɵadvance();
|
|
400
|
-
i0.ɵɵproperty("checked", ctx_r2.filters$.value.accessLevel === "public");
|
|
401
|
-
i0.ɵɵadvance(3);
|
|
402
|
-
i0.ɵɵclassProp("selected", ctx_r2.filters$.value.accessLevel === "restricted");
|
|
403
|
-
i0.ɵɵadvance();
|
|
404
|
-
i0.ɵɵproperty("checked", ctx_r2.filters$.value.accessLevel === "restricted");
|
|
405
|
-
i0.ɵɵadvance(3);
|
|
406
|
-
i0.ɵɵclassProp("selected", ctx_r2.filters$.value.accessLevel === "custom");
|
|
407
|
-
i0.ɵɵadvance();
|
|
408
|
-
i0.ɵɵproperty("checked", ctx_r2.filters$.value.accessLevel === "custom");
|
|
409
|
-
i0.ɵɵadvance(7);
|
|
410
|
-
i0.ɵɵclassProp("selected", !ctx_r2.filters$.value.roleId);
|
|
411
|
-
i0.ɵɵadvance();
|
|
412
|
-
i0.ɵɵproperty("checked", !ctx_r2.filters$.value.roleId);
|
|
413
|
-
i0.ɵɵadvance(3);
|
|
414
|
-
i0.ɵɵrepeater(ctx_r2.roles);
|
|
297
|
+
i0.ɵɵconditional(ctx_r1.filteredEntityAccess.length === 0 ? 2 : -1);
|
|
415
298
|
} }
|
|
416
299
|
let EntityPermissionsComponent = class EntityPermissionsComponent extends BaseDashboard {
|
|
417
300
|
cdr;
|
|
@@ -441,7 +324,12 @@ let EntityPermissionsComponent = class EntityPermissionsComponent extends BaseDa
|
|
|
441
324
|
// UI State
|
|
442
325
|
expandedEntityId = null;
|
|
443
326
|
viewMode = 'list';
|
|
444
|
-
|
|
327
|
+
/** Options for the <mj-view-toggle> in the interior chrome. Icon-only;
|
|
328
|
+
title drives the tooltip + aria-label. */
|
|
329
|
+
viewToggleOptions = [
|
|
330
|
+
{ key: 'list', icon: 'fa-solid fa-list', title: 'List View' },
|
|
331
|
+
{ key: 'grid', icon: 'fa-solid fa-th', title: 'Grid View' }
|
|
332
|
+
];
|
|
445
333
|
destroy$ = new Subject();
|
|
446
334
|
get metadata() { return this.ProviderToUse; }
|
|
447
335
|
constructor(cdr, ngZone) {
|
|
@@ -596,22 +484,49 @@ let EntityPermissionsComponent = class EntityPermissionsComponent extends BaseDa
|
|
|
596
484
|
};
|
|
597
485
|
}
|
|
598
486
|
// Public methods for template
|
|
599
|
-
onSearchChange(event) {
|
|
600
|
-
const value = event.target.value;
|
|
601
|
-
this.updateFilter({ entitySearch: value });
|
|
602
|
-
}
|
|
603
487
|
onAccessLevelChange(level) {
|
|
604
488
|
this.updateFilter({ accessLevel: level });
|
|
605
489
|
}
|
|
606
|
-
onRoleFilterChange(event) {
|
|
607
|
-
const value = event.target.value;
|
|
608
|
-
this.updateFilter({ roleId: value || null });
|
|
609
|
-
}
|
|
610
490
|
updateFilter(partial) {
|
|
611
491
|
this.filters$.next({
|
|
612
492
|
...this.filters$.value,
|
|
613
493
|
...partial
|
|
614
494
|
});
|
|
495
|
+
// Discrete changes (chips, popover dropdowns) apply immediately. Text search
|
|
496
|
+
// still goes through the 300ms debounce in setupFilterSubscription.
|
|
497
|
+
if (!('entitySearch' in partial)) {
|
|
498
|
+
this.applyFilters();
|
|
499
|
+
this.cdr.markForCheck();
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
// -- Filter panel binding (mj-filter-panel inside the popover) -------------
|
|
503
|
+
// Role lives in the popover; access level chips track their own state in
|
|
504
|
+
// the toolbar slot. See Section 10 of explorer-chrome-conventions.
|
|
505
|
+
get filterFields() {
|
|
506
|
+
return [{
|
|
507
|
+
key: 'roleId',
|
|
508
|
+
type: 'dropdown',
|
|
509
|
+
label: 'Role',
|
|
510
|
+
icon: 'fa-solid fa-user-shield',
|
|
511
|
+
placeholder: 'All Roles',
|
|
512
|
+
filterable: this.roles.length > 10,
|
|
513
|
+
options: [
|
|
514
|
+
{ text: 'All Roles', value: '' },
|
|
515
|
+
...this.roles.map(r => ({ text: r.Name ?? '', value: r.ID }))
|
|
516
|
+
]
|
|
517
|
+
}];
|
|
518
|
+
}
|
|
519
|
+
get filterValues() {
|
|
520
|
+
return { roleId: this.filters$.value.roleId ?? '' };
|
|
521
|
+
}
|
|
522
|
+
get popoverActiveFilterCount() {
|
|
523
|
+
return this.filters$.value.roleId ? 1 : 0;
|
|
524
|
+
}
|
|
525
|
+
onFilterPanelChange(values) {
|
|
526
|
+
const roleId = values['roleId'] || null;
|
|
527
|
+
this.filters$.next({ ...this.filters$.value, roleId });
|
|
528
|
+
this.applyFilters();
|
|
529
|
+
this.cdr.markForCheck();
|
|
615
530
|
}
|
|
616
531
|
toggleEntityExpansion(entityId) {
|
|
617
532
|
this.expandedEntityId = this.expandedEntityId === entityId ? null : entityId;
|
|
@@ -682,153 +597,74 @@ let EntityPermissionsComponent = class EntityPermissionsComponent extends BaseDa
|
|
|
682
597
|
this.viewMode = mode;
|
|
683
598
|
}
|
|
684
599
|
static ɵfac = function EntityPermissionsComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || EntityPermissionsComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i0.NgZone)); };
|
|
685
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: EntityPermissionsComponent, selectors: [["mj-entity-permissions"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls:
|
|
686
|
-
i0.ɵɵelementStart(0, "
|
|
687
|
-
i0.ɵɵ
|
|
688
|
-
i0.ɵɵelement(5, "i", 5);
|
|
600
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: EntityPermissionsComponent, selectors: [["mj-entity-permissions"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 20, vars: 19, consts: [["AriaLabel", "Filter entity permissions", "Title", "Entity Permissions", "Subtitle", "Manage entity-level access for each role"], ["meta", ""], ["Label", "entities", 3, "Count", "Total"], ["toolbar", ""], ["Placeholder", "Search entities by name or description...", 3, "ValueChange", "Value"], ["role", "group", "aria-label", "Filter by access level", 1, "status-chips"], ["Label", "All", 3, "Clicked", "Active"], ["Label", "Public", 3, "Clicked", "Active"], ["Label", "Restricted", 3, "Clicked", "Active"], ["Label", "Custom", 3, "Clicked", "Active"], ["actions", ""], [3, "ClearAllRequested", "ActiveCount", "ShowClearAll"], [3, "ValuesChange", "Reset", "Fields", "Values"], [3, "KeyChange", "Options", "ActiveKey"], [3, "Clicked", "Loading"], ["role", "status", "aria-live", "polite", "aria-busy", "true", 1, "loading-container"], ["role", "alert", "aria-live", "assertive", 1, "error-container"], [3, "result", "visible", "data"], ["text", "Loading entity permissions...", "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 entity permissions", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-refresh"], ["role", "list", "aria-label", "Entity permissions list", 1, "entities-list"], ["role", "list", "aria-label", "Entity permissions grid", 1, "entities-grid"], ["role", "status", 1, "empty-state"], ["role", "listitem", 1, "entity-card", 3, "expanded"], ["role", "listitem", 1, "entity-card"], ["tabindex", "0", "role", "button", 1, "entity-header", 3, "click", "keydown.enter", "keydown.space"], [1, "entity-info"], ["aria-hidden", "true", 1, "entity-icon-wrapper"], [1, "fa-solid", "fa-table"], [1, "entity-details"], [1, "entity-name"], [1, "entity-description"], [1, "entity-meta"], [1, "access-badge"], ["aria-hidden", "true"], [1, "entity-actions", 3, "click"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Edit Permissions", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-edit"], [1, "expand-btn"], ["aria-hidden", "true", 1, "fa-solid", "fa-chevron-down"], ["role", "region", 1, "entity-content"], [1, "mobile-actions-bar", 3, "click"], [1, "mobile-action-buttons"], [1, "btn-label"], ["role", "table", "aria-label", "Role permissions", 1, "permissions-grid"], [1, "no-permissions"], ["role", "rowgroup", 1, "permissions-header"], ["role", "row"], ["scope", "col", "role", "columnheader", 1, "role-header"], ["scope", "col", "role", "columnheader", 1, "permission-header"], ["role", "rowgroup"], ["role", "row", 1, "permission-row"], ["role", "cell", 1, "role-name"], ["role", "cell", 1, "permission-cell"], [1, "entity-grid-card", 3, "class"], [1, "entity-grid-card"], [1, "grid-card-header"], ["aria-hidden", "true", 1, "fa-solid", "fa-table"], [1, "grid-card-title"], [1, "grid-card-description"], [1, "grid-card-footer"], [1, "access-label"], [1, "permission-count"], ["aria-hidden", "true", 1, "fa-solid", "fa-shield-alt", "empty-state-icon"], [1, "empty-text"], [1, "empty-subtext"], ["mjButton", "", "variant", "primary", "aria-label", "Refresh permissions data", 3, "click"]], template: function EntityPermissionsComponent_Template(rf, ctx) { if (rf & 1) {
|
|
601
|
+
i0.ɵɵelementStart(0, "mj-page-header-interior", 0)(1, "div", 1);
|
|
602
|
+
i0.ɵɵelement(2, "mj-stat-badge", 2);
|
|
689
603
|
i0.ɵɵelementEnd();
|
|
690
|
-
i0.ɵɵelementStart(
|
|
691
|
-
i0.ɵɵlistener("
|
|
692
|
-
i0.ɵɵelement(7, "i", 7);
|
|
693
|
-
i0.ɵɵelementEnd()();
|
|
694
|
-
i0.ɵɵelementStart(8, "button", 8);
|
|
695
|
-
i0.ɵɵlistener("click", function EntityPermissionsComponent_Template_button_click_8_listener() { return ctx.refreshData(); });
|
|
696
|
-
i0.ɵɵelement(9, "i", 9);
|
|
697
|
-
i0.ɵɵelementStart(10, "span", 10);
|
|
698
|
-
i0.ɵɵtext(11, "Refresh");
|
|
699
|
-
i0.ɵɵelementEnd()()();
|
|
700
|
-
i0.ɵɵelementStart(12, "div", 11)(13, "div", 12)(14, "div", 13);
|
|
701
|
-
i0.ɵɵelement(15, "i", 14);
|
|
604
|
+
i0.ɵɵelementStart(3, "div", 3)(4, "mj-page-search", 4);
|
|
605
|
+
i0.ɵɵlistener("ValueChange", function EntityPermissionsComponent_Template_mj_page_search_ValueChange_4_listener($event) { return ctx.updateFilter({ entitySearch: $event }); });
|
|
702
606
|
i0.ɵɵelementEnd();
|
|
703
|
-
i0.ɵɵelementStart(
|
|
704
|
-
i0.ɵɵ
|
|
607
|
+
i0.ɵɵelementStart(5, "div", 5)(6, "mj-filter-chip", 6);
|
|
608
|
+
i0.ɵɵlistener("Clicked", function EntityPermissionsComponent_Template_mj_filter_chip_Clicked_6_listener() { return ctx.onAccessLevelChange("all"); });
|
|
705
609
|
i0.ɵɵelementEnd();
|
|
706
|
-
i0.ɵɵelementStart(
|
|
707
|
-
i0.ɵɵ
|
|
708
|
-
i0.ɵɵelementEnd()()();
|
|
709
|
-
i0.ɵɵelementStart(21, "div", 18)(22, "div", 19);
|
|
710
|
-
i0.ɵɵelement(23, "i", 20);
|
|
610
|
+
i0.ɵɵelementStart(7, "mj-filter-chip", 7);
|
|
611
|
+
i0.ɵɵlistener("Clicked", function EntityPermissionsComponent_Template_mj_filter_chip_Clicked_7_listener() { return ctx.onAccessLevelChange("public"); });
|
|
711
612
|
i0.ɵɵelementEnd();
|
|
712
|
-
i0.ɵɵelementStart(
|
|
713
|
-
i0.ɵɵ
|
|
613
|
+
i0.ɵɵelementStart(8, "mj-filter-chip", 8);
|
|
614
|
+
i0.ɵɵlistener("Clicked", function EntityPermissionsComponent_Template_mj_filter_chip_Clicked_8_listener() { return ctx.onAccessLevelChange("restricted"); });
|
|
714
615
|
i0.ɵɵelementEnd();
|
|
715
|
-
i0.ɵɵelementStart(
|
|
716
|
-
i0.ɵɵ
|
|
616
|
+
i0.ɵɵelementStart(9, "mj-filter-chip", 9);
|
|
617
|
+
i0.ɵɵlistener("Clicked", function EntityPermissionsComponent_Template_mj_filter_chip_Clicked_9_listener() { return ctx.onAccessLevelChange("custom"); });
|
|
717
618
|
i0.ɵɵelementEnd()()();
|
|
718
|
-
i0.ɵɵelementStart(
|
|
719
|
-
i0.ɵɵ
|
|
720
|
-
i0.ɵɵ
|
|
721
|
-
i0.ɵɵ
|
|
722
|
-
i0.ɵɵtext(34);
|
|
723
|
-
i0.ɵɵelementEnd();
|
|
724
|
-
i0.ɵɵelementStart(35, "div", 17);
|
|
725
|
-
i0.ɵɵtext(36, "Restricted Entities");
|
|
726
|
-
i0.ɵɵelementEnd()()();
|
|
727
|
-
i0.ɵɵelementStart(37, "div", 26)(38, "div", 27);
|
|
728
|
-
i0.ɵɵelement(39, "i", 28);
|
|
729
|
-
i0.ɵɵelementEnd();
|
|
730
|
-
i0.ɵɵelementStart(40, "div", 15)(41, "div", 29);
|
|
731
|
-
i0.ɵɵtext(42);
|
|
732
|
-
i0.ɵɵelementEnd();
|
|
733
|
-
i0.ɵɵelementStart(43, "div", 17);
|
|
734
|
-
i0.ɵɵtext(44, "Total Permissions");
|
|
735
|
-
i0.ɵɵelementEnd()()()();
|
|
736
|
-
i0.ɵɵelementStart(45, "div", 30)(46, "div", 31)(47, "div", 32)(48, "div", 33);
|
|
737
|
-
i0.ɵɵelement(49, "i", 34);
|
|
738
|
-
i0.ɵɵelementStart(50, "input", 35);
|
|
739
|
-
i0.ɵɵlistener("input", function EntityPermissionsComponent_Template_input_input_50_listener($event) { return ctx.onSearchChange($event); });
|
|
619
|
+
i0.ɵɵelementStart(10, "div", 10)(11, "mj-filter-popover", 11);
|
|
620
|
+
i0.ɵɵlistener("ClearAllRequested", function EntityPermissionsComponent_Template_mj_filter_popover_ClearAllRequested_11_listener() { return ctx.onFilterPanelChange({ roleId: null }); });
|
|
621
|
+
i0.ɵɵelementStart(12, "mj-filter-panel", 12);
|
|
622
|
+
i0.ɵɵlistener("ValuesChange", function EntityPermissionsComponent_Template_mj_filter_panel_ValuesChange_12_listener($event) { return ctx.onFilterPanelChange($event); })("Reset", function EntityPermissionsComponent_Template_mj_filter_panel_Reset_12_listener() { return ctx.onFilterPanelChange({ roleId: null }); });
|
|
740
623
|
i0.ɵɵelementEnd()();
|
|
741
|
-
i0.ɵɵelementStart(
|
|
742
|
-
i0.ɵɵlistener("
|
|
743
|
-
i0.ɵɵelement(52, "i", 37);
|
|
744
|
-
i0.ɵɵelementStart(53, "span");
|
|
745
|
-
i0.ɵɵtext(54, "Filters");
|
|
746
|
-
i0.ɵɵelementEnd()()();
|
|
747
|
-
i0.ɵɵelementStart(55, "div", 38)(56, "label", 39);
|
|
748
|
-
i0.ɵɵtext(57, "Access Level");
|
|
749
|
-
i0.ɵɵelementEnd();
|
|
750
|
-
i0.ɵɵelementStart(58, "div", 40)(59, "button", 41);
|
|
751
|
-
i0.ɵɵlistener("click", function EntityPermissionsComponent_Template_button_click_59_listener() { return ctx.onAccessLevelChange("all"); });
|
|
752
|
-
i0.ɵɵtext(60, " All ");
|
|
624
|
+
i0.ɵɵelementStart(13, "mj-view-toggle", 13);
|
|
625
|
+
i0.ɵɵlistener("KeyChange", function EntityPermissionsComponent_Template_mj_view_toggle_KeyChange_13_listener($event) { return ctx.setViewMode($event); });
|
|
753
626
|
i0.ɵɵelementEnd();
|
|
754
|
-
i0.ɵɵelementStart(
|
|
755
|
-
i0.ɵɵlistener("
|
|
756
|
-
i0.ɵɵtext(62, " Public ");
|
|
757
|
-
i0.ɵɵelementEnd();
|
|
758
|
-
i0.ɵɵelementStart(63, "button", 43);
|
|
759
|
-
i0.ɵɵlistener("click", function EntityPermissionsComponent_Template_button_click_63_listener() { return ctx.onAccessLevelChange("restricted"); });
|
|
760
|
-
i0.ɵɵtext(64, " Restricted ");
|
|
761
|
-
i0.ɵɵelementEnd();
|
|
762
|
-
i0.ɵɵelementStart(65, "button", 44);
|
|
763
|
-
i0.ɵɵlistener("click", function EntityPermissionsComponent_Template_button_click_65_listener() { return ctx.onAccessLevelChange("custom"); });
|
|
764
|
-
i0.ɵɵtext(66, " Custom ");
|
|
627
|
+
i0.ɵɵelementStart(14, "mj-refresh-button", 14);
|
|
628
|
+
i0.ɵɵlistener("Clicked", function EntityPermissionsComponent_Template_mj_refresh_button_Clicked_14_listener() { return ctx.refreshData(); });
|
|
765
629
|
i0.ɵɵelementEnd()()();
|
|
766
|
-
i0.ɵɵelementStart(
|
|
767
|
-
i0.ɵɵ
|
|
768
|
-
i0.ɵɵ
|
|
769
|
-
i0.ɵɵ
|
|
770
|
-
i0.ɵɵlistener("change", function EntityPermissionsComponent_Template_select_change_70_listener($event) { return ctx.onRoleFilterChange($event); });
|
|
771
|
-
i0.ɵɵelementStart(71, "option", 47);
|
|
772
|
-
i0.ɵɵtext(72, "All Roles");
|
|
773
|
-
i0.ɵɵelementEnd();
|
|
774
|
-
i0.ɵɵrepeaterCreate(73, EntityPermissionsComponent_For_74_Template, 2, 2, "option", 48, _forTrack0);
|
|
775
|
-
i0.ɵɵelementEnd()()()()();
|
|
776
|
-
i0.ɵɵelementStart(75, "div", 49);
|
|
777
|
-
i0.ɵɵconditionalCreate(76, EntityPermissionsComponent_Conditional_76_Template, 2, 0, "div", 50);
|
|
778
|
-
i0.ɵɵconditionalCreate(77, EntityPermissionsComponent_Conditional_77_Template, 8, 1, "div", 51);
|
|
779
|
-
i0.ɵɵconditionalCreate(78, EntityPermissionsComponent_Conditional_78_Template, 4, 2, "div", 52);
|
|
630
|
+
i0.ɵɵelementStart(15, "mj-page-body-interior");
|
|
631
|
+
i0.ɵɵconditionalCreate(16, EntityPermissionsComponent_Conditional_16_Template, 2, 0, "div", 15);
|
|
632
|
+
i0.ɵɵconditionalCreate(17, EntityPermissionsComponent_Conditional_17_Template, 8, 1, "div", 16);
|
|
633
|
+
i0.ɵɵconditionalCreate(18, EntityPermissionsComponent_Conditional_18_Template, 3, 2);
|
|
780
634
|
i0.ɵɵelementEnd();
|
|
781
|
-
i0.ɵɵelementStart(
|
|
782
|
-
i0.ɵɵlistener("result", function
|
|
783
|
-
i0.ɵɵelementEnd();
|
|
784
|
-
i0.ɵɵconditionalCreate(80, EntityPermissionsComponent_Conditional_80_Template, 45, 15, "div", 54);
|
|
635
|
+
i0.ɵɵelementStart(19, "mj-permission-dialog", 17);
|
|
636
|
+
i0.ɵɵlistener("result", function EntityPermissionsComponent_Template_mj_permission_dialog_result_19_listener($event) { return ctx.onPermissionDialogResult($event); });
|
|
785
637
|
i0.ɵɵelementEnd();
|
|
786
638
|
} if (rf & 2) {
|
|
787
|
-
i0.ɵɵadvance(4);
|
|
788
|
-
i0.ɵɵproperty("variant", ctx.viewMode === "list" ? "primary" : "flat");
|
|
789
|
-
i0.ɵɵattribute("aria-label", "List view" + (ctx.viewMode === "list" ? " (active)" : ""))("aria-pressed", ctx.viewMode === "list");
|
|
790
639
|
i0.ɵɵadvance(2);
|
|
791
|
-
i0.ɵɵproperty("
|
|
792
|
-
i0.ɵɵattribute("aria-label", "Grid view" + (ctx.viewMode === "grid" ? " (active)" : ""))("aria-pressed", ctx.viewMode === "grid");
|
|
640
|
+
i0.ɵɵproperty("Count", ctx.filteredEntityAccess.length)("Total", ctx.entityAccess.length);
|
|
793
641
|
i0.ɵɵadvance(2);
|
|
794
|
-
i0.ɵɵproperty("
|
|
795
|
-
i0.ɵɵadvance();
|
|
796
|
-
i0.ɵɵclassProp("fa-spin", ctx.isLoading);
|
|
797
|
-
i0.ɵɵadvance(9);
|
|
798
|
-
i0.ɵɵtextInterpolate(ctx.stats.totalEntities);
|
|
799
|
-
i0.ɵɵadvance(8);
|
|
800
|
-
i0.ɵɵtextInterpolate(ctx.stats.publicEntities);
|
|
801
|
-
i0.ɵɵadvance(8);
|
|
802
|
-
i0.ɵɵtextInterpolate(ctx.stats.restrictedEntities);
|
|
803
|
-
i0.ɵɵadvance(8);
|
|
804
|
-
i0.ɵɵtextInterpolate(ctx.stats.totalPermissions);
|
|
805
|
-
i0.ɵɵadvance(8);
|
|
806
|
-
i0.ɵɵproperty("value", ctx.filters$.value.entitySearch);
|
|
807
|
-
i0.ɵɵadvance(9);
|
|
808
|
-
i0.ɵɵproperty("variant", ctx.filters$.value.accessLevel === "all" ? "primary" : "flat");
|
|
809
|
-
i0.ɵɵattribute("aria-pressed", ctx.filters$.value.accessLevel === "all");
|
|
642
|
+
i0.ɵɵproperty("Value", ctx.filters$.value.entitySearch);
|
|
810
643
|
i0.ɵɵadvance(2);
|
|
811
|
-
i0.ɵɵproperty("
|
|
812
|
-
i0.ɵɵ
|
|
644
|
+
i0.ɵɵproperty("Active", ctx.filters$.value.accessLevel === "all");
|
|
645
|
+
i0.ɵɵadvance();
|
|
646
|
+
i0.ɵɵproperty("Active", ctx.filters$.value.accessLevel === "public");
|
|
647
|
+
i0.ɵɵadvance();
|
|
648
|
+
i0.ɵɵproperty("Active", ctx.filters$.value.accessLevel === "restricted");
|
|
649
|
+
i0.ɵɵadvance();
|
|
650
|
+
i0.ɵɵproperty("Active", ctx.filters$.value.accessLevel === "custom");
|
|
813
651
|
i0.ɵɵadvance(2);
|
|
814
|
-
i0.ɵɵproperty("
|
|
815
|
-
i0.ɵɵ
|
|
652
|
+
i0.ɵɵproperty("ActiveCount", ctx.popoverActiveFilterCount)("ShowClearAll", ctx.popoverActiveFilterCount > 0);
|
|
653
|
+
i0.ɵɵadvance();
|
|
654
|
+
i0.ɵɵproperty("Fields", ctx.filterFields)("Values", ctx.filterValues);
|
|
655
|
+
i0.ɵɵadvance();
|
|
656
|
+
i0.ɵɵproperty("Options", ctx.viewToggleOptions)("ActiveKey", ctx.viewMode);
|
|
657
|
+
i0.ɵɵadvance();
|
|
658
|
+
i0.ɵɵproperty("Loading", ctx.isLoading);
|
|
816
659
|
i0.ɵɵadvance(2);
|
|
817
|
-
i0.ɵɵ
|
|
818
|
-
i0.ɵɵattribute("aria-pressed", ctx.filters$.value.accessLevel === "custom");
|
|
819
|
-
i0.ɵɵadvance(8);
|
|
820
|
-
i0.ɵɵrepeater(ctx.roles);
|
|
821
|
-
i0.ɵɵadvance(3);
|
|
822
|
-
i0.ɵɵconditional(ctx.isLoading ? 76 : -1);
|
|
660
|
+
i0.ɵɵconditional(ctx.isLoading ? 16 : -1);
|
|
823
661
|
i0.ɵɵadvance();
|
|
824
|
-
i0.ɵɵconditional(ctx.error && !ctx.isLoading ?
|
|
662
|
+
i0.ɵɵconditional(ctx.error && !ctx.isLoading ? 17 : -1);
|
|
825
663
|
i0.ɵɵadvance();
|
|
826
|
-
i0.ɵɵconditional(!ctx.isLoading && !ctx.error ?
|
|
664
|
+
i0.ɵɵconditional(!ctx.isLoading && !ctx.error ? 18 : -1);
|
|
827
665
|
i0.ɵɵadvance();
|
|
828
666
|
i0.ɵɵproperty("visible", ctx.showPermissionDialog)("data", ctx.permissionDialogData);
|
|
829
|
-
i0.ɵɵadvance();
|
|
830
|
-
i0.ɵɵconditional(ctx.showMobileFilters ? 80 : -1);
|
|
831
|
-
} }, dependencies: [i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i2.LoadingComponent, i3.MJButtonDirective, i4.PermissionDialogComponent], 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 .mj-btn-icon-mobile[_ngcontent-%COMP%] .btn-text[_ngcontent-%COMP%] {\n display: none;\n }\n\n .mj-btn-icon-mobile[_ngcontent-%COMP%] {\n padding: 0.5rem 0.75rem;\n min-height: 40px;\n gap: 0;\n border-radius: var(--mj-radius-full);\n }\n\n .mj-btn-icon-mobile[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1.125rem;\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.mj-filter-buttons[_ngcontent-%COMP%] .mj-btn[_ngcontent-%COMP%] {\n border-radius: var(--mj-radius-full);\n}\n\n.mj-filter-buttons[_ngcontent-%COMP%] .mj-btn-primary[_ngcontent-%COMP%] {\n background-color: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n box-shadow: var(--mj-shadow-sm);\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 .modal-footer[_ngcontent-%COMP%] .mj-btn[_ngcontent-%COMP%] {\n width: 100%;\n justify-content: center;\n }\n}\n\n\n\n\n\n.mj-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.75rem 1.5rem;\n font-size: 0.875rem;\n font-weight: 600;\n border: none;\n border-radius: var(--mj-radius-full);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n white-space: nowrap;\n min-height: 44px;\n}\n\n.mj-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.38;\n cursor: not-allowed;\n}\n\n\n\n.mj-btn-primary[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.mj-btn-primary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-brand-primary-light);\n box-shadow: var(--mj-shadow-md);\n}\n\n.mj-btn-primary[_ngcontent-%COMP%]:active:not(:disabled) {\n background: var(--mj-color-brand-400);\n transform: scale(0.98);\n}\n\n\n\n.mj-btn-secondary[_ngcontent-%COMP%] {\n background: var(--mj-bg-page);\n color: var(--mj-brand-primary);\n border: 1px solid var(--mj-color-neutral-400);\n}\n\n.mj-btn-secondary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.mj-btn-secondary[_ngcontent-%COMP%]:active:not(:disabled) {\n background: var(--mj-color-brand-700);\n border-color: var(--mj-color-brand-700);\n}\n\n\n\n.mj-btn-ghost[_ngcontent-%COMP%] {\n background: transparent;\n color: var(--mj-text-secondary);\n}\n\n.mj-btn-ghost[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.mj-btn-ghost[_ngcontent-%COMP%]:active:not(:disabled) {\n background: var(--mj-color-brand-700);\n}\n\n\n\n.mj-btn-danger[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.mj-btn-danger[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-color-error-300);\n box-shadow: var(--mj-shadow-md);\n}\n\n.mj-btn-danger[_ngcontent-%COMP%]:active:not(:disabled) {\n background: var(--mj-color-error-200);\n transform: scale(0.98);\n}\n\n\n\n.mj-btn-sm[_ngcontent-%COMP%] {\n padding: 0.5rem 0.875rem;\n font-size: 0.8125rem;\n min-height: 36px;\n}\n\n\n\n.mj-btn-icon-only[_ngcontent-%COMP%] {\n padding: 0.625rem;\n min-width: 44px;\n min-height: 44px;\n}\n\n\n\n.mj-btn-icon-mobile[_ngcontent-%COMP%] {\n gap: 0.5rem;\n}\n\n.mj-btn-icon-mobile[_ngcontent-%COMP%] .btn-text[_ngcontent-%COMP%] {\n display: inline;\n}\n\n\n\n.mj-btn-ghost.mj-btn-danger[_ngcontent-%COMP%] {\n background: transparent;\n color: var(--mj-status-error);\n box-shadow: none;\n}\n\n.mj-btn-ghost.mj-btn-danger[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\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.mj-btn[_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.entity-permissions-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n position: relative;\n margin: 0 auto;\n width: 100%;\n background: var(--mj-bg-page);\n}\n\n\n\n\n\n.sticky-header[_ngcontent-%COMP%] {\n padding: 1rem;\n}\n\n@media (min-width: 640px) {\n .sticky-header[_ngcontent-%COMP%] {\n padding: 1.25rem;\n }\n}\n\n@media (min-width: 768px) {\n .sticky-header[_ngcontent-%COMP%] {\n padding: 1.5rem;\n }\n}\n\n@media (min-width: 1024px) {\n .sticky-header[_ngcontent-%COMP%] {\n padding: 1.5rem 2rem;\n }\n}\n\n\n\n\n\n@media (min-width: 640px) {\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 1.25rem;\n }\n}\n\n@media (min-width: 768px) {\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 1.5rem;\n }\n}\n\n@media (min-width: 1024px) {\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 1.5rem 2rem;\n }\n}\n\n\n\n\n\n.mj-btn[_ngcontent-%COMP%] {\n min-width: 44px;\n text-decoration: none;\n}\n\n\n\n.mj-btn-ghost[_ngcontent-%COMP%] {\n padding: 0.75rem 1rem;\n}\n\n\n\n.mj-btn-sm[_ngcontent-%COMP%] {\n min-width: 36px;\n}\n\n\n\n.mj-btn-icon-mobile[_ngcontent-%COMP%] {\n padding: 0.75rem 1.5rem;\n}\n\n@media (min-width: 640px) {\n .mj-btn-icon-mobile[_ngcontent-%COMP%] {\n padding: 0.75rem 1.5rem;\n }\n}\n\n@media (max-width: 639px) {\n .mj-btn-icon-mobile[_ngcontent-%COMP%] .btn-text[_ngcontent-%COMP%] {\n display: none;\n }\n\n .mj-btn-icon-mobile[_ngcontent-%COMP%] {\n padding: 0.625rem;\n gap: 0;\n }\n}\n\n\n\n\n\n.action-buttons[_ngcontent-%COMP%] {\n justify-content: space-between;\n margin-bottom: 1rem;\n flex-wrap: wrap;\n align-items: center;\n padding: 0;\n background: transparent;\n}\n\n@media (min-width: 768px) {\n .action-buttons[_ngcontent-%COMP%] {\n margin-bottom: 1.25rem;\n }\n}\n\n\n\n\n\n@media (max-width: 639px) {\n .sticky-header[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n padding: 0.75rem 1rem;\n }\n\n .action-buttons[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: 0.5rem;\n margin-bottom: 0;\n order: 1;\n width: 100%;\n }\n\n .mj-view-toggle[_ngcontent-%COMP%] {\n flex-shrink: 0;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n padding: 3px;\n gap: 2px;\n }\n\n .mj-view-toggle[_ngcontent-%COMP%] .mj-btn[_ngcontent-%COMP%] {\n padding: 0.5rem 0.625rem;\n min-width: 36px;\n min-height: 36px;\n border-radius: var(--mj-radius-md);\n }\n\n .mj-view-toggle[_ngcontent-%COMP%] .mj-btn.mj-btn-primary[_ngcontent-%COMP%] {\n box-shadow: none;\n }\n\n .action-buttons[_ngcontent-%COMP%] > .mj-btn-secondary[_ngcontent-%COMP%] {\n flex-shrink: 0;\n padding: 0.5rem 0.625rem;\n min-width: 36px;\n min-height: 36px;\n border-radius: var(--mj-radius-lg);\n }\n\n .filters-section[_ngcontent-%COMP%] {\n order: 2;\n width: 100%;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n padding: 0.75rem;\n margin-bottom: 0.5rem;\n border-radius: var(--mj-radius-lg);\n }\n\n .filters-row[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 0.5rem;\n flex-wrap: nowrap;\n }\n\n \n\n .mobile-search-container[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.5rem;\n align-items: center;\n flex: 1;\n min-width: 0;\n }\n\n .mj-search[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n }\n\n .mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%] {\n padding: 0.75rem 1rem 0.75rem 2.5rem;\n min-height: 44px;\n font-size: 1rem;\n border-width: 1.5px;\n border-radius: var(--mj-radius-full);\n box-sizing: border-box;\n width: 100%;\n }\n\n .mj-search[_ngcontent-%COMP%] .mj-search-icon[_ngcontent-%COMP%] {\n left: 0.875rem;\n font-size: 1rem;\n }\n\n .mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%]::placeholder {\n font-size: 0.875rem;\n }\n\n \n\n .mj-filter-group[_ngcontent-%COMP%] {\n display: none !important;\n }\n\n .mj-filter-label[_ngcontent-%COMP%] {\n display: none;\n }\n\n .mj-filter-select[_ngcontent-%COMP%] {\n display: none;\n }\n}\n\n\n\n\n\n.mj-view-toggle[_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.mj-view-toggle[_ngcontent-%COMP%] .mj-btn[_ngcontent-%COMP%] {\n padding: 0.5rem 1rem;\n border-radius: var(--mj-radius-full);\n background: transparent;\n color: var(--mj-text-secondary);\n border: none;\n font-size: 0.875rem;\n font-weight: 500;\n}\n\n.mj-view-toggle[_ngcontent-%COMP%] .mj-btn[_ngcontent-%COMP%]:hover:not(:disabled):not(.mj-btn-primary) {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.mj-view-toggle[_ngcontent-%COMP%] .mj-btn.mj-btn-primary[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n\n\n\n\n.mj-grid-4[_ngcontent-%COMP%] {\n margin-bottom: 1rem;\n width: 100%;\n padding: 0;\n background: transparent;\n}\n\n@media (max-width: 639px) {\n .mj-grid-4[_ngcontent-%COMP%] {\n display: none !important;\n }\n}\n\n@media (min-width: 640px) {\n .mj-grid-4[_ngcontent-%COMP%] {\n gap: 1rem;\n }\n}\n\n@media (min-width: 768px) {\n .mj-grid-4[_ngcontent-%COMP%] {\n gap: 1.25rem;\n margin-bottom: 1.25rem;\n }\n}\n\n\n\n.mj-card[_ngcontent-%COMP%] {\n min-width: 0;\n box-shadow: none;\n}\n\n\n\n@media (min-width: 768px) {\n .stat-content[_ngcontent-%COMP%] .stat-value[_ngcontent-%COMP%] {\n font-size: 1.875rem;\n }\n}\n\n\n\n.stat-icon-public[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-status-success);\n}\n\n.stat-icon-restricted[_ngcontent-%COMP%] {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n}\n\n.stat-icon-permissions[_ngcontent-%COMP%] {\n background: var(--mj-color-warning-50);\n color: var(--mj-color-warning-700);\n}\n\n\n\n\n\n.filters-section[_ngcontent-%COMP%] {\n border-radius: var(--mj-radius-xl);\n padding: 1rem;\n margin-bottom: 0;\n width: 100%;\n margin-left: 0;\n margin-right: 0;\n}\n\n@media (min-width: 768px) {\n .filters-section[_ngcontent-%COMP%] {\n padding: 1rem 1.25rem;\n }\n}\n\n@media (min-width: 1024px) {\n .filters-section[_ngcontent-%COMP%] {\n padding: 1.25rem 1.5rem;\n }\n}\n\n@media (max-width: 639px) {\n .filters-row[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: stretch;\n gap: 0.75rem;\n }\n}\n\n@media (min-width: 1024px) {\n .filters-row[_ngcontent-%COMP%] {\n gap: 1.5rem;\n }\n}\n\n\n\n@media (min-width: 1024px) {\n .mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%] {\n padding: 1rem 1.25rem 1rem 3rem;\n }\n\n .mj-search[_ngcontent-%COMP%] .mj-search-icon[_ngcontent-%COMP%] {\n left: 1.125rem;\n font-size: 1.125rem;\n }\n}\n\n\n\n.mj-filter-buttons[_ngcontent-%COMP%] {\n flex-wrap: wrap;\n}\n\n.mj-filter-buttons[_ngcontent-%COMP%] .mj-btn[_ngcontent-%COMP%] {\n padding: 0.5rem 1rem;\n border-radius: var(--mj-radius-full);\n font-size: 0.875rem;\n font-weight: 500;\n background: transparent;\n color: var(--mj-text-secondary);\n border: none;\n}\n\n.mj-filter-buttons[_ngcontent-%COMP%] .mj-btn[_ngcontent-%COMP%]:hover:not(:disabled):not(.mj-btn-primary) {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.mj-filter-buttons[_ngcontent-%COMP%] .mj-btn.mj-btn-primary[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n@media (max-width: 639px) {\n .mj-filter-buttons[_ngcontent-%COMP%] {\n flex-direction: row;\n flex-wrap: wrap;\n }\n\n .mj-filter-buttons[_ngcontent-%COMP%] .mj-btn[_ngcontent-%COMP%] {\n flex: 1;\n min-width: calc(50% - 4px);\n padding: 0.5rem 0.75rem;\n font-size: 0.75rem;\n }\n}\n\n\n\n.mj-filter-label[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n line-height: 1.2;\n}\n\n\n\n.mj-filter-select[_ngcontent-%COMP%] {\n padding: 0.5rem 1rem;\n border: 2px solid var(--mj-border-default);\n border-radius: var(--mj-radius-full);\n font-size: 0.875rem;\n font-weight: 500;\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n height: 44px;\n min-width: 150px;\n box-sizing: border-box;\n}\n\n.mj-filter-select[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n}\n\n.mj-filter-select[_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}\n\n@media (max-width: 639px) {\n .mj-filter-select[_ngcontent-%COMP%] {\n width: 100%;\n }\n}\n\n\n\n.mobile-search-container[_ngcontent-%COMP%] {\n display: contents;\n}\n\n\n\n.filter-button[_ngcontent-%COMP%] .filter-badge[_ngcontent-%COMP%] {\n position: absolute;\n top: -4px;\n right: -4px;\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n font-size: 0.625rem;\n font-weight: 700;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0 4px;\n}\n\n\n\n.loading-container[_ngcontent-%COMP%] {\n min-height: 200px;\n padding: 3rem 1.5rem;\n flex-direction: row;\n}\n\n\n\n\n\n.content-area[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n box-shadow: var(--mj-shadow-md);\n padding: 1.25rem;\n}\n\n@media (min-width: 1024px) {\n .content-area[_ngcontent-%COMP%] {\n padding: 1.75rem;\n }\n}\n\n\n\n\n\n.entities-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n overflow: visible;\n}\n\n@media (min-width: 768px) {\n .entities-list[_ngcontent-%COMP%] {\n gap: 1rem;\n }\n}\n\n\n\n\n\n.entity-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.entity-card[_ngcontent-%COMP%]:hover {\n box-shadow: var(--mj-shadow-md);\n border-color: var(--mj-brand-primary);\n}\n\n.entity-card.expanded[_ngcontent-%COMP%] {\n box-shadow: var(--mj-shadow-lg);\n border-color: var(--mj-brand-primary);\n}\n\n.entity-card.expanded[_ngcontent-%COMP%] .expand-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n transform: rotate(180deg);\n}\n\n\n\n.entity-header[_ngcontent-%COMP%] {\n padding: 1rem;\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 gap: 0.75rem;\n}\n\n.entity-header[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n@media (min-width: 768px) {\n .entity-header[_ngcontent-%COMP%] {\n padding: 1.25rem 1.5rem;\n }\n}\n\n\n\n.entity-info[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n flex: 1;\n min-width: 0;\n}\n\n@media (min-width: 768px) {\n .entity-info[_ngcontent-%COMP%] {\n gap: 1rem;\n }\n}\n\n\n\n.entity-icon-wrapper[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\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.25rem;\n flex-shrink: 0;\n}\n\n@media (min-width: 768px) {\n .entity-icon-wrapper[_ngcontent-%COMP%] {\n width: 52px;\n height: 52px;\n font-size: 1.375rem;\n }\n}\n\n\n\n.entity-details[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.entity-details[_ngcontent-%COMP%] .entity-name[_ngcontent-%COMP%] {\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.25rem 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n@media (min-width: 768px) {\n .entity-details[_ngcontent-%COMP%] .entity-name[_ngcontent-%COMP%] {\n font-size: 1.125rem;\n }\n}\n\n.entity-details[_ngcontent-%COMP%] .entity-description[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n margin: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n@media (min-width: 768px) {\n .entity-details[_ngcontent-%COMP%] .entity-description[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n }\n}\n\n\n\n.entity-meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n\n@media (min-width: 768px) {\n .entity-meta[_ngcontent-%COMP%] {\n gap: 0.75rem;\n }\n}\n\n\n\n\n\n.access-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 white-space: nowrap;\n}\n\n@media (min-width: 768px) {\n .access-badge[_ngcontent-%COMP%] {\n padding: 0.5rem 1rem;\n font-size: 0.8125rem;\n }\n}\n\n.access-badge.access-public[_ngcontent-%COMP%] {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.access-badge.access-restricted[_ngcontent-%COMP%] {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n border: 1px solid var(--mj-status-error-border);\n}\n\n.access-badge.access-custom[_ngcontent-%COMP%] {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n}\n\n\n\n.entity-actions[_ngcontent-%COMP%] {\n display: none;\n}\n\n@media (min-width: 640px) {\n .entity-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.25rem;\n }\n}\n\n@media (max-width: 639px) {\n .entity-meta[_ngcontent-%COMP%] > .access-badge[_ngcontent-%COMP%] {\n display: none;\n }\n}\n\n\n\n.expand-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n}\n\n.expand-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n\n\n\n\n.entity-content[_ngcontent-%COMP%] {\n padding: 1.25rem;\n background: var(--mj-bg-surface);\n border-top: 1px solid var(--mj-border-default);\n animation: _ngcontent-%COMP%_slideDown 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@media (min-width: 768px) {\n .entity-content[_ngcontent-%COMP%] {\n padding: 1.5rem;\n }\n}\n\n@media (min-width: 1024px) {\n .entity-content[_ngcontent-%COMP%] {\n padding: 1.75rem;\n }\n}\n\n\n\n\n\n@media (max-width: 639px) {\n .mobile-actions-bar[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.75rem;\n margin: -1.25rem -1.25rem 1rem -1.25rem;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\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[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n }\n\n .mobile-action-buttons[_ngcontent-%COMP%] .btn-label[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n }\n}\n\n\n\n\n\n.permissions-grid[_ngcontent-%COMP%] {\n width: 100%;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n border: 1px solid var(--mj-border-default);\n border-collapse: collapse;\n}\n\n\n\n.permissions-header[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 2fr 1fr 1fr 1fr 1fr;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n background: var(--mj-bg-surface-sunken);\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n@media (min-width: 768px) {\n .permissions-header[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] {\n padding: 1rem 1.25rem;\n gap: 0.75rem;\n }\n}\n\n@media (max-width: 639px) {\n .permissions-header[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] {\n grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr;\n padding: 0.625rem 0.75rem;\n font-size: 0.75rem;\n }\n}\n\n\n\n.permission-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 2fr 1fr 1fr 1fr 1fr;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--mj-border-default);\n transition: background-color 0.2s ease;\n}\n\n.permission-row[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface);\n}\n\n.permission-row[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n@media (min-width: 768px) {\n .permission-row[_ngcontent-%COMP%] {\n padding: 1rem 1.25rem;\n gap: 0.75rem;\n }\n}\n\n@media (max-width: 639px) {\n .permission-row[_ngcontent-%COMP%] {\n grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr;\n padding: 0.625rem 0.75rem;\n }\n}\n\n.role-header[_ngcontent-%COMP%], \n.permission-header[_ngcontent-%COMP%] {\n text-align: left;\n}\n\n.permission-header[_ngcontent-%COMP%] {\n text-align: center;\n}\n\n.role-name[_ngcontent-%COMP%] {\n font-weight: 500;\n color: var(--mj-text-primary);\n font-size: 0.875rem;\n display: flex;\n align-items: center;\n}\n\n@media (max-width: 639px) {\n .role-name[_ngcontent-%COMP%] {\n font-size: 0.75rem;\n }\n}\n\n.permission-cell[_ngcontent-%COMP%] {\n text-align: center;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n\n\n.text-success[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n}\n\n.text-muted[_ngcontent-%COMP%] {\n color: var(--mj-color-neutral-400);\n opacity: 0.6;\n}\n\n.no-permissions[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n font-size: 0.875rem;\n margin: 0;\n padding: 1rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n text-align: center;\n}\n\n\n\n\n\n.entities-grid[_ngcontent-%COMP%] {\n display: grid;\n gap: 0.75rem;\n grid-template-columns: 1fr;\n}\n\n@media (min-width: 640px) {\n .entities-grid[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n gap: 1rem;\n }\n}\n\n@media (min-width: 768px) {\n .entities-grid[_ngcontent-%COMP%] {\n gap: 1.25rem;\n }\n}\n\n@media (min-width: 1024px) {\n .entities-grid[_ngcontent-%COMP%] {\n grid-template-columns: repeat(3, 1fr);\n gap: 1.5rem;\n }\n}\n\n\n\n.entity-grid-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 padding: 1.25rem;\n position: relative;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.entity-grid-card[_ngcontent-%COMP%]:hover {\n box-shadow: var(--mj-shadow-md);\n border-color: var(--mj-brand-primary);\n}\n\n.entity-grid-card.access-public[_ngcontent-%COMP%] {\n border-left: 4px solid var(--mj-status-success);\n}\n\n.entity-grid-card.access-restricted[_ngcontent-%COMP%] {\n border-left: 4px solid var(--mj-status-error);\n}\n\n.entity-grid-card.access-custom[_ngcontent-%COMP%] {\n border-left: 4px solid var(--mj-color-warning-500);\n}\n\n@media (min-width: 768px) {\n .entity-grid-card[_ngcontent-%COMP%] {\n padding: 1.5rem;\n }\n}\n\n\n\n.grid-card-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 0.75rem;\n font-size: 1.5rem;\n color: var(--mj-brand-primary);\n}\n\n@media (min-width: 768px) {\n .grid-card-header[_ngcontent-%COMP%] {\n margin-bottom: 1rem;\n }\n}\n\n\n\n.grid-card-title[_ngcontent-%COMP%] {\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.5rem 0;\n}\n\n@media (min-width: 768px) {\n .grid-card-title[_ngcontent-%COMP%] {\n font-size: 1.125rem;\n }\n}\n\n\n\n.grid-card-description[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n margin: 0 0 0.75rem 0;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n@media (min-width: 768px) {\n .grid-card-description[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n margin: 0 0 1rem 0;\n }\n}\n\n\n\n.grid-card-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n}\n\n\n\n.access-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-weight: 500;\n}\n\n.permission-count[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n}\n\n\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1rem;\n}\n\n@media (min-width: 768px) {\n .empty-state[_ngcontent-%COMP%] {\n padding: 5rem 2.5rem;\n }\n}\n\n.empty-state[_ngcontent-%COMP%] .empty-state-icon[_ngcontent-%COMP%] {\n font-size: 4rem;\n color: var(--mj-border-default);\n margin-bottom: 0.5rem;\n}\n\n\n\n.error-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n min-height: 200px;\n}\n\n@media (min-width: 768px) {\n .error-container[_ngcontent-%COMP%] {\n padding: 5rem 2.5rem;\n }\n}\n\n.error-container[_ngcontent-%COMP%] .error-icon[_ngcontent-%COMP%] {\n margin-bottom: 0.5rem;\n}\n\n\n\n@keyframes _ngcontent-%COMP%_slideDown {\n from {\n opacity: 0;\n max-height: 0;\n }\n to {\n opacity: 1;\n max-height: 800px;\n }\n}\n\n\n\n\n\n@media (max-width: 374px) {\n .filters-section[_ngcontent-%COMP%] {\n padding: 0.5rem;\n margin-bottom: 0.375rem;\n }\n\n .filters-row[_ngcontent-%COMP%] {\n gap: 0.375rem;\n }\n\n .mobile-search-container[_ngcontent-%COMP%] {\n gap: 0.375rem;\n }\n\n .mj-search[_ngcontent-%COMP%] .mj-search-input[_ngcontent-%COMP%] {\n padding: 0.625rem 0.75rem 0.625rem 2.25rem;\n font-size: 0.875rem;\n }\n\n .mj-search[_ngcontent-%COMP%] .mj-search-icon[_ngcontent-%COMP%] {\n left: 0.75rem;\n font-size: 0.875rem;\n }\n\n .filter-button[_ngcontent-%COMP%] {\n padding: 0.625rem;\n min-width: 40px;\n flex-shrink: 0;\n }\n\n .filter-button[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n display: none;\n }\n\n .filter-modal-header[_ngcontent-%COMP%] {\n padding: 1rem 1.25rem;\n }\n\n .filter-modal-body[_ngcontent-%COMP%] {\n padding: 1.25rem;\n }\n\n .filter-modal-footer[_ngcontent-%COMP%] {\n padding: 1rem 1.25rem;\n }\n}"] });
|
|
667
|
+
} }, dependencies: [i1.LoadingComponent, i2.MJButtonDirective, i2.MJPageHeaderInteriorComponent, i2.MJPageBodyInteriorComponent, i2.MJPageSearchComponent, i2.MJFilterPopoverComponent, i2.MJFilterPanelComponent, i2.MJFilterChipComponent, i2.MJRefreshButtonComponent, i2.MJStatBadgeComponent, i2.MJViewToggleComponent, i3.PermissionDialogComponent], 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@media (min-width: 640px) {\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 1.25rem;\n }\n}\n\n@media (min-width: 768px) {\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 1.5rem;\n }\n}\n\n@media (min-width: 1024px) {\n .scrollable-content[_ngcontent-%COMP%] {\n padding: 1.5rem 2rem;\n }\n}\n\n@media (max-width: 639px) {\n .mj-grid-4[_ngcontent-%COMP%] {\n display: none !important;\n }\n}\n\n\n\n\n\n.mj-grid-4[_ngcontent-%COMP%] {\n margin-bottom: 1rem;\n width: 100%;\n padding: 0;\n background: transparent;\n}\n\n@media (max-width: 639px) {\n .mj-grid-4[_ngcontent-%COMP%] {\n display: none !important;\n }\n}\n\n@media (min-width: 640px) {\n .mj-grid-4[_ngcontent-%COMP%] {\n gap: 1rem;\n }\n}\n\n@media (min-width: 768px) {\n .mj-grid-4[_ngcontent-%COMP%] {\n gap: 1.25rem;\n margin-bottom: 1.25rem;\n }\n}\n\n\n\n.mj-card[_ngcontent-%COMP%] {\n min-width: 0;\n box-shadow: none;\n}\n\n\n\n@media (min-width: 768px) {\n .stat-content[_ngcontent-%COMP%] .stat-value[_ngcontent-%COMP%] {\n font-size: 1.875rem;\n }\n}\n\n\n\n.stat-icon-public[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-status-success);\n}\n\n.stat-icon-restricted[_ngcontent-%COMP%] {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n}\n\n.stat-icon-permissions[_ngcontent-%COMP%] {\n background: var(--mj-color-warning-50);\n color: var(--mj-color-warning-700);\n}\n\n\n\n.loading-container[_ngcontent-%COMP%] {\n min-height: 200px;\n padding: 3rem 1.5rem;\n flex-direction: row;\n}\n\n\n\n\n\n.entities-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n overflow: visible;\n}\n\n@media (min-width: 768px) {\n .entities-list[_ngcontent-%COMP%] {\n gap: 1rem;\n }\n}\n\n\n\n\n\n.entity-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.entity-card[_ngcontent-%COMP%]:hover {\n box-shadow: var(--mj-shadow-md);\n border-color: var(--mj-brand-primary);\n}\n\n.entity-card.expanded[_ngcontent-%COMP%] {\n box-shadow: var(--mj-shadow-lg);\n border-color: var(--mj-brand-primary);\n}\n\n.entity-card.expanded[_ngcontent-%COMP%] .expand-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n transform: rotate(180deg);\n}\n\n\n\n.entity-header[_ngcontent-%COMP%] {\n padding: 1rem;\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 gap: 0.75rem;\n}\n\n.entity-header[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n@media (min-width: 768px) {\n .entity-header[_ngcontent-%COMP%] {\n padding: 1.25rem 1.5rem;\n }\n}\n\n\n\n.entity-info[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n flex: 1;\n min-width: 0;\n}\n\n@media (min-width: 768px) {\n .entity-info[_ngcontent-%COMP%] {\n gap: 1rem;\n }\n}\n\n\n\n.entity-icon-wrapper[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\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.25rem;\n flex-shrink: 0;\n}\n\n@media (min-width: 768px) {\n .entity-icon-wrapper[_ngcontent-%COMP%] {\n width: 52px;\n height: 52px;\n font-size: 1.375rem;\n }\n}\n\n\n\n.entity-details[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.entity-details[_ngcontent-%COMP%] .entity-name[_ngcontent-%COMP%] {\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.25rem 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n@media (min-width: 768px) {\n .entity-details[_ngcontent-%COMP%] .entity-name[_ngcontent-%COMP%] {\n font-size: 1.125rem;\n }\n}\n\n.entity-details[_ngcontent-%COMP%] .entity-description[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n margin: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n@media (min-width: 768px) {\n .entity-details[_ngcontent-%COMP%] .entity-description[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n }\n}\n\n\n\n.entity-meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n\n@media (min-width: 768px) {\n .entity-meta[_ngcontent-%COMP%] {\n gap: 0.75rem;\n }\n}\n\n\n\n\n\n.access-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 white-space: nowrap;\n}\n\n@media (min-width: 768px) {\n .access-badge[_ngcontent-%COMP%] {\n padding: 0.5rem 1rem;\n font-size: 0.8125rem;\n }\n}\n\n.access-badge.access-public[_ngcontent-%COMP%] {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.access-badge.access-restricted[_ngcontent-%COMP%] {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n border: 1px solid var(--mj-status-error-border);\n}\n\n.access-badge.access-custom[_ngcontent-%COMP%] {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n}\n\n\n\n.entity-actions[_ngcontent-%COMP%] {\n display: none;\n}\n\n@media (min-width: 640px) {\n .entity-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.25rem;\n }\n}\n\n@media (max-width: 639px) {\n .entity-meta[_ngcontent-%COMP%] > .access-badge[_ngcontent-%COMP%] {\n display: none;\n }\n}\n\n\n\n.expand-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n}\n\n.expand-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n\n\n\n\n.entity-content[_ngcontent-%COMP%] {\n padding: 1.25rem;\n background: var(--mj-bg-surface);\n border-top: 1px solid var(--mj-border-default);\n animation: _ngcontent-%COMP%_slideDown 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@media (min-width: 768px) {\n .entity-content[_ngcontent-%COMP%] {\n padding: 1.5rem;\n }\n}\n\n@media (min-width: 1024px) {\n .entity-content[_ngcontent-%COMP%] {\n padding: 1.75rem;\n }\n}\n\n\n\n\n\n@media (max-width: 639px) {\n .mobile-actions-bar[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.75rem;\n margin: -1.25rem -1.25rem 1rem -1.25rem;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .mobile-action-buttons[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.5rem;\n }\n\n .mobile-action-buttons[_ngcontent-%COMP%] .btn-label[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n }\n}\n\n\n\n\n\n.permissions-grid[_ngcontent-%COMP%] {\n width: 100%;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n border: 1px solid var(--mj-border-default);\n border-collapse: collapse;\n}\n\n\n\n.permissions-header[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 2fr 1fr 1fr 1fr 1fr;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n background: var(--mj-bg-surface-sunken);\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n@media (min-width: 768px) {\n .permissions-header[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] {\n padding: 1rem 1.25rem;\n gap: 0.75rem;\n }\n}\n\n@media (max-width: 639px) {\n .permissions-header[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] {\n grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr;\n padding: 0.625rem 0.75rem;\n font-size: 0.75rem;\n }\n}\n\n\n\n.permission-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 2fr 1fr 1fr 1fr 1fr;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--mj-border-default);\n transition: background-color 0.2s ease;\n}\n\n.permission-row[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface);\n}\n\n.permission-row[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n@media (min-width: 768px) {\n .permission-row[_ngcontent-%COMP%] {\n padding: 1rem 1.25rem;\n gap: 0.75rem;\n }\n}\n\n@media (max-width: 639px) {\n .permission-row[_ngcontent-%COMP%] {\n grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr;\n padding: 0.625rem 0.75rem;\n }\n}\n\n.role-header[_ngcontent-%COMP%], \n.permission-header[_ngcontent-%COMP%] {\n text-align: left;\n}\n\n.permission-header[_ngcontent-%COMP%] {\n text-align: center;\n}\n\n.role-name[_ngcontent-%COMP%] {\n font-weight: 500;\n color: var(--mj-text-primary);\n font-size: 0.875rem;\n display: flex;\n align-items: center;\n}\n\n@media (max-width: 639px) {\n .role-name[_ngcontent-%COMP%] {\n font-size: 0.75rem;\n }\n}\n\n.permission-cell[_ngcontent-%COMP%] {\n text-align: center;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n\n\n.text-success[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n}\n\n.text-muted[_ngcontent-%COMP%] {\n color: var(--mj-color-neutral-400);\n opacity: 0.6;\n}\n\n.no-permissions[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n font-size: 0.875rem;\n margin: 0;\n padding: 1rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n text-align: center;\n}\n\n\n\n\n\n.entities-grid[_ngcontent-%COMP%] {\n display: grid;\n gap: 0.75rem;\n grid-template-columns: 1fr;\n}\n\n@media (min-width: 640px) {\n .entities-grid[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n gap: 1rem;\n }\n}\n\n@media (min-width: 768px) {\n .entities-grid[_ngcontent-%COMP%] {\n gap: 1.25rem;\n }\n}\n\n@media (min-width: 1024px) {\n .entities-grid[_ngcontent-%COMP%] {\n grid-template-columns: repeat(3, 1fr);\n gap: 1.5rem;\n }\n}\n\n\n\n.entity-grid-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 padding: 1.25rem;\n position: relative;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.entity-grid-card[_ngcontent-%COMP%]:hover {\n box-shadow: var(--mj-shadow-md);\n border-color: var(--mj-brand-primary);\n}\n\n.entity-grid-card.access-public[_ngcontent-%COMP%] {\n border-left: 4px solid var(--mj-status-success);\n}\n\n.entity-grid-card.access-restricted[_ngcontent-%COMP%] {\n border-left: 4px solid var(--mj-status-error);\n}\n\n.entity-grid-card.access-custom[_ngcontent-%COMP%] {\n border-left: 4px solid var(--mj-color-warning-500);\n}\n\n@media (min-width: 768px) {\n .entity-grid-card[_ngcontent-%COMP%] {\n padding: 1.5rem;\n }\n}\n\n\n\n.grid-card-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 0.75rem;\n font-size: 1.5rem;\n color: var(--mj-brand-primary);\n}\n\n@media (min-width: 768px) {\n .grid-card-header[_ngcontent-%COMP%] {\n margin-bottom: 1rem;\n }\n}\n\n\n\n.grid-card-title[_ngcontent-%COMP%] {\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.5rem 0;\n}\n\n@media (min-width: 768px) {\n .grid-card-title[_ngcontent-%COMP%] {\n font-size: 1.125rem;\n }\n}\n\n\n\n.grid-card-description[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n margin: 0 0 0.75rem 0;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n@media (min-width: 768px) {\n .grid-card-description[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n margin: 0 0 1rem 0;\n }\n}\n\n\n\n.grid-card-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n}\n\n\n\n.access-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-weight: 500;\n}\n\n.permission-count[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n}\n\n\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1rem;\n}\n\n@media (min-width: 768px) {\n .empty-state[_ngcontent-%COMP%] {\n padding: 5rem 2.5rem;\n }\n}\n\n.empty-state[_ngcontent-%COMP%] .empty-state-icon[_ngcontent-%COMP%] {\n font-size: 4rem;\n color: var(--mj-border-default);\n margin-bottom: 0.5rem;\n}\n\n\n\n.error-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n min-height: 200px;\n}\n\n@media (min-width: 768px) {\n .error-container[_ngcontent-%COMP%] {\n padding: 5rem 2.5rem;\n }\n}\n\n.error-container[_ngcontent-%COMP%] .error-icon[_ngcontent-%COMP%] {\n margin-bottom: 0.5rem;\n}\n\n\n\n@keyframes _ngcontent-%COMP%_slideDown {\n from {\n opacity: 0;\n max-height: 0;\n }\n to {\n opacity: 1;\n max-height: 800px;\n }\n}"] });
|
|
832
668
|
};
|
|
833
669
|
EntityPermissionsComponent = __decorate([
|
|
834
670
|
RegisterClass(BaseDashboard, 'EntityPermissions')
|
|
@@ -836,7 +672,7 @@ EntityPermissionsComponent = __decorate([
|
|
|
836
672
|
export { EntityPermissionsComponent };
|
|
837
673
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(EntityPermissionsComponent, [{
|
|
838
674
|
type: Component,
|
|
839
|
-
args: [{ standalone: false, selector: 'mj-entity-permissions', template: "<div class=\"entity-permissions-container\">\n <!-- Sticky Header Section - Contains controls that should always be visible -->\n <div class=\"sticky-header\">\n <!-- Action Buttons -->\n <div class=\"action-buttons\" role=\"toolbar\" aria-label=\"Entity permissions actions\">\n <div class=\"mj-view-toggle\">\n <button mjButton\n [variant]=\"viewMode === 'list' ? 'primary' : 'flat'\"\n (click)=\"setViewMode('list')\"\n (keydown.enter)=\"setViewMode('list')\"\n (keydown.space)=\"setViewMode('list'); $event.preventDefault()\"\n [attr.aria-label]=\"'List view' + (viewMode === 'list' ? ' (active)' : '')\"\n [attr.aria-pressed]=\"viewMode === 'list'\"\n role=\"button\"\n tabindex=\"0\"\n title=\"List View\"\n >\n <i class=\"fa-solid fa-list\" aria-hidden=\"true\"></i>\n </button>\n <button mjButton\n [variant]=\"viewMode === 'grid' ? 'primary' : 'flat'\"\n (click)=\"setViewMode('grid')\"\n (keydown.enter)=\"setViewMode('grid')\"\n (keydown.space)=\"setViewMode('grid'); $event.preventDefault()\"\n [attr.aria-label]=\"'Grid view' + (viewMode === 'grid' ? ' (active)' : '')\"\n [attr.aria-pressed]=\"viewMode === 'grid'\"\n role=\"button\"\n tabindex=\"0\"\n title=\"Grid View\"\n >\n <i class=\"fa-solid fa-th\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <button\n mjButton variant=\"secondary\" class=\"mj-btn-icon-mobile\"\n (click)=\"refreshData()\"\n [disabled]=\"isLoading\"\n aria-label=\"Refresh entity permissions data\"\n title=\"Refresh\"\n >\n <i class=\"fa-solid fa-refresh\" [class.fa-spin]=\"isLoading\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Refresh</span>\n </button>\n </div>\n\n <!-- Stats Cards -->\n <div class=\"mj-grid-4\" role=\"region\" aria-label=\"Permission statistics\">\n <div class=\"mj-card\" role=\"article\" aria-label=\"Total entities statistic\">\n <div class=\"stat-icon stat-icon-total\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-database\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" aria-label=\"Total entities count\">{{ stats.totalEntities }}</div>\n <div class=\"stat-label\">Total Entities</div>\n </div>\n </div>\n\n <div class=\"mj-card\" role=\"article\" aria-label=\"Public entities statistic\">\n <div class=\"stat-icon stat-icon-public\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-globe\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" aria-label=\"Public entities count\">{{ stats.publicEntities }}</div>\n <div class=\"stat-label\">Public Entities</div>\n </div>\n </div>\n\n <div class=\"mj-card\" role=\"article\" aria-label=\"Restricted entities statistic\">\n <div class=\"stat-icon stat-icon-restricted\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-lock\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" aria-label=\"Restricted entities count\">{{ stats.restrictedEntities }}</div>\n <div class=\"stat-label\">Restricted Entities</div>\n </div>\n </div>\n\n <div class=\"mj-card\" role=\"article\" aria-label=\"Total permissions statistic\">\n <div class=\"stat-icon stat-icon-permissions\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-key\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" aria-label=\"Total permissions count\">{{ stats.totalPermissions }}</div>\n <div class=\"stat-label\">Total Permissions</div>\n </div>\n </div>\n </div>\n\n <!-- Filters Section -->\n <div class=\"filters-section\" role=\"search\" aria-label=\"Entity filters\">\n <div class=\"filters-row\">\n <!-- Mobile: Search + Filter Button Container -->\n <div class=\"mobile-search-container\">\n <!-- Entity Search -->\n <div class=\"mj-search\">\n <i class=\"fa-solid fa-search mj-search-icon\" aria-hidden=\"true\"></i>\n <input\n type=\"text\"\n class=\"mj-search-input\"\n placeholder=\"Search entities by name or description...\"\n (input)=\"onSearchChange($event)\"\n [value]=\"filters$.value.entitySearch\"\n aria-label=\"Search entities\"\n />\n </div>\n\n <!-- Filter Button (Mobile Only) -->\n <button\n class=\"filter-button\"\n (click)=\"showMobileFilters = true\"\n aria-label=\"Open filters\"\n >\n <i class=\"fa-solid fa-filter\" aria-hidden=\"true\"></i>\n <span>Filters</span>\n </button>\n </div>\n\n <!-- Access Level Filter -->\n <div class=\"mj-filter-group\">\n <label class=\"mj-filter-label\" id=\"access-level-label\">Access Level</label>\n <div class=\"mj-filter-buttons\" role=\"group\" aria-labelledby=\"access-level-label\">\n <button mjButton\n [variant]=\"filters$.value.accessLevel === 'all' ? 'primary' : 'flat'\"\n (click)=\"onAccessLevelChange('all')\"\n [attr.aria-pressed]=\"filters$.value.accessLevel === 'all'\"\n aria-label=\"Show all access levels\"\n >\n All\n </button>\n <button mjButton\n [variant]=\"filters$.value.accessLevel === 'public' ? 'primary' : 'flat'\"\n (click)=\"onAccessLevelChange('public')\"\n [attr.aria-pressed]=\"filters$.value.accessLevel === 'public'\"\n aria-label=\"Show only public entities\"\n >\n Public\n </button>\n <button mjButton\n [variant]=\"filters$.value.accessLevel === 'restricted' ? 'primary' : 'flat'\"\n (click)=\"onAccessLevelChange('restricted')\"\n [attr.aria-pressed]=\"filters$.value.accessLevel === 'restricted'\"\n aria-label=\"Show only restricted entities\"\n >\n Restricted\n </button>\n <button mjButton\n [variant]=\"filters$.value.accessLevel === 'custom' ? 'primary' : 'flat'\"\n (click)=\"onAccessLevelChange('custom')\"\n [attr.aria-pressed]=\"filters$.value.accessLevel === 'custom'\"\n aria-label=\"Show only custom access entities\"\n >\n Custom\n </button>\n </div>\n </div>\n\n <!-- Role Filter -->\n <div class=\"mj-filter-group\">\n <label class=\"mj-filter-label\" for=\"role-filter\">Filter by Role</label>\n <select\n id=\"role-filter\"\n class=\"mj-filter-select\"\n (change)=\"onRoleFilterChange($event)\"\n aria-label=\"Filter entities by role\"\n >\n <option value=\"\">All Roles</option>\n @for (role of roles; track role.ID) {\n <option [value]=\"role.ID\">{{ role.Name }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n </div><!-- End Sticky Header -->\n\n <!-- Scrollable Content Section -->\n <div class=\"scrollable-content\">\n <!-- Loading State -->\n @if (isLoading) {\n <div class=\"loading-container\" role=\"status\" aria-live=\"polite\" aria-busy=\"true\">\n <mj-loading text=\"Loading entity permissions...\" 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\n mjButton variant=\"primary\"\n (click)=\"loadInitialData()\"\n aria-label=\"Retry loading entity permissions\"\n >\n <i class=\"fa-solid fa-refresh\" aria-hidden=\"true\"></i>\n Try Again\n </button>\n </div>\n </div>\n }\n\n <!-- Content Area -->\n @if (!isLoading && !error) {\n <div class=\"content-area\">\n @if (viewMode === 'list') {\n <!-- List View -->\n <div class=\"entities-list\" role=\"list\" aria-label=\"Entity permissions list\">\n @for (ea of filteredEntityAccess; track ea.entity.ID) {\n <div\n class=\"entity-card\"\n [class.expanded]=\"isEntityExpanded(ea.entity.ID)\"\n role=\"listitem\"\n [attr.aria-expanded]=\"isEntityExpanded(ea.entity.ID)\"\n >\n <div\n class=\"entity-header\"\n (click)=\"toggleEntityExpansion(ea.entity.ID)\"\n (keydown.enter)=\"toggleEntityExpansion(ea.entity.ID)\"\n (keydown.space)=\"toggleEntityExpansion(ea.entity.ID); $event.preventDefault()\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-label]=\"'Expand permissions for ' + ea.entity.Name\"\n >\n <div class=\"entity-info\">\n <div class=\"entity-icon-wrapper\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-table\"></i>\n </div>\n <div class=\"entity-details\">\n <h3 class=\"entity-name\">{{ ea.entity.Name }}</h3>\n <p class=\"entity-description\">{{ ea.entity.Description || 'No description available' }}</p>\n </div>\n </div>\n\n <div class=\"entity-meta\">\n <span\n class=\"access-badge\"\n [class]=\"getAccessLevelClass(ea)\"\n [attr.aria-label]=\"'Access level: ' + getAccessLevelLabel(ea)\"\n >\n <i\n [class]=\"getAccessLevelClass(ea) === 'access-public' ? 'fa-solid fa-globe' :\n getAccessLevelClass(ea) === 'access-restricted' ? 'fa-solid fa-lock' :\n 'fa-solid fa-key'\"\n aria-hidden=\"true\"\n ></i>\n {{ getAccessLevelLabel(ea) }}\n </span>\n <div class=\"entity-actions\" (click)=\"$event.stopPropagation()\">\n <button\n mjButton variant=\"flat\" size=\"sm\"\n (click)=\"editEntityPermissions(ea)\"\n title=\"Edit Permissions\"\n [attr.aria-label]=\"'Edit permissions for ' + ea.entity.Name\"\n >\n <i class=\"fa-solid fa-edit\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <button\n class=\"expand-btn\"\n [attr.aria-label]=\"(isEntityExpanded(ea.entity.ID) ? 'Collapse' : 'Expand') + ' permissions for ' + ea.entity.Name\"\n >\n <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n\n @if (isEntityExpanded(ea.entity.ID)) {\n <div class=\"entity-content\" role=\"region\" [attr.aria-label]=\"'Permissions for ' + ea.entity.Name\">\n <!-- Mobile-only actions bar -->\n <div class=\"mobile-actions-bar\" (click)=\"$event.stopPropagation()\">\n <span class=\"access-badge\" [class]=\"getAccessLevelClass(ea)\">\n <i\n [class]=\"getAccessLevelClass(ea) === 'access-public' ? 'fa-solid fa-globe' :\n getAccessLevelClass(ea) === 'access-restricted' ? 'fa-solid fa-lock' :\n 'fa-solid fa-key'\"\n aria-hidden=\"true\"\n ></i>\n {{ getAccessLevelLabel(ea) }}\n </span>\n <div class=\"mobile-action-buttons\">\n <button\n mjButton variant=\"flat\" size=\"sm\"\n (click)=\"editEntityPermissions(ea)\"\n title=\"Edit Permissions\"\n [attr.aria-label]=\"'Edit permissions for ' + ea.entity.Name\"\n >\n <i class=\"fa-solid fa-edit\" aria-hidden=\"true\"></i>\n <span class=\"btn-label\">Edit</span>\n </button>\n </div>\n </div>\n\n @if (ea.permissions.length > 0) {\n <table class=\"permissions-grid\" role=\"table\" aria-label=\"Role permissions\">\n <thead class=\"permissions-header\" role=\"rowgroup\">\n <tr role=\"row\">\n <th class=\"role-header\" scope=\"col\" role=\"columnheader\">Role</th>\n <th class=\"permission-header\" scope=\"col\" role=\"columnheader\">Create</th>\n <th class=\"permission-header\" scope=\"col\" role=\"columnheader\">Read</th>\n <th class=\"permission-header\" scope=\"col\" role=\"columnheader\">Update</th>\n <th class=\"permission-header\" scope=\"col\" role=\"columnheader\">Delete</th>\n </tr>\n </thead>\n <tbody role=\"rowgroup\">\n @for (roleId of ea.rolePermissions.keys(); track roleId) {\n <tr class=\"permission-row\" role=\"row\">\n <td class=\"role-name\" role=\"cell\">{{ getRoleName(roleId) }}</td>\n <td class=\"permission-cell\" role=\"cell\" [attr.aria-label]=\"'Create: ' + (hasPermission(ea, roleId, 'canCreate') ? 'allowed' : 'denied')\">\n <i\n [class]=\"hasPermission(ea, roleId, 'canCreate') ?\n 'fa-solid fa-check text-success' :\n 'fa-solid fa-times text-muted'\"\n [attr.aria-hidden]=\"true\"\n ></i>\n </td>\n <td class=\"permission-cell\" role=\"cell\" [attr.aria-label]=\"'Read: ' + (hasPermission(ea, roleId, 'canRead') ? 'allowed' : 'denied')\">\n <i\n [class]=\"hasPermission(ea, roleId, 'canRead') ?\n 'fa-solid fa-check text-success' :\n 'fa-solid fa-times text-muted'\"\n [attr.aria-hidden]=\"true\"\n ></i>\n </td>\n <td class=\"permission-cell\" role=\"cell\" [attr.aria-label]=\"'Update: ' + (hasPermission(ea, roleId, 'canUpdate') ? 'allowed' : 'denied')\">\n <i\n [class]=\"hasPermission(ea, roleId, 'canUpdate') ?\n 'fa-solid fa-check text-success' :\n 'fa-solid fa-times text-muted'\"\n [attr.aria-hidden]=\"true\"\n ></i>\n </td>\n <td class=\"permission-cell\" role=\"cell\" [attr.aria-label]=\"'Delete: ' + (hasPermission(ea, roleId, 'canDelete') ? 'allowed' : 'denied')\">\n <i\n [class]=\"hasPermission(ea, roleId, 'canDelete') ?\n 'fa-solid fa-check text-success' :\n 'fa-solid fa-times text-muted'\"\n [attr.aria-hidden]=\"true\"\n ></i>\n </td>\n </tr>\n }\n </tbody>\n </table>\n } @else {\n <p class=\"no-permissions\">\n @if (ea.isPublic) {\n This entity is publicly accessible by all users.\n } @else {\n No specific role permissions configured. Access is restricted to system administrators.\n }\n </p>\n }\n </div>\n }\n </div>\n }\n </div>\n } @else {\n <!-- Grid View -->\n <div class=\"entities-grid\" role=\"list\" aria-label=\"Entity permissions grid\">\n @for (ea of filteredEntityAccess; track ea.entity.ID) {\n <article\n class=\"entity-grid-card\"\n [class]=\"getAccessLevelClass(ea)\"\n [attr.aria-label]=\"'Entity: ' + ea.entity.Name\"\n >\n <div class=\"grid-card-header\">\n <i class=\"fa-solid fa-table\" aria-hidden=\"true\"></i>\n <button\n mjButton variant=\"flat\" size=\"sm\"\n (click)=\"editEntityPermissions(ea)\"\n [attr.aria-label]=\"'Edit permissions for ' + ea.entity.Name\"\n title=\"Edit Permissions\"\n >\n <i class=\"fa-solid fa-edit\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <h4 class=\"grid-card-title\">{{ ea.entity.Name }}</h4>\n <p class=\"grid-card-description\">{{ ea.entity.Description || 'No description' }}</p>\n <div class=\"grid-card-footer\">\n <span\n class=\"access-label\"\n [attr.aria-label]=\"'Access level: ' + getAccessLevelLabel(ea)\"\n >\n <i\n [class]=\"getAccessLevelClass(ea) === 'access-public' ? 'fa-solid fa-globe' :\n getAccessLevelClass(ea) === 'access-restricted' ? 'fa-solid fa-lock' :\n 'fa-solid fa-key'\"\n aria-hidden=\"true\"\n ></i>\n {{ getAccessLevelLabel(ea) }}\n </span>\n @if (ea.permissions.length > 0) {\n <span\n class=\"permission-count\"\n [attr.aria-label]=\"ea.permissions.length + ' permission' + (ea.permissions.length === 1 ? '' : 's')\"\n >\n {{ ea.permissions.length }} permission{{ ea.permissions.length === 1 ? '' : 's' }}\n </span>\n }\n </div>\n </article>\n }\n </div>\n }\n\n @if (filteredEntityAccess.length === 0) {\n <div class=\"empty-state\" role=\"status\">\n <i class=\"fa-solid fa-shield-alt empty-state-icon\" aria-hidden=\"true\"></i>\n <h3 class=\"empty-text\">No Permissions Found</h3>\n <p class=\"empty-subtext\">Try adjusting your filters or refresh the data.</p>\n <button\n mjButton variant=\"primary\"\n (click)=\"refreshData()\"\n aria-label=\"Refresh permissions data\"\n >\n <i class=\"fa-solid fa-refresh\" aria-hidden=\"true\"></i>\n Refresh\n </button>\n </div>\n }\n </div>\n }\n </div><!-- End Scrollable Content -->\n\n <!-- Permission Edit Dialog -->\n <mj-permission-dialog\n [visible]=\"showPermissionDialog\"\n [data]=\"permissionDialogData\"\n (result)=\"onPermissionDialogResult($event)\"\n ></mj-permission-dialog>\n\n <!-- Mobile Filter Modal -->\n @if (showMobileFilters) {\n <div\n class=\"filter-modal-backdrop\"\n (click)=\"showMobileFilters = false\"\n role=\"presentation\"\n >\n <div\n class=\"filter-modal\"\n (click)=\"$event.stopPropagation()\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"filter-modal-title\"\n >\n <div class=\"filter-modal-header\">\n <h3 class=\"filter-modal-title\" id=\"filter-modal-title\">\n <i class=\"fa-solid fa-filter\" aria-hidden=\"true\"></i>\n Filters\n </h3>\n <button\n class=\"filter-modal-close\"\n (click)=\"showMobileFilters = false\"\n aria-label=\"Close filters\"\n >\n <i class=\"fa-solid fa-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <div class=\"filter-modal-body\">\n <div class=\"filter-options-container\">\n <!-- Access Level Filter -->\n <div class=\"filter-group\">\n <h4 class=\"filter-group-label\">Access Level</h4>\n <div class=\"filter-group-options\">\n <label\n class=\"filter-option\"\n [class.selected]=\"filters$.value.accessLevel === 'all'\"\n >\n <input\n type=\"radio\"\n name=\"accessLevel\"\n value=\"all\"\n [checked]=\"filters$.value.accessLevel === 'all'\"\n (change)=\"onAccessLevelChange('all')\"\n />\n <span class=\"filter-option-label\">All Levels</span>\n </label>\n <label\n class=\"filter-option\"\n [class.selected]=\"filters$.value.accessLevel === 'public'\"\n >\n <input\n type=\"radio\"\n name=\"accessLevel\"\n value=\"public\"\n [checked]=\"filters$.value.accessLevel === 'public'\"\n (change)=\"onAccessLevelChange('public')\"\n />\n <span class=\"filter-option-label\">Public Entities</span>\n </label>\n <label\n class=\"filter-option\"\n [class.selected]=\"filters$.value.accessLevel === 'restricted'\"\n >\n <input\n type=\"radio\"\n name=\"accessLevel\"\n value=\"restricted\"\n [checked]=\"filters$.value.accessLevel === 'restricted'\"\n (change)=\"onAccessLevelChange('restricted')\"\n />\n <span class=\"filter-option-label\">Restricted Entities</span>\n </label>\n <label\n class=\"filter-option\"\n [class.selected]=\"filters$.value.accessLevel === 'custom'\"\n >\n <input\n type=\"radio\"\n name=\"accessLevel\"\n value=\"custom\"\n [checked]=\"filters$.value.accessLevel === 'custom'\"\n (change)=\"onAccessLevelChange('custom')\"\n />\n <span class=\"filter-option-label\">Custom Access Entities</span>\n </label>\n </div>\n </div>\n\n <!-- Role Filter -->\n <div class=\"filter-group\">\n <h4 class=\"filter-group-label\">Filter by Role</h4>\n <div class=\"filter-group-options\">\n <label class=\"filter-option\" [class.selected]=\"!filters$.value.roleId\">\n <input\n type=\"radio\"\n name=\"role\"\n value=\"\"\n [checked]=\"!filters$.value.roleId\"\n (change)=\"onRoleFilterChange($event)\"\n />\n <span class=\"filter-option-label\">All Roles</span>\n </label>\n @for (role of roles; track role.ID) {\n <label\n class=\"filter-option\"\n [class.selected]=\"filters$.value.roleId === role.ID\"\n >\n <input\n type=\"radio\"\n name=\"role\"\n [value]=\"role.ID\"\n [checked]=\"filters$.value.roleId === role.ID\"\n (change)=\"onRoleFilterChange($event)\"\n />\n <span class=\"filter-option-label\">{{ role.Name }}</span>\n </label>\n }\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"filter-modal-footer\">\n <button\n mjButton variant=\"primary\"\n (click)=\"showMobileFilters = false\"\n >\n Apply Filters\n </button>\n <button\n mjButton\n (click)=\"showMobileFilters = false\"\n >\n Cancel\n </button>\n </div>\n </div>\n </div>\n }\n</div>\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 .mj-btn-icon-mobile .btn-text {\n display: none;\n }\n\n .mj-btn-icon-mobile {\n padding: 0.5rem 0.75rem;\n min-height: 40px;\n gap: 0;\n border-radius: var(--mj-radius-full);\n }\n\n .mj-btn-icon-mobile i {\n font-size: 1.125rem;\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.mj-filter-buttons .mj-btn {\n border-radius: var(--mj-radius-full);\n}\n\n.mj-filter-buttons .mj-btn-primary {\n background-color: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n box-shadow: var(--mj-shadow-sm);\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 .modal-footer .mj-btn {\n width: 100%;\n justify-content: center;\n }\n}\n\n/* -----------------------------------------------------------------------------\n MJ Button System\n ----------------------------------------------------------------------------- */\n.mj-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.75rem 1.5rem;\n font-size: 0.875rem;\n font-weight: 600;\n border: none;\n border-radius: var(--mj-radius-full);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n white-space: nowrap;\n min-height: 44px;\n}\n\n.mj-btn:disabled {\n opacity: 0.38;\n cursor: not-allowed;\n}\n\n/* Primary Button */\n.mj-btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.mj-btn-primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-light);\n box-shadow: var(--mj-shadow-md);\n}\n\n.mj-btn-primary:active:not(:disabled) {\n background: var(--mj-color-brand-400);\n transform: scale(0.98);\n}\n\n/* Secondary Button */\n.mj-btn-secondary {\n background: var(--mj-bg-page);\n color: var(--mj-brand-primary);\n border: 1px solid var(--mj-color-neutral-400);\n}\n\n.mj-btn-secondary:hover:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.mj-btn-secondary:active:not(:disabled) {\n background: var(--mj-color-brand-700);\n border-color: var(--mj-color-brand-700);\n}\n\n/* Ghost Button */\n.mj-btn-ghost {\n background: transparent;\n color: var(--mj-text-secondary);\n}\n\n.mj-btn-ghost:hover:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.mj-btn-ghost:active:not(:disabled) {\n background: var(--mj-color-brand-700);\n}\n\n/* Danger Button */\n.mj-btn-danger {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.mj-btn-danger:hover:not(:disabled) {\n background: var(--mj-color-error-300);\n box-shadow: var(--mj-shadow-md);\n}\n\n.mj-btn-danger:active:not(:disabled) {\n background: var(--mj-color-error-200);\n transform: scale(0.98);\n}\n\n/* Small Button */\n.mj-btn-sm {\n padding: 0.5rem 0.875rem;\n font-size: 0.8125rem;\n min-height: 36px;\n}\n\n/* Icon-only Button */\n.mj-btn-icon-only {\n padding: 0.625rem;\n min-width: 44px;\n min-height: 44px;\n}\n\n/* Icon-mobile Button (text hidden on small screens) */\n.mj-btn-icon-mobile {\n gap: 0.5rem;\n}\n\n.mj-btn-icon-mobile .btn-text {\n display: inline;\n}\n\n/* Ghost Danger Button */\n.mj-btn-ghost.mj-btn-danger {\n background: transparent;\n color: var(--mj-status-error);\n box-shadow: none;\n}\n\n.mj-btn-ghost.mj-btn-danger:hover:not(:disabled) {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n}\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.mj-btn: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 Entity Permissions Component - Component-Specific Styles\n Shared patterns are in ../shared/styles/_admin-patterns.css\n ============================================================================= */\n\n/* -----------------------------------------------------------------------------\n Container\n ----------------------------------------------------------------------------- */\n.entity-permissions-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n position: relative;\n margin: 0 auto;\n width: 100%;\n background: var(--mj-bg-page);\n}\n\n/* -----------------------------------------------------------------------------\n Sticky Header Override - Has explicit padding values\n ----------------------------------------------------------------------------- */\n.sticky-header {\n padding: 1rem;\n}\n\n@media (min-width: 640px) {\n .sticky-header {\n padding: 1.25rem;\n }\n}\n\n@media (min-width: 768px) {\n .sticky-header {\n padding: 1.5rem;\n }\n}\n\n@media (min-width: 1024px) {\n .sticky-header {\n padding: 1.5rem 2rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Scrollable Content Override - Different breakpoints\n ----------------------------------------------------------------------------- */\n@media (min-width: 640px) {\n .scrollable-content {\n padding: 1.25rem;\n }\n}\n\n@media (min-width: 768px) {\n .scrollable-content {\n padding: 1.5rem;\n }\n}\n\n@media (min-width: 1024px) {\n .scrollable-content {\n padding: 1.5rem 2rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Button Override - min-width and text-decoration\n ----------------------------------------------------------------------------- */\n.mj-btn {\n min-width: 44px;\n text-decoration: none;\n}\n\n/* Ghost Button Override - Different padding */\n.mj-btn-ghost {\n padding: 0.75rem 1rem;\n}\n\n/* Small Button Override - Different min-width */\n.mj-btn-sm {\n min-width: 36px;\n}\n\n/* Icon-mobile Override */\n.mj-btn-icon-mobile {\n padding: 0.75rem 1.5rem;\n}\n\n@media (min-width: 640px) {\n .mj-btn-icon-mobile {\n padding: 0.75rem 1.5rem;\n }\n}\n\n@media (max-width: 639px) {\n .mj-btn-icon-mobile .btn-text {\n display: none;\n }\n\n .mj-btn-icon-mobile {\n padding: 0.625rem;\n gap: 0;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Action Buttons Override - Different layout (space-between with margin)\n ----------------------------------------------------------------------------- */\n.action-buttons {\n justify-content: space-between;\n margin-bottom: 1rem;\n flex-wrap: wrap;\n align-items: center;\n padding: 0;\n background: transparent;\n}\n\n@media (min-width: 768px) {\n .action-buttons {\n margin-bottom: 1.25rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Mobile Navigation Rail\n ----------------------------------------------------------------------------- */\n@media (max-width: 639px) {\n .sticky-header {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n padding: 0.75rem 1rem;\n }\n\n .action-buttons {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: 0.5rem;\n margin-bottom: 0;\n order: 1;\n width: 100%;\n }\n\n .mj-view-toggle {\n flex-shrink: 0;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n padding: 3px;\n gap: 2px;\n }\n\n .mj-view-toggle .mj-btn {\n padding: 0.5rem 0.625rem;\n min-width: 36px;\n min-height: 36px;\n border-radius: var(--mj-radius-md);\n }\n\n .mj-view-toggle .mj-btn.mj-btn-primary {\n box-shadow: none;\n }\n\n .action-buttons > .mj-btn-secondary {\n flex-shrink: 0;\n padding: 0.5rem 0.625rem;\n min-width: 36px;\n min-height: 36px;\n border-radius: var(--mj-radius-lg);\n }\n\n .filters-section {\n order: 2;\n width: 100%;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n padding: 0.75rem;\n margin-bottom: 0.5rem;\n border-radius: var(--mj-radius-lg);\n }\n\n .filters-row {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 0.5rem;\n flex-wrap: nowrap;\n }\n\n /* Mobile search container */\n .mobile-search-container {\n display: flex;\n gap: 0.5rem;\n align-items: center;\n flex: 1;\n min-width: 0;\n }\n\n .mj-search {\n flex: 1;\n min-width: 0;\n }\n\n .mj-search .mj-search-input {\n padding: 0.75rem 1rem 0.75rem 2.5rem;\n min-height: 44px;\n font-size: 1rem;\n border-width: 1.5px;\n border-radius: var(--mj-radius-full);\n box-sizing: border-box;\n width: 100%;\n }\n\n .mj-search .mj-search-icon {\n left: 0.875rem;\n font-size: 1rem;\n }\n\n .mj-search .mj-search-input::placeholder {\n font-size: 0.875rem;\n }\n\n /* Hide filter groups on mobile - they go in modal */\n .mj-filter-group {\n display: none !important;\n }\n\n .mj-filter-label {\n display: none;\n }\n\n .mj-filter-select {\n display: none;\n }\n}\n\n/* -----------------------------------------------------------------------------\n View Toggle\n ----------------------------------------------------------------------------- */\n.mj-view-toggle {\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.mj-view-toggle .mj-btn {\n padding: 0.5rem 1rem;\n border-radius: var(--mj-radius-full);\n background: transparent;\n color: var(--mj-text-secondary);\n border: none;\n font-size: 0.875rem;\n font-weight: 500;\n}\n\n.mj-view-toggle .mj-btn:hover:not(:disabled):not(.mj-btn-primary) {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.mj-view-toggle .mj-btn.mj-btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n/* -----------------------------------------------------------------------------\n Stats Grid Override - Different layout (margin-based, hidden on mobile)\n ----------------------------------------------------------------------------- */\n.mj-grid-4 {\n margin-bottom: 1rem;\n width: 100%;\n padding: 0;\n background: transparent;\n}\n\n@media (max-width: 639px) {\n .mj-grid-4 {\n display: none !important;\n }\n}\n\n@media (min-width: 640px) {\n .mj-grid-4 {\n gap: 1rem;\n }\n}\n\n@media (min-width: 768px) {\n .mj-grid-4 {\n gap: 1.25rem;\n margin-bottom: 1.25rem;\n }\n}\n\n/* Stat Card Override - min-width and no box-shadow */\n.mj-card {\n min-width: 0;\n box-shadow: none;\n}\n\n/* Stat Value Override at 768px - different size */\n@media (min-width: 768px) {\n .stat-content .stat-value {\n font-size: 1.875rem;\n }\n}\n\n/* Stat Icon Override - Component-specific colors */\n.stat-icon-public {\n background: var(--mj-color-success-100);\n color: var(--mj-status-success);\n}\n\n.stat-icon-restricted {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n}\n\n.stat-icon-permissions {\n background: var(--mj-color-warning-50);\n color: var(--mj-color-warning-700);\n}\n\n/* -----------------------------------------------------------------------------\n Filters Section Override - Different layout (no margin, full width)\n ----------------------------------------------------------------------------- */\n.filters-section {\n border-radius: var(--mj-radius-xl);\n padding: 1rem;\n margin-bottom: 0;\n width: 100%;\n margin-left: 0;\n margin-right: 0;\n}\n\n@media (min-width: 768px) {\n .filters-section {\n padding: 1rem 1.25rem;\n }\n}\n\n@media (min-width: 1024px) {\n .filters-section {\n padding: 1.25rem 1.5rem;\n }\n}\n\n@media (max-width: 639px) {\n .filters-row {\n flex-direction: column;\n align-items: stretch;\n gap: 0.75rem;\n }\n}\n\n@media (min-width: 1024px) {\n .filters-row {\n gap: 1.5rem;\n }\n}\n\n/* Search Input Override at 1024px - different padding */\n@media (min-width: 1024px) {\n .mj-search .mj-search-input {\n padding: 1rem 1.25rem 1rem 3rem;\n }\n\n .mj-search .mj-search-icon {\n left: 1.125rem;\n font-size: 1.125rem;\n }\n}\n\n/* Filter Button Overrides */\n.mj-filter-buttons {\n flex-wrap: wrap;\n}\n\n.mj-filter-buttons .mj-btn {\n padding: 0.5rem 1rem;\n border-radius: var(--mj-radius-full);\n font-size: 0.875rem;\n font-weight: 500;\n background: transparent;\n color: var(--mj-text-secondary);\n border: none;\n}\n\n.mj-filter-buttons .mj-btn:hover:not(:disabled):not(.mj-btn-primary) {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.mj-filter-buttons .mj-btn.mj-btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n@media (max-width: 639px) {\n .mj-filter-buttons {\n flex-direction: row;\n flex-wrap: wrap;\n }\n\n .mj-filter-buttons .mj-btn {\n flex: 1;\n min-width: calc(50% - 4px);\n padding: 0.5rem 0.75rem;\n font-size: 0.75rem;\n }\n}\n\n/* Filter Label Override */\n.mj-filter-label {\n font-size: 0.8125rem;\n line-height: 1.2;\n}\n\n/* Filter Select Dropdown */\n.mj-filter-select {\n padding: 0.5rem 1rem;\n border: 2px solid var(--mj-border-default);\n border-radius: var(--mj-radius-full);\n font-size: 0.875rem;\n font-weight: 500;\n background: var(--mj-bg-page);\n color: var(--mj-text-primary);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n height: 44px;\n min-width: 150px;\n box-sizing: border-box;\n}\n\n.mj-filter-select:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n}\n\n.mj-filter-select: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}\n\n@media (max-width: 639px) {\n .mj-filter-select {\n width: 100%;\n }\n}\n\n/* Mobile Search Container (Desktop Default) */\n.mobile-search-container {\n display: contents;\n}\n\n/* Filter Button Badge Override */\n.filter-button .filter-badge {\n position: absolute;\n top: -4px;\n right: -4px;\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n font-size: 0.625rem;\n font-weight: 700;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0 4px;\n}\n\n/* Loading Container Override - different layout */\n.loading-container {\n min-height: 200px;\n padding: 3rem 1.5rem;\n flex-direction: row;\n}\n\n/* -----------------------------------------------------------------------------\n Content Area Override - Different background and shadow\n ----------------------------------------------------------------------------- */\n.content-area {\n background: var(--mj-bg-surface);\n box-shadow: var(--mj-shadow-md);\n padding: 1.25rem;\n}\n\n@media (min-width: 1024px) {\n .content-area {\n padding: 1.75rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Entities List\n ----------------------------------------------------------------------------- */\n.entities-list {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n overflow: visible;\n}\n\n@media (min-width: 768px) {\n .entities-list {\n gap: 1rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Entity Card - Interactive\n ----------------------------------------------------------------------------- */\n.entity-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.entity-card:hover {\n box-shadow: var(--mj-shadow-md);\n border-color: var(--mj-brand-primary);\n}\n\n.entity-card.expanded {\n box-shadow: var(--mj-shadow-lg);\n border-color: var(--mj-brand-primary);\n}\n\n.entity-card.expanded .expand-btn i {\n transform: rotate(180deg);\n}\n\n/* Entity Header */\n.entity-header {\n padding: 1rem;\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 gap: 0.75rem;\n}\n\n.entity-header:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n@media (min-width: 768px) {\n .entity-header {\n padding: 1.25rem 1.5rem;\n }\n}\n\n/* Entity Info */\n.entity-info {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n flex: 1;\n min-width: 0;\n}\n\n@media (min-width: 768px) {\n .entity-info {\n gap: 1rem;\n }\n}\n\n/* Entity Icon Container */\n.entity-icon-wrapper {\n width: 48px;\n height: 48px;\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.25rem;\n flex-shrink: 0;\n}\n\n@media (min-width: 768px) {\n .entity-icon-wrapper {\n width: 52px;\n height: 52px;\n font-size: 1.375rem;\n }\n}\n\n/* Entity Details */\n.entity-details {\n flex: 1;\n min-width: 0;\n}\n\n.entity-details .entity-name {\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.25rem 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n@media (min-width: 768px) {\n .entity-details .entity-name {\n font-size: 1.125rem;\n }\n}\n\n.entity-details .entity-description {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n margin: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n@media (min-width: 768px) {\n .entity-details .entity-description {\n font-size: 0.875rem;\n }\n}\n\n/* Entity Meta */\n.entity-meta {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n\n@media (min-width: 768px) {\n .entity-meta {\n gap: 0.75rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Access Badges\n ----------------------------------------------------------------------------- */\n.access-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 white-space: nowrap;\n}\n\n@media (min-width: 768px) {\n .access-badge {\n padding: 0.5rem 1rem;\n font-size: 0.8125rem;\n }\n}\n\n.access-badge.access-public {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.access-badge.access-restricted {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n border: 1px solid var(--mj-status-error-border);\n}\n\n.access-badge.access-custom {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n}\n\n/* Entity Actions - Hide on mobile */\n.entity-actions {\n display: none;\n}\n\n@media (min-width: 640px) {\n .entity-actions {\n display: flex;\n gap: 0.25rem;\n }\n}\n\n@media (max-width: 639px) {\n .entity-meta > .access-badge {\n display: none;\n }\n}\n\n/* Expand Button Override - inline-flex, different transition */\n.expand-btn {\n display: inline-flex;\n}\n\n.expand-btn i {\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n/* -----------------------------------------------------------------------------\n Entity Content - Expanded Area\n ----------------------------------------------------------------------------- */\n.entity-content {\n padding: 1.25rem;\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@media (min-width: 768px) {\n .entity-content {\n padding: 1.5rem;\n }\n}\n\n@media (min-width: 1024px) {\n .entity-content {\n padding: 1.75rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Mobile Actions Bar\n ----------------------------------------------------------------------------- */\n@media (max-width: 639px) {\n .mobile-actions-bar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.75rem;\n margin: -1.25rem -1.25rem 1rem -1.25rem;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .mobile-action-buttons {\n display: flex;\n gap: 0.5rem;\n }\n\n .mobile-action-buttons .mj-btn {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n }\n\n .mobile-action-buttons .btn-label {\n font-size: 0.8125rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Permissions Grid (Table)\n ----------------------------------------------------------------------------- */\n.permissions-grid {\n width: 100%;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n border: 1px solid var(--mj-border-default);\n border-collapse: collapse;\n}\n\n/* Permissions Header */\n.permissions-header tr {\n display: grid;\n grid-template-columns: 2fr 1fr 1fr 1fr 1fr;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n background: var(--mj-bg-surface-sunken);\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n@media (min-width: 768px) {\n .permissions-header tr {\n padding: 1rem 1.25rem;\n gap: 0.75rem;\n }\n}\n\n@media (max-width: 639px) {\n .permissions-header tr {\n grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr;\n padding: 0.625rem 0.75rem;\n font-size: 0.75rem;\n }\n}\n\n/* Permission Row */\n.permission-row {\n display: grid;\n grid-template-columns: 2fr 1fr 1fr 1fr 1fr;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--mj-border-default);\n transition: background-color 0.2s ease;\n}\n\n.permission-row:hover {\n background: var(--mj-bg-surface);\n}\n\n.permission-row:last-child {\n border-bottom: none;\n}\n\n@media (min-width: 768px) {\n .permission-row {\n padding: 1rem 1.25rem;\n gap: 0.75rem;\n }\n}\n\n@media (max-width: 639px) {\n .permission-row {\n grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr;\n padding: 0.625rem 0.75rem;\n }\n}\n\n.role-header,\n.permission-header {\n text-align: left;\n}\n\n.permission-header {\n text-align: center;\n}\n\n.role-name {\n font-weight: 500;\n color: var(--mj-text-primary);\n font-size: 0.875rem;\n display: flex;\n align-items: center;\n}\n\n@media (max-width: 639px) {\n .role-name {\n font-size: 0.75rem;\n }\n}\n\n.permission-cell {\n text-align: center;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n/* Success text */\n.text-success {\n color: var(--mj-status-success);\n}\n\n.text-muted {\n color: var(--mj-color-neutral-400);\n opacity: 0.6;\n}\n\n.no-permissions {\n color: var(--mj-text-secondary);\n font-size: 0.875rem;\n margin: 0;\n padding: 1rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n text-align: center;\n}\n\n/* -----------------------------------------------------------------------------\n Entities Grid (Grid View)\n ----------------------------------------------------------------------------- */\n.entities-grid {\n display: grid;\n gap: 0.75rem;\n grid-template-columns: 1fr;\n}\n\n@media (min-width: 640px) {\n .entities-grid {\n grid-template-columns: repeat(2, 1fr);\n gap: 1rem;\n }\n}\n\n@media (min-width: 768px) {\n .entities-grid {\n gap: 1.25rem;\n }\n}\n\n@media (min-width: 1024px) {\n .entities-grid {\n grid-template-columns: repeat(3, 1fr);\n gap: 1.5rem;\n }\n}\n\n/* Entity Grid Card */\n.entity-grid-card {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-xl);\n padding: 1.25rem;\n position: relative;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.entity-grid-card:hover {\n box-shadow: var(--mj-shadow-md);\n border-color: var(--mj-brand-primary);\n}\n\n.entity-grid-card.access-public {\n border-left: 4px solid var(--mj-status-success);\n}\n\n.entity-grid-card.access-restricted {\n border-left: 4px solid var(--mj-status-error);\n}\n\n.entity-grid-card.access-custom {\n border-left: 4px solid var(--mj-color-warning-500);\n}\n\n@media (min-width: 768px) {\n .entity-grid-card {\n padding: 1.5rem;\n }\n}\n\n/* Grid Card Header */\n.grid-card-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 0.75rem;\n font-size: 1.5rem;\n color: var(--mj-brand-primary);\n}\n\n@media (min-width: 768px) {\n .grid-card-header {\n margin-bottom: 1rem;\n }\n}\n\n/* Grid Card Title */\n.grid-card-title {\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.5rem 0;\n}\n\n@media (min-width: 768px) {\n .grid-card-title {\n font-size: 1.125rem;\n }\n}\n\n/* Grid Card Description */\n.grid-card-description {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n margin: 0 0 0.75rem 0;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n@media (min-width: 768px) {\n .grid-card-description {\n font-size: 0.875rem;\n margin: 0 0 1rem 0;\n }\n}\n\n/* Grid Card Footer */\n.grid-card-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n}\n\n/* Access Label */\n.access-label {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-weight: 500;\n}\n\n.permission-count {\n color: var(--mj-text-secondary);\n}\n\n/* -----------------------------------------------------------------------------\n Empty State Override - flex layout with gap\n ----------------------------------------------------------------------------- */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1rem;\n}\n\n@media (min-width: 768px) {\n .empty-state {\n padding: 5rem 2.5rem;\n }\n}\n\n.empty-state .empty-state-icon {\n font-size: 4rem;\n color: var(--mj-border-default);\n margin-bottom: 0.5rem;\n}\n\n/* Error Container Override - flex layout with min-height */\n.error-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n min-height: 200px;\n}\n\n@media (min-width: 768px) {\n .error-container {\n padding: 5rem 2.5rem;\n }\n}\n\n.error-container .error-icon {\n margin-bottom: 0.5rem;\n}\n\n/* slideDown Override - uses max-height instead of translateY */\n@keyframes slideDown {\n from {\n opacity: 0;\n max-height: 0;\n }\n to {\n opacity: 1;\n max-height: 800px;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Very Small Mobile (< 375px)\n ----------------------------------------------------------------------------- */\n@media (max-width: 374px) {\n .filters-section {\n padding: 0.5rem;\n margin-bottom: 0.375rem;\n }\n\n .filters-row {\n gap: 0.375rem;\n }\n\n .mobile-search-container {\n gap: 0.375rem;\n }\n\n .mj-search .mj-search-input {\n padding: 0.625rem 0.75rem 0.625rem 2.25rem;\n font-size: 0.875rem;\n }\n\n .mj-search .mj-search-icon {\n left: 0.75rem;\n font-size: 0.875rem;\n }\n\n .filter-button {\n padding: 0.625rem;\n min-width: 40px;\n flex-shrink: 0;\n }\n\n .filter-button span {\n display: none;\n }\n\n .filter-modal-header {\n padding: 1rem 1.25rem;\n }\n\n .filter-modal-body {\n padding: 1.25rem;\n }\n\n .filter-modal-footer {\n padding: 1rem 1.25rem;\n }\n}\n"] }]
|
|
675
|
+
args: [{ standalone: false, selector: 'mj-entity-permissions', template: "<!--\n Interior chrome \u2014 see Section 10 of explorer-chrome-conventions.\n-->\n<mj-page-header-interior\n AriaLabel=\"Filter entity permissions\"\n Title=\"Entity Permissions\"\n Subtitle=\"Manage entity-level access for each role\">\n <!-- X-of-Y filtered count earns its meta spot per chrome conventions \u00A72.\n Partition badges (public/restricted/custom) stay dropped \u2014 they mirror\n the Access Level filter chips in [toolbar]. -->\n <div meta>\n <mj-stat-badge\n [Count]=\"filteredEntityAccess.length\"\n [Total]=\"entityAccess.length\"\n Label=\"entities\">\n </mj-stat-badge>\n </div>\n <div toolbar>\n <mj-page-search\n Placeholder=\"Search entities by name or description...\"\n [Value]=\"filters$.value.entitySearch\"\n (ValueChange)=\"updateFilter({ entitySearch: $event })\">\n </mj-page-search>\n <div class=\"status-chips\" role=\"group\" aria-label=\"Filter by access level\">\n <mj-filter-chip\n Label=\"All\"\n [Active]=\"filters$.value.accessLevel === 'all'\"\n (Clicked)=\"onAccessLevelChange('all')\">\n </mj-filter-chip>\n <mj-filter-chip\n Label=\"Public\"\n [Active]=\"filters$.value.accessLevel === 'public'\"\n (Clicked)=\"onAccessLevelChange('public')\">\n </mj-filter-chip>\n <mj-filter-chip\n Label=\"Restricted\"\n [Active]=\"filters$.value.accessLevel === 'restricted'\"\n (Clicked)=\"onAccessLevelChange('restricted')\">\n </mj-filter-chip>\n <mj-filter-chip\n Label=\"Custom\"\n [Active]=\"filters$.value.accessLevel === 'custom'\"\n (Clicked)=\"onAccessLevelChange('custom')\">\n </mj-filter-chip>\n </div>\n </div>\n <div actions>\n <mj-filter-popover\n [ActiveCount]=\"popoverActiveFilterCount\"\n [ShowClearAll]=\"popoverActiveFilterCount > 0\"\n (ClearAllRequested)=\"onFilterPanelChange({ roleId: null })\">\n <mj-filter-panel\n [Fields]=\"filterFields\"\n [Values]=\"filterValues\"\n (ValuesChange)=\"onFilterPanelChange($event)\"\n (Reset)=\"onFilterPanelChange({ roleId: null })\">\n </mj-filter-panel>\n </mj-filter-popover>\n <mj-view-toggle\n [Options]=\"viewToggleOptions\"\n [ActiveKey]=\"viewMode\"\n (KeyChange)=\"setViewMode($any($event))\">\n </mj-view-toggle>\n <mj-refresh-button [Loading]=\"isLoading\" (Clicked)=\"refreshData()\"></mj-refresh-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 entity permissions...\" 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\n mjButton variant=\"primary\"\n (click)=\"loadInitialData()\"\n aria-label=\"Retry loading entity permissions\"\n >\n <i class=\"fa-solid fa-refresh\" aria-hidden=\"true\"></i>\n Try Again\n </button>\n </div>\n </div>\n }\n\n @if (!isLoading && !error) {\n @if (viewMode === 'list') {\n <!-- List View -->\n <div class=\"entities-list\" role=\"list\" aria-label=\"Entity permissions list\">\n @for (ea of filteredEntityAccess; track ea.entity.ID) {\n <div\n class=\"entity-card\"\n [class.expanded]=\"isEntityExpanded(ea.entity.ID)\"\n role=\"listitem\"\n [attr.aria-expanded]=\"isEntityExpanded(ea.entity.ID)\"\n >\n <div\n class=\"entity-header\"\n (click)=\"toggleEntityExpansion(ea.entity.ID)\"\n (keydown.enter)=\"toggleEntityExpansion(ea.entity.ID)\"\n (keydown.space)=\"toggleEntityExpansion(ea.entity.ID); $event.preventDefault()\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-label]=\"'Expand permissions for ' + ea.entity.Name\"\n >\n <div class=\"entity-info\">\n <div class=\"entity-icon-wrapper\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-table\"></i>\n </div>\n <div class=\"entity-details\">\n <h3 class=\"entity-name\">{{ ea.entity.Name }}</h3>\n <p class=\"entity-description\">{{ ea.entity.Description || 'No description available' }}</p>\n </div>\n </div>\n\n <div class=\"entity-meta\">\n <span\n class=\"access-badge\"\n [class]=\"getAccessLevelClass(ea)\"\n [attr.aria-label]=\"'Access level: ' + getAccessLevelLabel(ea)\"\n >\n <i\n [class]=\"getAccessLevelClass(ea) === 'access-public' ? 'fa-solid fa-globe' :\n getAccessLevelClass(ea) === 'access-restricted' ? 'fa-solid fa-lock' :\n 'fa-solid fa-key'\"\n aria-hidden=\"true\"\n ></i>\n {{ getAccessLevelLabel(ea) }}\n </span>\n <div class=\"entity-actions\" (click)=\"$event.stopPropagation()\">\n <button\n mjButton variant=\"flat\" size=\"sm\"\n (click)=\"editEntityPermissions(ea)\"\n title=\"Edit Permissions\"\n [attr.aria-label]=\"'Edit permissions for ' + ea.entity.Name\"\n >\n <i class=\"fa-solid fa-edit\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <button\n class=\"expand-btn\"\n [attr.aria-label]=\"(isEntityExpanded(ea.entity.ID) ? 'Collapse' : 'Expand') + ' permissions for ' + ea.entity.Name\"\n >\n <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n\n @if (isEntityExpanded(ea.entity.ID)) {\n <div class=\"entity-content\" role=\"region\" [attr.aria-label]=\"'Permissions for ' + ea.entity.Name\">\n <!-- Mobile-only actions bar -->\n <div class=\"mobile-actions-bar\" (click)=\"$event.stopPropagation()\">\n <span class=\"access-badge\" [class]=\"getAccessLevelClass(ea)\">\n <i\n [class]=\"getAccessLevelClass(ea) === 'access-public' ? 'fa-solid fa-globe' :\n getAccessLevelClass(ea) === 'access-restricted' ? 'fa-solid fa-lock' :\n 'fa-solid fa-key'\"\n aria-hidden=\"true\"\n ></i>\n {{ getAccessLevelLabel(ea) }}\n </span>\n <div class=\"mobile-action-buttons\">\n <button\n mjButton variant=\"flat\" size=\"sm\"\n (click)=\"editEntityPermissions(ea)\"\n title=\"Edit Permissions\"\n [attr.aria-label]=\"'Edit permissions for ' + ea.entity.Name\"\n >\n <i class=\"fa-solid fa-edit\" aria-hidden=\"true\"></i>\n <span class=\"btn-label\">Edit</span>\n </button>\n </div>\n </div>\n\n @if (ea.permissions.length > 0) {\n <table class=\"permissions-grid\" role=\"table\" aria-label=\"Role permissions\">\n <thead class=\"permissions-header\" role=\"rowgroup\">\n <tr role=\"row\">\n <th class=\"role-header\" scope=\"col\" role=\"columnheader\">Role</th>\n <th class=\"permission-header\" scope=\"col\" role=\"columnheader\">Create</th>\n <th class=\"permission-header\" scope=\"col\" role=\"columnheader\">Read</th>\n <th class=\"permission-header\" scope=\"col\" role=\"columnheader\">Update</th>\n <th class=\"permission-header\" scope=\"col\" role=\"columnheader\">Delete</th>\n </tr>\n </thead>\n <tbody role=\"rowgroup\">\n @for (roleId of ea.rolePermissions.keys(); track roleId) {\n <tr class=\"permission-row\" role=\"row\">\n <td class=\"role-name\" role=\"cell\">{{ getRoleName(roleId) }}</td>\n <td class=\"permission-cell\" role=\"cell\" [attr.aria-label]=\"'Create: ' + (hasPermission(ea, roleId, 'canCreate') ? 'allowed' : 'denied')\">\n <i\n [class]=\"hasPermission(ea, roleId, 'canCreate') ?\n 'fa-solid fa-check text-success' :\n 'fa-solid fa-times text-muted'\"\n [attr.aria-hidden]=\"true\"\n ></i>\n </td>\n <td class=\"permission-cell\" role=\"cell\" [attr.aria-label]=\"'Read: ' + (hasPermission(ea, roleId, 'canRead') ? 'allowed' : 'denied')\">\n <i\n [class]=\"hasPermission(ea, roleId, 'canRead') ?\n 'fa-solid fa-check text-success' :\n 'fa-solid fa-times text-muted'\"\n [attr.aria-hidden]=\"true\"\n ></i>\n </td>\n <td class=\"permission-cell\" role=\"cell\" [attr.aria-label]=\"'Update: ' + (hasPermission(ea, roleId, 'canUpdate') ? 'allowed' : 'denied')\">\n <i\n [class]=\"hasPermission(ea, roleId, 'canUpdate') ?\n 'fa-solid fa-check text-success' :\n 'fa-solid fa-times text-muted'\"\n [attr.aria-hidden]=\"true\"\n ></i>\n </td>\n <td class=\"permission-cell\" role=\"cell\" [attr.aria-label]=\"'Delete: ' + (hasPermission(ea, roleId, 'canDelete') ? 'allowed' : 'denied')\">\n <i\n [class]=\"hasPermission(ea, roleId, 'canDelete') ?\n 'fa-solid fa-check text-success' :\n 'fa-solid fa-times text-muted'\"\n [attr.aria-hidden]=\"true\"\n ></i>\n </td>\n </tr>\n }\n </tbody>\n </table>\n } @else {\n <p class=\"no-permissions\">\n @if (ea.isPublic) {\n This entity is publicly accessible by all users.\n } @else {\n No specific role permissions configured. Access is restricted to system administrators.\n }\n </p>\n }\n </div>\n }\n </div>\n }\n </div>\n } @else {\n <!-- Grid View -->\n <div class=\"entities-grid\" role=\"list\" aria-label=\"Entity permissions grid\">\n @for (ea of filteredEntityAccess; track ea.entity.ID) {\n <article\n class=\"entity-grid-card\"\n [class]=\"getAccessLevelClass(ea)\"\n [attr.aria-label]=\"'Entity: ' + ea.entity.Name\"\n >\n <div class=\"grid-card-header\">\n <i class=\"fa-solid fa-table\" aria-hidden=\"true\"></i>\n <button\n mjButton variant=\"flat\" size=\"sm\"\n (click)=\"editEntityPermissions(ea)\"\n [attr.aria-label]=\"'Edit permissions for ' + ea.entity.Name\"\n title=\"Edit Permissions\"\n >\n <i class=\"fa-solid fa-edit\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <h4 class=\"grid-card-title\">{{ ea.entity.Name }}</h4>\n <p class=\"grid-card-description\">{{ ea.entity.Description || 'No description' }}</p>\n <div class=\"grid-card-footer\">\n <span\n class=\"access-label\"\n [attr.aria-label]=\"'Access level: ' + getAccessLevelLabel(ea)\"\n >\n <i\n [class]=\"getAccessLevelClass(ea) === 'access-public' ? 'fa-solid fa-globe' :\n getAccessLevelClass(ea) === 'access-restricted' ? 'fa-solid fa-lock' :\n 'fa-solid fa-key'\"\n aria-hidden=\"true\"\n ></i>\n {{ getAccessLevelLabel(ea) }}\n </span>\n @if (ea.permissions.length > 0) {\n <span\n class=\"permission-count\"\n [attr.aria-label]=\"ea.permissions.length + ' permission' + (ea.permissions.length === 1 ? '' : 's')\"\n >\n {{ ea.permissions.length }} permission{{ ea.permissions.length === 1 ? '' : 's' }}\n </span>\n }\n </div>\n </article>\n }\n </div>\n }\n\n @if (filteredEntityAccess.length === 0) {\n <div class=\"empty-state\" role=\"status\">\n <i class=\"fa-solid fa-shield-alt empty-state-icon\" aria-hidden=\"true\"></i>\n <h3 class=\"empty-text\">No Permissions Found</h3>\n <p class=\"empty-subtext\">Try adjusting your filters or refresh the data.</p>\n <button\n mjButton variant=\"primary\"\n (click)=\"refreshData()\"\n aria-label=\"Refresh permissions data\"\n >\n <i class=\"fa-solid fa-refresh\" aria-hidden=\"true\"></i>\n Refresh\n </button>\n </div>\n }\n }\n </mj-page-body-interior>\n\n <!-- Permission Edit Dialog -->\n <mj-permission-dialog\n [visible]=\"showPermissionDialog\"\n [data]=\"permissionDialogData\"\n (result)=\"onPermissionDialogResult($event)\"\n ></mj-permission-dialog>\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 Entity Permissions 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. */\n\n/* Status 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 Scrollable Content Override - Different breakpoints\n ----------------------------------------------------------------------------- */\n@media (min-width: 640px) {\n .scrollable-content {\n padding: 1.25rem;\n }\n}\n\n@media (min-width: 768px) {\n .scrollable-content {\n padding: 1.5rem;\n }\n}\n\n@media (min-width: 1024px) {\n .scrollable-content {\n padding: 1.5rem 2rem;\n }\n}\n\n@media (max-width: 639px) {\n .mj-grid-4 {\n display: none !important;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Stats Grid Override - Different layout (margin-based, hidden on mobile)\n ----------------------------------------------------------------------------- */\n.mj-grid-4 {\n margin-bottom: 1rem;\n width: 100%;\n padding: 0;\n background: transparent;\n}\n\n@media (max-width: 639px) {\n .mj-grid-4 {\n display: none !important;\n }\n}\n\n@media (min-width: 640px) {\n .mj-grid-4 {\n gap: 1rem;\n }\n}\n\n@media (min-width: 768px) {\n .mj-grid-4 {\n gap: 1.25rem;\n margin-bottom: 1.25rem;\n }\n}\n\n/* Stat Card Override - min-width and no box-shadow */\n.mj-card {\n min-width: 0;\n box-shadow: none;\n}\n\n/* Stat Value Override at 768px - different size */\n@media (min-width: 768px) {\n .stat-content .stat-value {\n font-size: 1.875rem;\n }\n}\n\n/* Stat Icon Override - Component-specific colors */\n.stat-icon-public {\n background: var(--mj-color-success-100);\n color: var(--mj-status-success);\n}\n\n.stat-icon-restricted {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n}\n\n.stat-icon-permissions {\n background: var(--mj-color-warning-50);\n color: var(--mj-color-warning-700);\n}\n\n/* Loading Container Override - different layout */\n.loading-container {\n min-height: 200px;\n padding: 3rem 1.5rem;\n flex-direction: row;\n}\n\n/* -----------------------------------------------------------------------------\n Entities List\n ----------------------------------------------------------------------------- */\n.entities-list {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n overflow: visible;\n}\n\n@media (min-width: 768px) {\n .entities-list {\n gap: 1rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Entity Card - Interactive\n ----------------------------------------------------------------------------- */\n.entity-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.entity-card:hover {\n box-shadow: var(--mj-shadow-md);\n border-color: var(--mj-brand-primary);\n}\n\n.entity-card.expanded {\n box-shadow: var(--mj-shadow-lg);\n border-color: var(--mj-brand-primary);\n}\n\n.entity-card.expanded .expand-btn i {\n transform: rotate(180deg);\n}\n\n/* Entity Header */\n.entity-header {\n padding: 1rem;\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 gap: 0.75rem;\n}\n\n.entity-header:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n@media (min-width: 768px) {\n .entity-header {\n padding: 1.25rem 1.5rem;\n }\n}\n\n/* Entity Info */\n.entity-info {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n flex: 1;\n min-width: 0;\n}\n\n@media (min-width: 768px) {\n .entity-info {\n gap: 1rem;\n }\n}\n\n/* Entity Icon Container */\n.entity-icon-wrapper {\n width: 48px;\n height: 48px;\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.25rem;\n flex-shrink: 0;\n}\n\n@media (min-width: 768px) {\n .entity-icon-wrapper {\n width: 52px;\n height: 52px;\n font-size: 1.375rem;\n }\n}\n\n/* Entity Details */\n.entity-details {\n flex: 1;\n min-width: 0;\n}\n\n.entity-details .entity-name {\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.25rem 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n@media (min-width: 768px) {\n .entity-details .entity-name {\n font-size: 1.125rem;\n }\n}\n\n.entity-details .entity-description {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n margin: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n@media (min-width: 768px) {\n .entity-details .entity-description {\n font-size: 0.875rem;\n }\n}\n\n/* Entity Meta */\n.entity-meta {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n\n@media (min-width: 768px) {\n .entity-meta {\n gap: 0.75rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Access Badges\n ----------------------------------------------------------------------------- */\n.access-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 white-space: nowrap;\n}\n\n@media (min-width: 768px) {\n .access-badge {\n padding: 0.5rem 1rem;\n font-size: 0.8125rem;\n }\n}\n\n.access-badge.access-public {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.access-badge.access-restricted {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n border: 1px solid var(--mj-status-error-border);\n}\n\n.access-badge.access-custom {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n}\n\n/* Entity Actions - Hide on mobile */\n.entity-actions {\n display: none;\n}\n\n@media (min-width: 640px) {\n .entity-actions {\n display: flex;\n gap: 0.25rem;\n }\n}\n\n@media (max-width: 639px) {\n .entity-meta > .access-badge {\n display: none;\n }\n}\n\n/* Expand Button Override - inline-flex, different transition */\n.expand-btn {\n display: inline-flex;\n}\n\n.expand-btn i {\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n/* -----------------------------------------------------------------------------\n Entity Content - Expanded Area\n ----------------------------------------------------------------------------- */\n.entity-content {\n padding: 1.25rem;\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@media (min-width: 768px) {\n .entity-content {\n padding: 1.5rem;\n }\n}\n\n@media (min-width: 1024px) {\n .entity-content {\n padding: 1.75rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Mobile Actions Bar\n ----------------------------------------------------------------------------- */\n@media (max-width: 639px) {\n .mobile-actions-bar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.75rem;\n margin: -1.25rem -1.25rem 1rem -1.25rem;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .mobile-action-buttons {\n display: flex;\n gap: 0.5rem;\n }\n\n .mobile-action-buttons .btn-label {\n font-size: 0.8125rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Permissions Grid (Table)\n ----------------------------------------------------------------------------- */\n.permissions-grid {\n width: 100%;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n border: 1px solid var(--mj-border-default);\n border-collapse: collapse;\n}\n\n/* Permissions Header */\n.permissions-header tr {\n display: grid;\n grid-template-columns: 2fr 1fr 1fr 1fr 1fr;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n background: var(--mj-bg-surface-sunken);\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n@media (min-width: 768px) {\n .permissions-header tr {\n padding: 1rem 1.25rem;\n gap: 0.75rem;\n }\n}\n\n@media (max-width: 639px) {\n .permissions-header tr {\n grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr;\n padding: 0.625rem 0.75rem;\n font-size: 0.75rem;\n }\n}\n\n/* Permission Row */\n.permission-row {\n display: grid;\n grid-template-columns: 2fr 1fr 1fr 1fr 1fr;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--mj-border-default);\n transition: background-color 0.2s ease;\n}\n\n.permission-row:hover {\n background: var(--mj-bg-surface);\n}\n\n.permission-row:last-child {\n border-bottom: none;\n}\n\n@media (min-width: 768px) {\n .permission-row {\n padding: 1rem 1.25rem;\n gap: 0.75rem;\n }\n}\n\n@media (max-width: 639px) {\n .permission-row {\n grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr;\n padding: 0.625rem 0.75rem;\n }\n}\n\n.role-header,\n.permission-header {\n text-align: left;\n}\n\n.permission-header {\n text-align: center;\n}\n\n.role-name {\n font-weight: 500;\n color: var(--mj-text-primary);\n font-size: 0.875rem;\n display: flex;\n align-items: center;\n}\n\n@media (max-width: 639px) {\n .role-name {\n font-size: 0.75rem;\n }\n}\n\n.permission-cell {\n text-align: center;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n/* Success text */\n.text-success {\n color: var(--mj-status-success);\n}\n\n.text-muted {\n color: var(--mj-color-neutral-400);\n opacity: 0.6;\n}\n\n.no-permissions {\n color: var(--mj-text-secondary);\n font-size: 0.875rem;\n margin: 0;\n padding: 1rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n text-align: center;\n}\n\n/* -----------------------------------------------------------------------------\n Entities Grid (Grid View)\n ----------------------------------------------------------------------------- */\n.entities-grid {\n display: grid;\n gap: 0.75rem;\n grid-template-columns: 1fr;\n}\n\n@media (min-width: 640px) {\n .entities-grid {\n grid-template-columns: repeat(2, 1fr);\n gap: 1rem;\n }\n}\n\n@media (min-width: 768px) {\n .entities-grid {\n gap: 1.25rem;\n }\n}\n\n@media (min-width: 1024px) {\n .entities-grid {\n grid-template-columns: repeat(3, 1fr);\n gap: 1.5rem;\n }\n}\n\n/* Entity Grid Card */\n.entity-grid-card {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-xl);\n padding: 1.25rem;\n position: relative;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.entity-grid-card:hover {\n box-shadow: var(--mj-shadow-md);\n border-color: var(--mj-brand-primary);\n}\n\n.entity-grid-card.access-public {\n border-left: 4px solid var(--mj-status-success);\n}\n\n.entity-grid-card.access-restricted {\n border-left: 4px solid var(--mj-status-error);\n}\n\n.entity-grid-card.access-custom {\n border-left: 4px solid var(--mj-color-warning-500);\n}\n\n@media (min-width: 768px) {\n .entity-grid-card {\n padding: 1.5rem;\n }\n}\n\n/* Grid Card Header */\n.grid-card-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 0.75rem;\n font-size: 1.5rem;\n color: var(--mj-brand-primary);\n}\n\n@media (min-width: 768px) {\n .grid-card-header {\n margin-bottom: 1rem;\n }\n}\n\n/* Grid Card Title */\n.grid-card-title {\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 0.5rem 0;\n}\n\n@media (min-width: 768px) {\n .grid-card-title {\n font-size: 1.125rem;\n }\n}\n\n/* Grid Card Description */\n.grid-card-description {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n margin: 0 0 0.75rem 0;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n@media (min-width: 768px) {\n .grid-card-description {\n font-size: 0.875rem;\n margin: 0 0 1rem 0;\n }\n}\n\n/* Grid Card Footer */\n.grid-card-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n}\n\n/* Access Label */\n.access-label {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n font-weight: 500;\n}\n\n.permission-count {\n color: var(--mj-text-secondary);\n}\n\n/* -----------------------------------------------------------------------------\n Empty State Override - flex layout with gap\n ----------------------------------------------------------------------------- */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1rem;\n}\n\n@media (min-width: 768px) {\n .empty-state {\n padding: 5rem 2.5rem;\n }\n}\n\n.empty-state .empty-state-icon {\n font-size: 4rem;\n color: var(--mj-border-default);\n margin-bottom: 0.5rem;\n}\n\n/* Error Container Override - flex layout with min-height */\n.error-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n min-height: 200px;\n}\n\n@media (min-width: 768px) {\n .error-container {\n padding: 5rem 2.5rem;\n }\n}\n\n.error-container .error-icon {\n margin-bottom: 0.5rem;\n}\n\n/* slideDown Override - uses max-height instead of translateY */\n@keyframes slideDown {\n from {\n opacity: 0;\n max-height: 0;\n }\n to {\n opacity: 1;\n max-height: 800px;\n }\n}\n\n"] }]
|
|
840
676
|
}], () => [{ type: i0.ChangeDetectorRef }, { type: i0.NgZone }], null); })();
|
|
841
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityPermissionsComponent, { className: "EntityPermissionsComponent", filePath: "src/lib/entity-permissions/entity-permissions.component.ts", lineNumber:
|
|
677
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityPermissionsComponent, { className: "EntityPermissionsComponent", filePath: "src/lib/entity-permissions/entity-permissions.component.ts", lineNumber: 50 }); })();
|
|
842
678
|
//# sourceMappingURL=entity-permissions.component.js.map
|