@memberjunction/ng-explorer-settings 5.38.0 → 5.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/application-management/application-management.component.d.ts +8 -0
- package/dist/lib/application-management/application-management.component.d.ts.map +1 -1
- package/dist/lib/application-management/application-management.component.js +162 -136
- package/dist/lib/application-management/application-management.component.js.map +1 -1
- package/dist/lib/entity-permissions/entity-permissions.component.d.ts +4 -1
- package/dist/lib/entity-permissions/entity-permissions.component.d.ts.map +1 -1
- package/dist/lib/entity-permissions/entity-permissions.component.js +144 -141
- package/dist/lib/entity-permissions/entity-permissions.component.js.map +1 -1
- package/dist/lib/role-management/role-management.component.d.ts +8 -0
- package/dist/lib/role-management/role-management.component.d.ts.map +1 -1
- package/dist/lib/role-management/role-management.component.js +147 -121
- package/dist/lib/role-management/role-management.component.js.map +1 -1
- package/dist/lib/sql-logging/sql-logging.component.js +135 -134
- package/dist/lib/sql-logging/sql-logging.component.js.map +1 -1
- package/dist/lib/user-management/user-management.component.d.ts +4 -2
- package/dist/lib/user-management/user-management.component.d.ts.map +1 -1
- package/dist/lib/user-management/user-management.component.js +264 -258
- package/dist/lib/user-management/user-management.component.js.map +1 -1
- package/package.json +19 -19
|
@@ -23,8 +23,9 @@ function SqlLoggingComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
|
23
23
|
i0.ɵɵelementStart(0, "button", 9);
|
|
24
24
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_3_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.openStartSessionDialog()); });
|
|
25
25
|
i0.ɵɵelement(1, "i", 10);
|
|
26
|
-
i0.ɵɵ
|
|
27
|
-
i0.ɵɵ
|
|
26
|
+
i0.ɵɵelementStart(2, "span", 11);
|
|
27
|
+
i0.ɵɵtext(3, "Start New Session");
|
|
28
|
+
i0.ɵɵelementEnd()();
|
|
28
29
|
} if (rf & 2) {
|
|
29
30
|
const ctx_r1 = i0.ɵɵnextContext();
|
|
30
31
|
i0.ɵɵproperty("disabled", ctx_r1.loading || ctx_r1.activeSessions.length >= ((ctx_r1.sqlLoggingConfig == null ? null : ctx_r1.sqlLoggingConfig.maxActiveSessions) || 5));
|
|
@@ -32,53 +33,53 @@ function SqlLoggingComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
|
32
33
|
} }
|
|
33
34
|
function SqlLoggingComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
34
35
|
const _r3 = i0.ɵɵgetCurrentView();
|
|
35
|
-
i0.ɵɵelementStart(0, "div", 4)(1, "button",
|
|
36
|
+
i0.ɵɵelementStart(0, "div", 4)(1, "button", 12);
|
|
36
37
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_5_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.showStats = !ctx_r1.showStats); });
|
|
37
|
-
i0.ɵɵelementStart(2, "div",
|
|
38
|
-
i0.ɵɵelement(4, "i",
|
|
38
|
+
i0.ɵɵelementStart(2, "div", 13)(3, "div", 14);
|
|
39
|
+
i0.ɵɵelement(4, "i", 15);
|
|
39
40
|
i0.ɵɵelementEnd();
|
|
40
|
-
i0.ɵɵelementStart(5, "div",
|
|
41
|
+
i0.ɵɵelementStart(5, "div", 16)(6, "h3", 17);
|
|
41
42
|
i0.ɵɵtext(7, "Statistics");
|
|
42
43
|
i0.ɵɵelementEnd();
|
|
43
|
-
i0.ɵɵelementStart(8, "p",
|
|
44
|
+
i0.ɵɵelementStart(8, "p", 18);
|
|
44
45
|
i0.ɵɵtext(9, "View session statistics and metrics");
|
|
45
46
|
i0.ɵɵelementEnd()()();
|
|
46
|
-
i0.ɵɵelement(10, "i",
|
|
47
|
+
i0.ɵɵelement(10, "i", 19);
|
|
47
48
|
i0.ɵɵelementEnd();
|
|
48
|
-
i0.ɵɵelementStart(11, "div",
|
|
49
|
-
i0.ɵɵelement(14, "i",
|
|
49
|
+
i0.ɵɵelementStart(11, "div", 20)(12, "div", 21)(13, "div", 22);
|
|
50
|
+
i0.ɵɵelement(14, "i", 23);
|
|
50
51
|
i0.ɵɵelementEnd();
|
|
51
|
-
i0.ɵɵelementStart(15, "div",
|
|
52
|
+
i0.ɵɵelementStart(15, "div", 24)(16, "div", 25);
|
|
52
53
|
i0.ɵɵtext(17);
|
|
53
54
|
i0.ɵɵelementEnd();
|
|
54
|
-
i0.ɵɵelementStart(18, "div",
|
|
55
|
+
i0.ɵɵelementStart(18, "div", 26);
|
|
55
56
|
i0.ɵɵtext(19, "Status");
|
|
56
57
|
i0.ɵɵelementEnd()()();
|
|
57
|
-
i0.ɵɵelementStart(20, "div",
|
|
58
|
-
i0.ɵɵelement(22, "i",
|
|
58
|
+
i0.ɵɵelementStart(20, "div", 21)(21, "div", 27);
|
|
59
|
+
i0.ɵɵelement(22, "i", 28);
|
|
59
60
|
i0.ɵɵelementEnd();
|
|
60
|
-
i0.ɵɵelementStart(23, "div",
|
|
61
|
+
i0.ɵɵelementStart(23, "div", 24)(24, "div", 25);
|
|
61
62
|
i0.ɵɵtext(25);
|
|
62
63
|
i0.ɵɵelementEnd();
|
|
63
|
-
i0.ɵɵelementStart(26, "div",
|
|
64
|
+
i0.ɵɵelementStart(26, "div", 26);
|
|
64
65
|
i0.ɵɵtext(27, "Active Sessions");
|
|
65
66
|
i0.ɵɵelementEnd()()();
|
|
66
|
-
i0.ɵɵelementStart(28, "div",
|
|
67
|
-
i0.ɵɵelement(30, "i",
|
|
67
|
+
i0.ɵɵelementStart(28, "div", 21)(29, "div", 29);
|
|
68
|
+
i0.ɵɵelement(30, "i", 30);
|
|
68
69
|
i0.ɵɵelementEnd();
|
|
69
|
-
i0.ɵɵelementStart(31, "div",
|
|
70
|
+
i0.ɵɵelementStart(31, "div", 24)(32, "div", 25);
|
|
70
71
|
i0.ɵɵtext(33);
|
|
71
72
|
i0.ɵɵelementEnd();
|
|
72
|
-
i0.ɵɵelementStart(34, "div",
|
|
73
|
+
i0.ɵɵelementStart(34, "div", 26);
|
|
73
74
|
i0.ɵɵtext(35, "Max Sessions");
|
|
74
75
|
i0.ɵɵelementEnd()()();
|
|
75
|
-
i0.ɵɵelementStart(36, "div",
|
|
76
|
-
i0.ɵɵelement(38, "i",
|
|
76
|
+
i0.ɵɵelementStart(36, "div", 21)(37, "div", 31);
|
|
77
|
+
i0.ɵɵelement(38, "i", 32);
|
|
77
78
|
i0.ɵɵelementEnd();
|
|
78
|
-
i0.ɵɵelementStart(39, "div",
|
|
79
|
+
i0.ɵɵelementStart(39, "div", 24)(40, "div", 25);
|
|
79
80
|
i0.ɵɵtext(41);
|
|
80
81
|
i0.ɵɵelementEnd();
|
|
81
|
-
i0.ɵɵelementStart(42, "div",
|
|
82
|
+
i0.ɵɵelementStart(42, "div", 26);
|
|
82
83
|
i0.ɵɵtext(43, "Total Statements");
|
|
83
84
|
i0.ɵɵelementEnd()()()()();
|
|
84
85
|
} if (rf & 2) {
|
|
@@ -107,35 +108,35 @@ function SqlLoggingComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
|
107
108
|
} }
|
|
108
109
|
function SqlLoggingComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
109
110
|
i0.ɵɵelementStart(0, "div", 5);
|
|
110
|
-
i0.ɵɵelement(1, "mj-loading",
|
|
111
|
+
i0.ɵɵelement(1, "mj-loading", 33);
|
|
111
112
|
i0.ɵɵelementEnd();
|
|
112
113
|
} }
|
|
113
114
|
function SqlLoggingComponent_Conditional_7_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
114
115
|
const _r4 = i0.ɵɵgetCurrentView();
|
|
115
|
-
i0.ɵɵelementStart(0, "div",
|
|
116
|
-
i0.ɵɵelement(1, "i",
|
|
117
|
-
i0.ɵɵelementStart(2, "p",
|
|
116
|
+
i0.ɵɵelementStart(0, "div", 34);
|
|
117
|
+
i0.ɵɵelement(1, "i", 37);
|
|
118
|
+
i0.ɵɵelementStart(2, "p", 38);
|
|
118
119
|
i0.ɵɵtext(3, "Access Denied");
|
|
119
120
|
i0.ɵɵelementEnd();
|
|
120
|
-
i0.ɵɵelementStart(4, "p",
|
|
121
|
+
i0.ɵɵelementStart(4, "p", 39);
|
|
121
122
|
i0.ɵɵtext(5, "SQL logging requires Owner privileges. Please contact your system administrator for access.");
|
|
122
123
|
i0.ɵɵelementEnd();
|
|
123
|
-
i0.ɵɵelementStart(6, "button",
|
|
124
|
+
i0.ɵɵelementStart(6, "button", 40);
|
|
124
125
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_7_Conditional_1_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.refreshUserPermissions()); });
|
|
125
|
-
i0.ɵɵelement(7, "i",
|
|
126
|
+
i0.ɵɵelement(7, "i", 41);
|
|
126
127
|
i0.ɵɵtext(8, " Refresh Permissions ");
|
|
127
128
|
i0.ɵɵelementEnd()();
|
|
128
129
|
} }
|
|
129
130
|
function SqlLoggingComponent_Conditional_7_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
130
|
-
i0.ɵɵelementStart(0, "div",
|
|
131
|
-
i0.ɵɵelement(1, "i",
|
|
132
|
-
i0.ɵɵelementStart(2, "p",
|
|
131
|
+
i0.ɵɵelementStart(0, "div", 34);
|
|
132
|
+
i0.ɵɵelement(1, "i", 42);
|
|
133
|
+
i0.ɵɵelementStart(2, "p", 38);
|
|
133
134
|
i0.ɵɵtext(3, "SQL Logging Disabled");
|
|
134
135
|
i0.ɵɵelementEnd();
|
|
135
|
-
i0.ɵɵelementStart(4, "p",
|
|
136
|
+
i0.ɵɵelementStart(4, "p", 39);
|
|
136
137
|
i0.ɵɵtext(5, "SQL logging is not enabled in the server configuration.");
|
|
137
138
|
i0.ɵɵelementEnd();
|
|
138
|
-
i0.ɵɵelementStart(6, "div",
|
|
139
|
+
i0.ɵɵelementStart(6, "div", 43)(7, "h4");
|
|
139
140
|
i0.ɵɵtext(8, "To enable SQL logging:");
|
|
140
141
|
i0.ɵɵelementEnd();
|
|
141
142
|
i0.ɵɵelementStart(9, "ol")(10, "li");
|
|
@@ -154,15 +155,15 @@ function SqlLoggingComponent_Conditional_7_Conditional_2_Template(rf, ctx) { if
|
|
|
154
155
|
} }
|
|
155
156
|
function SqlLoggingComponent_Conditional_7_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
156
157
|
const _r5 = i0.ɵɵgetCurrentView();
|
|
157
|
-
i0.ɵɵelementStart(0, "div",
|
|
158
|
-
i0.ɵɵelement(1, "i",
|
|
159
|
-
i0.ɵɵelementStart(2, "p",
|
|
158
|
+
i0.ɵɵelementStart(0, "div", 35);
|
|
159
|
+
i0.ɵɵelement(1, "i", 44);
|
|
160
|
+
i0.ɵɵelementStart(2, "p", 38);
|
|
160
161
|
i0.ɵɵtext(3, "No Active Sessions");
|
|
161
162
|
i0.ɵɵelementEnd();
|
|
162
|
-
i0.ɵɵelementStart(4, "p",
|
|
163
|
+
i0.ɵɵelementStart(4, "p", 39);
|
|
163
164
|
i0.ɵɵtext(5, "Start a new SQL logging session to begin capturing SQL statements.");
|
|
164
165
|
i0.ɵɵelementEnd();
|
|
165
|
-
i0.ɵɵelementStart(6, "button",
|
|
166
|
+
i0.ɵɵelementStart(6, "button", 45);
|
|
166
167
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_7_Conditional_3_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.openStartSessionDialog()); });
|
|
167
168
|
i0.ɵɵelement(7, "i", 10);
|
|
168
169
|
i0.ɵɵtext(8, " Start New Session ");
|
|
@@ -170,9 +171,9 @@ function SqlLoggingComponent_Conditional_7_Conditional_3_Template(rf, ctx) { if
|
|
|
170
171
|
} }
|
|
171
172
|
function SqlLoggingComponent_Conditional_7_Conditional_4_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
172
173
|
const _r6 = i0.ɵɵgetCurrentView();
|
|
173
|
-
i0.ɵɵelementStart(0, "button",
|
|
174
|
+
i0.ɵɵelementStart(0, "button", 53);
|
|
174
175
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_7_Conditional_4_Conditional_5_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.openStopAllSessionsConfirm()); });
|
|
175
|
-
i0.ɵɵelement(1, "i",
|
|
176
|
+
i0.ɵɵelement(1, "i", 54);
|
|
176
177
|
i0.ɵɵtext(2, " Stop All ");
|
|
177
178
|
i0.ɵɵelementEnd();
|
|
178
179
|
} if (rf & 2) {
|
|
@@ -180,40 +181,40 @@ function SqlLoggingComponent_Conditional_7_Conditional_4_Conditional_5_Template(
|
|
|
180
181
|
i0.ɵɵproperty("disabled", ctx_r1.loading);
|
|
181
182
|
} }
|
|
182
183
|
function SqlLoggingComponent_Conditional_7_Conditional_4_For_8_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
183
|
-
i0.ɵɵelementStart(0, "span",
|
|
184
|
-
i0.ɵɵelement(1, "i",
|
|
184
|
+
i0.ɵɵelementStart(0, "span", 64);
|
|
185
|
+
i0.ɵɵelement(1, "i", 67);
|
|
185
186
|
i0.ɵɵtext(2, " User Filtered ");
|
|
186
187
|
i0.ɵɵelementEnd();
|
|
187
188
|
} }
|
|
188
189
|
function SqlLoggingComponent_Conditional_7_Conditional_4_For_8_Conditional_16_Template(rf, ctx) { if (rf & 1) {
|
|
189
|
-
i0.ɵɵelementStart(0, "span",
|
|
190
|
-
i0.ɵɵelement(1, "i",
|
|
190
|
+
i0.ɵɵelementStart(0, "span", 65);
|
|
191
|
+
i0.ɵɵelement(1, "i", 68);
|
|
191
192
|
i0.ɵɵtext(2, " Migration ");
|
|
192
193
|
i0.ɵɵelementEnd();
|
|
193
194
|
} }
|
|
194
195
|
function SqlLoggingComponent_Conditional_7_Conditional_4_For_8_Template(rf, ctx) { if (rf & 1) {
|
|
195
196
|
const _r7 = i0.ɵɵgetCurrentView();
|
|
196
|
-
i0.ɵɵelementStart(0, "div",
|
|
197
|
+
i0.ɵɵelementStart(0, "div", 55);
|
|
197
198
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_7_Conditional_4_For_8_Template_div_click_0_listener() { const session_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.selectSession(session_r8)); })("keydown.enter", function SqlLoggingComponent_Conditional_7_Conditional_4_For_8_Template_div_keydown_enter_0_listener() { const session_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.selectSession(session_r8)); })("keydown.space", function SqlLoggingComponent_Conditional_7_Conditional_4_For_8_Template_div_keydown_space_0_listener($event) { const session_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); ctx_r1.selectSession(session_r8); return i0.ɵɵresetView($event.preventDefault()); });
|
|
198
|
-
i0.ɵɵelementStart(1, "div",
|
|
199
|
+
i0.ɵɵelementStart(1, "div", 56)(2, "div", 57)(3, "h4", 58);
|
|
199
200
|
i0.ɵɵtext(4);
|
|
200
201
|
i0.ɵɵelementEnd();
|
|
201
|
-
i0.ɵɵelementStart(5, "div",
|
|
202
|
-
i0.ɵɵelement(7, "i",
|
|
202
|
+
i0.ɵɵelementStart(5, "div", 59)(6, "span", 60);
|
|
203
|
+
i0.ɵɵelement(7, "i", 61);
|
|
203
204
|
i0.ɵɵtext(8);
|
|
204
205
|
i0.ɵɵelementEnd();
|
|
205
|
-
i0.ɵɵelementStart(9, "span",
|
|
206
|
-
i0.ɵɵelement(10, "i",
|
|
206
|
+
i0.ɵɵelementStart(9, "span", 60);
|
|
207
|
+
i0.ɵɵelement(10, "i", 32);
|
|
207
208
|
i0.ɵɵtext(11);
|
|
208
209
|
i0.ɵɵelementEnd()()();
|
|
209
|
-
i0.ɵɵelementStart(12, "button",
|
|
210
|
+
i0.ɵɵelementStart(12, "button", 62);
|
|
210
211
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_7_Conditional_4_For_8_Template_button_click_12_listener($event) { const session_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.openStopSessionConfirm(session_r8, $event)); });
|
|
211
|
-
i0.ɵɵelement(13, "i",
|
|
212
|
+
i0.ɵɵelement(13, "i", 54);
|
|
212
213
|
i0.ɵɵelementEnd()();
|
|
213
|
-
i0.ɵɵelementStart(14, "div",
|
|
214
|
-
i0.ɵɵconditionalCreate(15, SqlLoggingComponent_Conditional_7_Conditional_4_For_8_Conditional_15_Template, 3, 0, "span",
|
|
215
|
-
i0.ɵɵconditionalCreate(16, SqlLoggingComponent_Conditional_7_Conditional_4_For_8_Conditional_16_Template, 3, 0, "span",
|
|
216
|
-
i0.ɵɵelementStart(17, "span",
|
|
214
|
+
i0.ɵɵelementStart(14, "div", 63);
|
|
215
|
+
i0.ɵɵconditionalCreate(15, SqlLoggingComponent_Conditional_7_Conditional_4_For_8_Conditional_15_Template, 3, 0, "span", 64);
|
|
216
|
+
i0.ɵɵconditionalCreate(16, SqlLoggingComponent_Conditional_7_Conditional_4_For_8_Conditional_16_Template, 3, 0, "span", 65);
|
|
217
|
+
i0.ɵɵelementStart(17, "span", 66);
|
|
217
218
|
i0.ɵɵtext(18);
|
|
218
219
|
i0.ɵɵelementEnd()()();
|
|
219
220
|
} if (rf & 2) {
|
|
@@ -238,24 +239,24 @@ function SqlLoggingComponent_Conditional_7_Conditional_4_For_8_Template(rf, ctx)
|
|
|
238
239
|
} }
|
|
239
240
|
function SqlLoggingComponent_Conditional_7_Conditional_4_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
240
241
|
const _r9 = i0.ɵɵgetCurrentView();
|
|
241
|
-
i0.ɵɵelementStart(0, "div",
|
|
242
|
+
i0.ɵɵelementStart(0, "div", 47)(1, "h3", 48);
|
|
242
243
|
i0.ɵɵtext(2);
|
|
243
244
|
i0.ɵɵelementEnd();
|
|
244
|
-
i0.ɵɵelementStart(3, "div",
|
|
245
|
+
i0.ɵɵelementStart(3, "div", 69)(4, "label", 70)(5, "input", 71);
|
|
245
246
|
i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_7_Conditional_4_Conditional_10_Template_input_ngModelChange_5_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(3); i0.ɵɵtwoWayBindingSet(ctx_r1.autoRefresh, $event) || (ctx_r1.autoRefresh = $event); return i0.ɵɵresetView($event); });
|
|
246
247
|
i0.ɵɵelementEnd();
|
|
247
248
|
i0.ɵɵtext(6, " Auto-refresh ");
|
|
248
249
|
i0.ɵɵelementEnd();
|
|
249
|
-
i0.ɵɵelementStart(7, "button",
|
|
250
|
+
i0.ɵɵelementStart(7, "button", 72);
|
|
250
251
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_7_Conditional_4_Conditional_10_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.loadSessionLog(ctx_r1.selectedSession)); });
|
|
251
|
-
i0.ɵɵelement(8, "i",
|
|
252
|
+
i0.ɵɵelement(8, "i", 41);
|
|
252
253
|
i0.ɵɵelementEnd();
|
|
253
|
-
i0.ɵɵelementStart(9, "button",
|
|
254
|
+
i0.ɵɵelementStart(9, "button", 73);
|
|
254
255
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_7_Conditional_4_Conditional_10_Template_button_click_9_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.toggleLogViewerExpand()); });
|
|
255
|
-
i0.ɵɵelement(10, "i",
|
|
256
|
+
i0.ɵɵelement(10, "i", 74);
|
|
256
257
|
i0.ɵɵelementEnd()()();
|
|
257
|
-
i0.ɵɵelementStart(11, "div",
|
|
258
|
-
i0.ɵɵelement(12, "mj-code-editor",
|
|
258
|
+
i0.ɵɵelementStart(11, "div", 75);
|
|
259
|
+
i0.ɵɵelement(12, "mj-code-editor", 76);
|
|
259
260
|
i0.ɵɵelementEnd();
|
|
260
261
|
} if (rf & 2) {
|
|
261
262
|
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
@@ -273,26 +274,26 @@ function SqlLoggingComponent_Conditional_7_Conditional_4_Conditional_10_Template
|
|
|
273
274
|
i0.ɵɵproperty("value", ctx_r1.logContent)("readonly", true)("disabled", true)("language", "sql")("setup", "basic")("lineWrapping", true)("highlightWhitespace", false);
|
|
274
275
|
} }
|
|
275
276
|
function SqlLoggingComponent_Conditional_7_Conditional_4_Conditional_11_Template(rf, ctx) { if (rf & 1) {
|
|
276
|
-
i0.ɵɵelementStart(0, "div",
|
|
277
|
-
i0.ɵɵelement(1, "i",
|
|
278
|
-
i0.ɵɵelementStart(2, "p",
|
|
277
|
+
i0.ɵɵelementStart(0, "div", 35);
|
|
278
|
+
i0.ɵɵelement(1, "i", 77);
|
|
279
|
+
i0.ɵɵelementStart(2, "p", 38);
|
|
279
280
|
i0.ɵɵtext(3, "Select a Session");
|
|
280
281
|
i0.ɵɵelementEnd();
|
|
281
|
-
i0.ɵɵelementStart(4, "p",
|
|
282
|
+
i0.ɵɵelementStart(4, "p", 39);
|
|
282
283
|
i0.ɵɵtext(5, "Choose a session from the list to view its SQL log.");
|
|
283
284
|
i0.ɵɵelementEnd()();
|
|
284
285
|
} }
|
|
285
286
|
function SqlLoggingComponent_Conditional_7_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
286
|
-
i0.ɵɵelementStart(0, "div",
|
|
287
|
+
i0.ɵɵelementStart(0, "div", 36)(1, "div", 46)(2, "div", 47)(3, "h3", 48);
|
|
287
288
|
i0.ɵɵtext(4, "Active Sessions");
|
|
288
289
|
i0.ɵɵelementEnd();
|
|
289
|
-
i0.ɵɵconditionalCreate(5, SqlLoggingComponent_Conditional_7_Conditional_4_Conditional_5_Template, 3, 1, "button",
|
|
290
|
+
i0.ɵɵconditionalCreate(5, SqlLoggingComponent_Conditional_7_Conditional_4_Conditional_5_Template, 3, 1, "button", 49);
|
|
290
291
|
i0.ɵɵelementEnd();
|
|
291
|
-
i0.ɵɵelementStart(6, "div",
|
|
292
|
-
i0.ɵɵrepeaterCreate(7, SqlLoggingComponent_Conditional_7_Conditional_4_For_8_Template, 19, 11, "div",
|
|
292
|
+
i0.ɵɵelementStart(6, "div", 50);
|
|
293
|
+
i0.ɵɵrepeaterCreate(7, SqlLoggingComponent_Conditional_7_Conditional_4_For_8_Template, 19, 11, "div", 51, _forTrack0);
|
|
293
294
|
i0.ɵɵelementEnd()();
|
|
294
|
-
i0.ɵɵelementStart(9, "div",
|
|
295
|
-
i0.ɵɵconditionalCreate(10, SqlLoggingComponent_Conditional_7_Conditional_4_Conditional_10_Template, 13, 17)(11, SqlLoggingComponent_Conditional_7_Conditional_4_Conditional_11_Template, 6, 0, "div",
|
|
295
|
+
i0.ɵɵelementStart(9, "div", 52);
|
|
296
|
+
i0.ɵɵconditionalCreate(10, SqlLoggingComponent_Conditional_7_Conditional_4_Conditional_10_Template, 13, 17)(11, SqlLoggingComponent_Conditional_7_Conditional_4_Conditional_11_Template, 6, 0, "div", 35);
|
|
296
297
|
i0.ɵɵelementEnd()();
|
|
297
298
|
} if (rf & 2) {
|
|
298
299
|
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
@@ -307,7 +308,7 @@ function SqlLoggingComponent_Conditional_7_Conditional_4_Template(rf, ctx) { if
|
|
|
307
308
|
} }
|
|
308
309
|
function SqlLoggingComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
309
310
|
i0.ɵɵelementStart(0, "div", 6);
|
|
310
|
-
i0.ɵɵconditionalCreate(1, SqlLoggingComponent_Conditional_7_Conditional_1_Template, 9, 0, "div",
|
|
311
|
+
i0.ɵɵconditionalCreate(1, SqlLoggingComponent_Conditional_7_Conditional_1_Template, 9, 0, "div", 34)(2, SqlLoggingComponent_Conditional_7_Conditional_2_Template, 19, 0, "div", 34)(3, SqlLoggingComponent_Conditional_7_Conditional_3_Template, 9, 0, "div", 35)(4, SqlLoggingComponent_Conditional_7_Conditional_4_Template, 12, 4, "div", 36);
|
|
311
312
|
i0.ɵɵelementEnd();
|
|
312
313
|
} if (rf & 2) {
|
|
313
314
|
const ctx_r1 = i0.ɵɵnextContext();
|
|
@@ -315,7 +316,7 @@ function SqlLoggingComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
|
315
316
|
i0.ɵɵconditional(!ctx_r1.isOwner ? 1 : !ctx_r1.configEnabled ? 2 : ctx_r1.activeSessions.length === 0 ? 3 : 4);
|
|
316
317
|
} }
|
|
317
318
|
function SqlLoggingComponent_Conditional_8_For_29_Template(rf, ctx) { if (rf & 1) {
|
|
318
|
-
i0.ɵɵelementStart(0, "option",
|
|
319
|
+
i0.ɵɵelementStart(0, "option", 96);
|
|
319
320
|
i0.ɵɵtext(1);
|
|
320
321
|
i0.ɵɵelementEnd();
|
|
321
322
|
} if (rf & 2) {
|
|
@@ -325,7 +326,7 @@ function SqlLoggingComponent_Conditional_8_For_29_Template(rf, ctx) { if (rf & 1
|
|
|
325
326
|
i0.ɵɵtextInterpolate(option_r11.text);
|
|
326
327
|
} }
|
|
327
328
|
function SqlLoggingComponent_Conditional_8_For_51_Template(rf, ctx) { if (rf & 1) {
|
|
328
|
-
i0.ɵɵelementStart(0, "option",
|
|
329
|
+
i0.ɵɵelementStart(0, "option", 96);
|
|
329
330
|
i0.ɵɵtext(1);
|
|
330
331
|
i0.ɵɵelementEnd();
|
|
331
332
|
} if (rf & 2) {
|
|
@@ -336,13 +337,13 @@ function SqlLoggingComponent_Conditional_8_For_51_Template(rf, ctx) { if (rf & 1
|
|
|
336
337
|
} }
|
|
337
338
|
function SqlLoggingComponent_Conditional_8_Conditional_64_Template(rf, ctx) { if (rf & 1) {
|
|
338
339
|
const _r13 = i0.ɵɵgetCurrentView();
|
|
339
|
-
i0.ɵɵelementStart(0, "div",
|
|
340
|
+
i0.ɵɵelementStart(0, "div", 87)(1, "label", 115);
|
|
340
341
|
i0.ɵɵtext(2, "Default Schema Name");
|
|
341
342
|
i0.ɵɵelementEnd();
|
|
342
|
-
i0.ɵɵelementStart(3, "input",
|
|
343
|
+
i0.ɵɵelementStart(3, "input", 116);
|
|
343
344
|
i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_8_Conditional_64_Template_input_ngModelChange_3_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r1 = i0.ɵɵnextContext(2); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.defaultSchemaName, $event) || (ctx_r1.newSessionOptions.defaultSchemaName = $event); return i0.ɵɵresetView($event); });
|
|
344
345
|
i0.ɵɵelementEnd();
|
|
345
|
-
i0.ɵɵelementStart(4, "div",
|
|
346
|
+
i0.ɵɵelementStart(4, "div", 117);
|
|
346
347
|
i0.ɵɵtext(5, "Schema name to replace with ${flyway:defaultSchema} placeholder.");
|
|
347
348
|
i0.ɵɵelementEnd()();
|
|
348
349
|
} if (rf & 2) {
|
|
@@ -351,7 +352,7 @@ function SqlLoggingComponent_Conditional_8_Conditional_64_Template(rf, ctx) { if
|
|
|
351
352
|
i0.ɵɵtwoWayProperty("ngModel", ctx_r1.newSessionOptions.defaultSchemaName);
|
|
352
353
|
} }
|
|
353
354
|
function SqlLoggingComponent_Conditional_8_Conditional_73_Template(rf, ctx) { if (rf & 1) {
|
|
354
|
-
i0.ɵɵelement(0, "i",
|
|
355
|
+
i0.ɵɵelement(0, "i", 118);
|
|
355
356
|
i0.ɵɵtext(1, " Starting... ");
|
|
356
357
|
} }
|
|
357
358
|
function SqlLoggingComponent_Conditional_8_Conditional_74_Template(rf, ctx) { if (rf & 1) {
|
|
@@ -360,106 +361,106 @@ function SqlLoggingComponent_Conditional_8_Conditional_74_Template(rf, ctx) { if
|
|
|
360
361
|
} }
|
|
361
362
|
function SqlLoggingComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
362
363
|
const _r10 = i0.ɵɵgetCurrentView();
|
|
363
|
-
i0.ɵɵelementStart(0, "div",
|
|
364
|
+
i0.ɵɵelementStart(0, "div", 78);
|
|
364
365
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_8_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.showStartSessionDialog = false); });
|
|
365
|
-
i0.ɵɵelementStart(1, "div",
|
|
366
|
+
i0.ɵɵelementStart(1, "div", 79);
|
|
366
367
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_8_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r10); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
367
|
-
i0.ɵɵelementStart(2, "div",
|
|
368
|
+
i0.ɵɵelementStart(2, "div", 80)(3, "h3", 81);
|
|
368
369
|
i0.ɵɵelement(4, "i", 10);
|
|
369
370
|
i0.ɵɵtext(5, " Start SQL Logging Session ");
|
|
370
371
|
i0.ɵɵelementEnd();
|
|
371
|
-
i0.ɵɵelementStart(6, "div",
|
|
372
|
+
i0.ɵɵelementStart(6, "div", 82)(7, "button", 83);
|
|
372
373
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_8_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.toggleStartDialogFullscreen()); });
|
|
373
|
-
i0.ɵɵelement(8, "i",
|
|
374
|
+
i0.ɵɵelement(8, "i", 74);
|
|
374
375
|
i0.ɵɵelementEnd();
|
|
375
|
-
i0.ɵɵelementStart(9, "button",
|
|
376
|
+
i0.ɵɵelementStart(9, "button", 84);
|
|
376
377
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_8_Template_button_click_9_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.showStartSessionDialog = false); });
|
|
377
|
-
i0.ɵɵelement(10, "i",
|
|
378
|
+
i0.ɵɵelement(10, "i", 85);
|
|
378
379
|
i0.ɵɵelementEnd()()();
|
|
379
|
-
i0.ɵɵelementStart(11, "div",
|
|
380
|
+
i0.ɵɵelementStart(11, "div", 86)(12, "div", 87)(13, "label", 88);
|
|
380
381
|
i0.ɵɵtext(14, "Session Name");
|
|
381
382
|
i0.ɵɵelementEnd();
|
|
382
|
-
i0.ɵɵelementStart(15, "input",
|
|
383
|
+
i0.ɵɵelementStart(15, "input", 89);
|
|
383
384
|
i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_8_Template_input_ngModelChange_15_listener($event) { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.sessionName, $event) || (ctx_r1.newSessionOptions.sessionName = $event); return i0.ɵɵresetView($event); });
|
|
384
385
|
i0.ɵɵelementEnd();
|
|
385
|
-
i0.ɵɵelementStart(16, "div",
|
|
386
|
+
i0.ɵɵelementStart(16, "div", 90);
|
|
386
387
|
i0.ɵɵtext(17, "A descriptive name to identify this logging session");
|
|
387
388
|
i0.ɵɵelementEnd()();
|
|
388
|
-
i0.ɵɵelementStart(18, "div",
|
|
389
|
+
i0.ɵɵelementStart(18, "div", 87)(19, "label", 91);
|
|
389
390
|
i0.ɵɵtext(20, "File Name");
|
|
390
391
|
i0.ɵɵelementEnd();
|
|
391
|
-
i0.ɵɵelementStart(21, "input",
|
|
392
|
+
i0.ɵɵelementStart(21, "input", 92);
|
|
392
393
|
i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_8_Template_input_ngModelChange_21_listener($event) { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.fileName, $event) || (ctx_r1.newSessionOptions.fileName = $event); return i0.ɵɵresetView($event); });
|
|
393
394
|
i0.ɵɵelementEnd();
|
|
394
|
-
i0.ɵɵelementStart(22, "div",
|
|
395
|
+
i0.ɵɵelementStart(22, "div", 93);
|
|
395
396
|
i0.ɵɵtext(23, "The SQL log will be saved to this file");
|
|
396
397
|
i0.ɵɵelementEnd()();
|
|
397
|
-
i0.ɵɵelementStart(24, "div",
|
|
398
|
+
i0.ɵɵelementStart(24, "div", 87)(25, "label", 94);
|
|
398
399
|
i0.ɵɵtext(26, "Statement Types");
|
|
399
400
|
i0.ɵɵelementEnd();
|
|
400
|
-
i0.ɵɵelementStart(27, "select",
|
|
401
|
+
i0.ɵɵelementStart(27, "select", 95);
|
|
401
402
|
i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_8_Template_select_ngModelChange_27_listener($event) { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.statementTypes, $event) || (ctx_r1.newSessionOptions.statementTypes = $event); return i0.ɵɵresetView($event); });
|
|
402
|
-
i0.ɵɵrepeaterCreate(28, SqlLoggingComponent_Conditional_8_For_29_Template, 2, 2, "option",
|
|
403
|
+
i0.ɵɵrepeaterCreate(28, SqlLoggingComponent_Conditional_8_For_29_Template, 2, 2, "option", 96, _forTrack1);
|
|
403
404
|
i0.ɵɵelementEnd();
|
|
404
|
-
i0.ɵɵelementStart(30, "div",
|
|
405
|
+
i0.ɵɵelementStart(30, "div", 97);
|
|
405
406
|
i0.ɵɵtext(31, "Filter which types of SQL statements to capture");
|
|
406
407
|
i0.ɵɵelementEnd()();
|
|
407
|
-
i0.ɵɵelementStart(32, "div",
|
|
408
|
+
i0.ɵɵelementStart(32, "div", 98)(33, "label", 70)(34, "input", 99);
|
|
408
409
|
i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_8_Template_input_ngModelChange_34_listener($event) { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.filterToCurrentUser, $event) || (ctx_r1.newSessionOptions.filterToCurrentUser = $event); return i0.ɵɵresetView($event); });
|
|
409
410
|
i0.ɵɵelementEnd();
|
|
410
411
|
i0.ɵɵtext(35, " Filter to my SQL statements only ");
|
|
411
412
|
i0.ɵɵelementEnd();
|
|
412
|
-
i0.ɵɵelementStart(36, "label",
|
|
413
|
+
i0.ɵɵelementStart(36, "label", 70)(37, "input", 99);
|
|
413
414
|
i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_8_Template_input_ngModelChange_37_listener($event) { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.formatAsMigration, $event) || (ctx_r1.newSessionOptions.formatAsMigration = $event); return i0.ɵɵresetView($event); });
|
|
414
415
|
i0.ɵɵelementEnd();
|
|
415
416
|
i0.ɵɵtext(38, " Format as migration file ");
|
|
416
417
|
i0.ɵɵelementEnd();
|
|
417
|
-
i0.ɵɵelementStart(39, "label",
|
|
418
|
+
i0.ɵɵelementStart(39, "label", 70)(40, "input", 99);
|
|
418
419
|
i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_8_Template_input_ngModelChange_40_listener($event) { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.prettyPrint, $event) || (ctx_r1.newSessionOptions.prettyPrint = $event); return i0.ɵɵresetView($event); });
|
|
419
420
|
i0.ɵɵelementEnd();
|
|
420
421
|
i0.ɵɵtext(41, " Pretty print SQL statements ");
|
|
421
422
|
i0.ɵɵelementEnd()();
|
|
422
|
-
i0.ɵɵelementStart(42, "div",
|
|
423
|
-
i0.ɵɵelement(44, "i",
|
|
423
|
+
i0.ɵɵelementStart(42, "div", 100)(43, "h4", 101);
|
|
424
|
+
i0.ɵɵelement(44, "i", 102);
|
|
424
425
|
i0.ɵɵtext(45, " SQL Pattern Filtering ");
|
|
425
426
|
i0.ɵɵelementEnd();
|
|
426
|
-
i0.ɵɵelementStart(46, "div",
|
|
427
|
+
i0.ɵɵelementStart(46, "div", 87)(47, "label", 103);
|
|
427
428
|
i0.ɵɵtext(48, "Filter Mode");
|
|
428
429
|
i0.ɵɵelementEnd();
|
|
429
|
-
i0.ɵɵelementStart(49, "select",
|
|
430
|
+
i0.ɵɵelementStart(49, "select", 104);
|
|
430
431
|
i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_8_Template_select_ngModelChange_49_listener($event) { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.filterType, $event) || (ctx_r1.newSessionOptions.filterType = $event); return i0.ɵɵresetView($event); });
|
|
431
|
-
i0.ɵɵrepeaterCreate(50, SqlLoggingComponent_Conditional_8_For_51_Template, 2, 2, "option",
|
|
432
|
+
i0.ɵɵrepeaterCreate(50, SqlLoggingComponent_Conditional_8_For_51_Template, 2, 2, "option", 96, _forTrack1);
|
|
432
433
|
i0.ɵɵelementEnd();
|
|
433
|
-
i0.ɵɵelementStart(52, "div",
|
|
434
|
+
i0.ɵɵelementStart(52, "div", 105);
|
|
434
435
|
i0.ɵɵtext(53, "Exclude: Skip SQL matching any pattern. Include: Only log SQL matching a pattern.");
|
|
435
436
|
i0.ɵɵelementEnd()();
|
|
436
|
-
i0.ɵɵelementStart(54, "div",
|
|
437
|
+
i0.ɵɵelementStart(54, "div", 87)(55, "label", 106);
|
|
437
438
|
i0.ɵɵtext(56, "Filter Patterns (one per line or comma-separated)");
|
|
438
439
|
i0.ɵɵelementEnd();
|
|
439
|
-
i0.ɵɵelementStart(57, "textarea",
|
|
440
|
+
i0.ɵɵelementStart(57, "textarea", 107);
|
|
440
441
|
i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_8_Template_textarea_ngModelChange_57_listener($event) { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.filterPatterns, $event) || (ctx_r1.newSessionOptions.filterPatterns = $event); return i0.ɵɵresetView($event); });
|
|
441
442
|
i0.ɵɵelementEnd();
|
|
442
|
-
i0.ɵɵelementStart(58, "div",
|
|
443
|
+
i0.ɵɵelementStart(58, "div", 108);
|
|
443
444
|
i0.ɵɵtext(59, "Supports wildcards (*) and regex (/pattern/flags). Example: *spCreate* matches any SP starting with spCreate.");
|
|
444
445
|
i0.ɵɵelementEnd()()();
|
|
445
|
-
i0.ɵɵelementStart(60, "div",
|
|
446
|
-
i0.ɵɵelement(62, "i",
|
|
446
|
+
i0.ɵɵelementStart(60, "div", 100)(61, "h4", 101);
|
|
447
|
+
i0.ɵɵelement(62, "i", 109);
|
|
447
448
|
i0.ɵɵtext(63, " Advanced Options ");
|
|
448
449
|
i0.ɵɵelementEnd();
|
|
449
|
-
i0.ɵɵconditionalCreate(64, SqlLoggingComponent_Conditional_8_Conditional_64_Template, 6, 1, "div",
|
|
450
|
-
i0.ɵɵelementStart(65, "div",
|
|
450
|
+
i0.ɵɵconditionalCreate(64, SqlLoggingComponent_Conditional_8_Conditional_64_Template, 6, 1, "div", 87);
|
|
451
|
+
i0.ɵɵelementStart(65, "div", 110)(66, "label", 70)(67, "input", 99);
|
|
451
452
|
i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_8_Template_input_ngModelChange_67_listener($event) { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.verboseOutput, $event) || (ctx_r1.newSessionOptions.verboseOutput = $event); return i0.ɵɵresetView($event); });
|
|
452
453
|
i0.ɵɵelementEnd();
|
|
453
454
|
i0.ɵɵtext(68, " Verbose Debug Output ");
|
|
454
455
|
i0.ɵɵelementEnd();
|
|
455
|
-
i0.ɵɵelementStart(69, "div",
|
|
456
|
+
i0.ɵɵelementStart(69, "div", 111);
|
|
456
457
|
i0.ɵɵtext(70, "Log detailed filter decisions to server console (for debugging).");
|
|
457
458
|
i0.ɵɵelementEnd()()()();
|
|
458
|
-
i0.ɵɵelementStart(71, "div",
|
|
459
|
+
i0.ɵɵelementStart(71, "div", 112)(72, "button", 113);
|
|
459
460
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_8_Template_button_click_72_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.startNewSession()); });
|
|
460
461
|
i0.ɵɵconditionalCreate(73, SqlLoggingComponent_Conditional_8_Conditional_73_Template, 2, 0)(74, SqlLoggingComponent_Conditional_8_Conditional_74_Template, 2, 0);
|
|
461
462
|
i0.ɵɵelementEnd();
|
|
462
|
-
i0.ɵɵelementStart(75, "button",
|
|
463
|
+
i0.ɵɵelementStart(75, "button", 114);
|
|
463
464
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_8_Template_button_click_75_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.showStartSessionDialog = false); });
|
|
464
465
|
i0.ɵɵtext(76, "Cancel");
|
|
465
466
|
i0.ɵɵelementEnd()()()();
|
|
@@ -523,7 +524,7 @@ function SqlLoggingComponent_Conditional_9_Conditional_10_Template(rf, ctx) { if
|
|
|
523
524
|
i0.ɵɵtext(2);
|
|
524
525
|
i0.ɵɵelementEnd();
|
|
525
526
|
i0.ɵɵtext(3, "? ");
|
|
526
|
-
i0.ɵɵelementStart(4, "span",
|
|
527
|
+
i0.ɵɵelementStart(4, "span", 129);
|
|
527
528
|
i0.ɵɵtext(5);
|
|
528
529
|
i0.ɵɵelementEnd();
|
|
529
530
|
} if (rf & 2) {
|
|
@@ -534,7 +535,7 @@ function SqlLoggingComponent_Conditional_9_Conditional_10_Template(rf, ctx) { if
|
|
|
534
535
|
i0.ɵɵtextInterpolate1(" This session has captured ", ctx_r1.sessionToStop.statementCount, " SQL statements. ");
|
|
535
536
|
} }
|
|
536
537
|
function SqlLoggingComponent_Conditional_9_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
537
|
-
i0.ɵɵelement(0, "i",
|
|
538
|
+
i0.ɵɵelement(0, "i", 118);
|
|
538
539
|
i0.ɵɵtext(1, " Stopping... ");
|
|
539
540
|
} }
|
|
540
541
|
function SqlLoggingComponent_Conditional_9_Conditional_14_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
@@ -544,7 +545,7 @@ function SqlLoggingComponent_Conditional_9_Conditional_14_Conditional_2_Template
|
|
|
544
545
|
i0.ɵɵtext(0, " Stop Session ");
|
|
545
546
|
} }
|
|
546
547
|
function SqlLoggingComponent_Conditional_9_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
547
|
-
i0.ɵɵelement(0, "i",
|
|
548
|
+
i0.ɵɵelement(0, "i", 54);
|
|
548
549
|
i0.ɵɵconditionalCreate(1, SqlLoggingComponent_Conditional_9_Conditional_14_Conditional_1_Template, 1, 0)(2, SqlLoggingComponent_Conditional_9_Conditional_14_Conditional_2_Template, 1, 0);
|
|
549
550
|
} if (rf & 2) {
|
|
550
551
|
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
@@ -553,24 +554,24 @@ function SqlLoggingComponent_Conditional_9_Conditional_14_Template(rf, ctx) { if
|
|
|
553
554
|
} }
|
|
554
555
|
function SqlLoggingComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
555
556
|
const _r14 = i0.ɵɵgetCurrentView();
|
|
556
|
-
i0.ɵɵelementStart(0, "div",
|
|
557
|
+
i0.ɵɵelementStart(0, "div", 119);
|
|
557
558
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_9_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.cancelStopConfirm()); });
|
|
558
|
-
i0.ɵɵelementStart(1, "div",
|
|
559
|
+
i0.ɵɵelementStart(1, "div", 120);
|
|
559
560
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_9_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r14); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
560
|
-
i0.ɵɵelementStart(2, "div",
|
|
561
|
-
i0.ɵɵelement(3, "i",
|
|
561
|
+
i0.ɵɵelementStart(2, "div", 121);
|
|
562
|
+
i0.ɵɵelement(3, "i", 122);
|
|
562
563
|
i0.ɵɵelementEnd();
|
|
563
|
-
i0.ɵɵelementStart(4, "div",
|
|
564
|
+
i0.ɵɵelementStart(4, "div", 123)(5, "h3", 124);
|
|
564
565
|
i0.ɵɵconditionalCreate(6, SqlLoggingComponent_Conditional_9_Conditional_6_Template, 1, 0)(7, SqlLoggingComponent_Conditional_9_Conditional_7_Template, 1, 0);
|
|
565
566
|
i0.ɵɵelementEnd();
|
|
566
|
-
i0.ɵɵelementStart(8, "p",
|
|
567
|
+
i0.ɵɵelementStart(8, "p", 125);
|
|
567
568
|
i0.ɵɵconditionalCreate(9, SqlLoggingComponent_Conditional_9_Conditional_9_Template, 4, 1)(10, SqlLoggingComponent_Conditional_9_Conditional_10_Template, 6, 2);
|
|
568
569
|
i0.ɵɵelementEnd()();
|
|
569
|
-
i0.ɵɵelementStart(11, "div",
|
|
570
|
+
i0.ɵɵelementStart(11, "div", 126)(12, "button", 127);
|
|
570
571
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_9_Template_button_click_12_listener() { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.confirmStopSession()); });
|
|
571
572
|
i0.ɵɵconditionalCreate(13, SqlLoggingComponent_Conditional_9_Conditional_13_Template, 2, 0)(14, SqlLoggingComponent_Conditional_9_Conditional_14_Template, 3, 1);
|
|
572
573
|
i0.ɵɵelementEnd();
|
|
573
|
-
i0.ɵɵelementStart(15, "button",
|
|
574
|
+
i0.ɵɵelementStart(15, "button", 128);
|
|
574
575
|
i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_9_Template_button_click_15_listener() { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.cancelStopConfirm()); });
|
|
575
576
|
i0.ɵɵtext(16, "Cancel");
|
|
576
577
|
i0.ɵɵelementEnd()()()();
|
|
@@ -1245,11 +1246,11 @@ let SqlLoggingComponent = class SqlLoggingComponent extends BaseDashboard {
|
|
|
1245
1246
|
static ɵfac = function SqlLoggingComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SqlLoggingComponent)(i0.ɵɵdirectiveInject(i1.SharedService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
|
|
1246
1247
|
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SqlLoggingComponent, selectors: [["mj-sql-logging"]], hostBindings: function SqlLoggingComponent_HostBindings(rf, ctx) { if (rf & 1) {
|
|
1247
1248
|
i0.ɵɵlistener("keydown.escape", function SqlLoggingComponent_keydown_escape_HostBindingHandler() { return ctx.onEscapeKey(); }, i0.ɵɵresolveDocument);
|
|
1248
|
-
} }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 10, vars: 7, consts: [["Role", "region", "AriaLabel", "SQL logging actions", "Title", "SQL Logging", "Subtitle", "Capture and download SQL statements executed by the API"], ["actions", ""], [3, "Clicked", "Loading"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "disabled"], [1, "stats-toggle-container"], ["role", "status", "aria-live", "polite", "aria-busy", "true", 1, "loading-container"], [1, "content-area"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "start-session-dialog-title", 1, "modal-backdrop"], ["role", "alertdialog", "aria-modal", "true", "aria-labelledby", "stop-dialog-title", "aria-describedby", "stop-dialog-desc", 1, "modal-backdrop"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-play"], ["aria-controls", "stats-grid-content", 1, "stats-toggle-button", 3, "click"], [1, "stats-toggle-left"], [1, "stats-toggle-icon"], ["aria-hidden", "true", 1, "fa-solid", "fa-chart-line"], [1, "stats-toggle-text"], [1, "stats-toggle-title"], [1, "stats-toggle-subtitle"], ["aria-hidden", "true", 1, "fa-solid", "fa-chevron-down", "stats-toggle-arrow"], ["id", "stats-grid-content", "role", "region", "aria-label", "SQL logging statistics", 1, "stats-grid"], [1, "stat-card"], [1, "stat-icon", "stat-icon-status"], ["aria-hidden", "true", 1, "fa-solid", "fa-power-off"], [1, "stat-content"], [1, "stat-value"], [1, "stat-label"], [1, "stat-icon", "stat-icon-active"], ["aria-hidden", "true", 1, "fa-solid", "fa-play-circle"], [1, "stat-icon", "stat-icon-limit"], ["aria-hidden", "true", 1, "fa-solid", "fa-gauge-high"], [1, "stat-icon", "stat-icon-total"], ["aria-hidden", "true", 1, "fa-solid", "fa-database"], ["text", "Loading SQL logging configuration...", "size", "medium"], ["role", "alert", 1, "empty-state"], ["role", "status", 1, "empty-state"], [1, "sessions-layout"], ["aria-hidden", "true", 1, "fa-solid", "fa-lock", "empty-icon"], [1, "empty-text"], [1, "empty-subtext"], ["mjButton", "", "variant", "secondary", "size", "md", "aria-label", "Refresh user permissions", 2, "margin-top", "1rem", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-sync"], ["aria-hidden", "true", 1, "fa-solid", "fa-exclamation-triangle", "empty-icon", "warning"], [1, "info-box"], ["aria-hidden", "true", 1, "fa-solid", "fa-file-code", "empty-icon"], ["mjButton", "", "variant", "primary", "size", "md", "aria-label", "Start a new SQL logging session", 2, "margin-top", "1rem", 3, "click"], ["role", "region", "aria-label", "Active sessions list", 1, "sessions-panel"], [1, "panel-header"], [1, "panel-title"], ["mjButton", "", "variant", "danger", "size", "sm", "aria-label", "Stop all active sessions", 3, "disabled"], ["role", "list", "aria-label", "SQL logging sessions", 1, "sessions-list"], ["role", "listitem", "tabindex", "0", 1, "session-card", 3, "selected"], ["role", "region", "aria-label", "SQL log viewer", 1, "log-viewer-panel"], ["mjButton", "", "variant", "danger", "size", "sm", "aria-label", "Stop all active sessions", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-stop"], ["role", "listitem", "tabindex", "0", 1, "session-card", 3, "click", "keydown.enter", "keydown.space"], [1, "session-header"], [1, "session-info"], [1, "session-title"], [1, "session-meta"], [1, "meta-item"], ["aria-hidden", "true", 1, "fa-solid", "fa-clock"], ["mjButton", "", "variant", "danger", "size", "sm", 3, "click"], [1, "session-badges"], [1, "badge", "badge-user"], [1, "badge", "badge-migration"], [1, "badge", "badge-type"], ["aria-hidden", "true", 1, "fa-solid", "fa-user"], ["aria-hidden", "true", 1, "fa-solid", "fa-code-branch"], [1, "panel-actions"], [1, "checkbox-label"], ["type", "checkbox", "aria-label", "Enable auto-refresh", 3, "ngModelChange", "ngModel"], ["mjButton", "", "variant", "flat", "size", "sm", "aria-label", "Refresh log content", 3, "click"], ["mjButton", "", "variant", "flat", "size", "sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid"], [1, "log-content"], ["aria-label", "SQL log content", 2, "height", "100%", 3, "value", "readonly", "disabled", "language", "setup", "lineWrapping", "highlightWhitespace"], ["aria-hidden", "true", 1, "fa-solid", "fa-arrow-left", "empty-icon"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "start-session-dialog-title", 1, "modal-backdrop", 3, "click"], [1, "modal-dialog", "modal-large", 3, "click"], [1, "modal-header"], ["id", "start-session-dialog-title", 1, "modal-title"], [1, "modal-header-actions"], ["type", "button", 1, "modal-action-btn", 3, "click"], ["aria-label", "Close dialog", 1, "modal-close", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-times"], [1, "modal-body"], [1, "form-group"], ["for", "session-name", 1, "form-label"], ["type", "text", "id", "session-name", "placeholder", "Enter a descriptive name for this session", "aria-describedby", "session-name-hint", 1, "form-input", 3, "ngModelChange", "ngModel"], ["id", "session-name-hint", 1, "form-hint"], ["for", "file-name", 1, "form-label"], ["type", "text", "id", "file-name", "placeholder", "sql-log-2024-01-01.sql", "aria-describedby", "file-name-hint", 1, "form-input", 3, "ngModelChange", "ngModel"], ["id", "file-name-hint", 1, "form-hint"], ["for", "statement-types", 1, "form-label"], ["id", "statement-types", "aria-describedby", "statement-types-hint", 1, "form-select", 3, "ngModelChange", "ngModel"], [3, "value"], ["id", "statement-types-hint", 1, "form-hint"], ["role", "group", "aria-label", "Session options", 1, "form-checkboxes"], ["type", "checkbox", 3, "ngModelChange", "ngModel"], [1, "form-section"], [1, "form-section-title"], ["aria-hidden", "true", 1, "fa-solid", "fa-filter"], ["for", "filter-type", 1, "form-label"], ["id", "filter-type", "aria-describedby", "filter-type-hint", 1, "form-select", 3, "ngModelChange", "ngModel"], ["id", "filter-type-hint", 1, "form-hint"], ["for", "filter-patterns", 1, "form-label"], ["id", "filter-patterns", "rows", "4", "placeholder", "Examples:\n*AIPrompt*\n/spCreate.*Run/i\nSELECT * FROM vwMetadata", "aria-describedby", "filter-patterns-hint", 1, "form-textarea", 3, "ngModelChange", "ngModel"], ["id", "filter-patterns-hint", 1, "form-hint"], ["aria-hidden", "true", 1, "fa-solid", "fa-cog"], [1, "form-checkboxes"], [1, "form-hint", 2, "margin-left", "1.75rem"], [1, "modal-footer"], ["mjButton", "", "variant", "primary", "size", "md", "aria-label", "Start the SQL logging session", 3, "click", "disabled"], ["mjButton", "", "variant", "secondary", "size", "md", "aria-label", "Cancel and close dialog", 3, "click"], ["for", "default-schema", 1, "form-label"], ["type", "text", "id", "default-schema", "placeholder", "__mj", "aria-describedby", "default-schema-hint", 1, "form-input", 3, "ngModelChange", "ngModel"], ["id", "default-schema-hint", 1, "form-hint"], ["aria-hidden", "true", 1, "fa-solid", "fa-spinner", "fa-spin"], ["role", "alertdialog", "aria-modal", "true", "aria-labelledby", "stop-dialog-title", "aria-describedby", "stop-dialog-desc", 1, "modal-backdrop", 3, "click"], [1, "confirm-dialog", 3, "click"], [1, "confirm-dialog-icon"], ["aria-hidden", "true", 1, "fa-solid", "fa-exclamation-triangle"], [1, "confirm-dialog-content"], ["id", "stop-dialog-title", 1, "confirm-dialog-title"], ["id", "stop-dialog-desc", 1, "confirm-dialog-message"], [1, "confirm-dialog-actions"], ["mjButton", "", "variant", "danger", "size", "md", 3, "click", "disabled"], ["mjButton", "", "variant", "secondary", "size", "md", "aria-label", "Cancel and keep sessions running", 3, "click", "disabled"], [1, "confirm-dialog-details"]], template: function SqlLoggingComponent_Template(rf, ctx) { if (rf & 1) {
|
|
1249
|
+
} }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 10, vars: 7, consts: [["Role", "region", "AriaLabel", "SQL logging actions", "Title", "SQL Logging", "Subtitle", "Capture and download SQL statements executed by the API"], ["actions", ""], [3, "Clicked", "Loading"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "disabled"], [1, "stats-toggle-container"], ["role", "status", "aria-live", "polite", "aria-busy", "true", 1, "loading-container"], [1, "content-area"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "start-session-dialog-title", 1, "modal-backdrop"], ["role", "alertdialog", "aria-modal", "true", "aria-labelledby", "stop-dialog-title", "aria-describedby", "stop-dialog-desc", 1, "modal-backdrop"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-play"], [1, "mj-action-label"], ["aria-controls", "stats-grid-content", 1, "stats-toggle-button", 3, "click"], [1, "stats-toggle-left"], [1, "stats-toggle-icon"], ["aria-hidden", "true", 1, "fa-solid", "fa-chart-line"], [1, "stats-toggle-text"], [1, "stats-toggle-title"], [1, "stats-toggle-subtitle"], ["aria-hidden", "true", 1, "fa-solid", "fa-chevron-down", "stats-toggle-arrow"], ["id", "stats-grid-content", "role", "region", "aria-label", "SQL logging statistics", 1, "stats-grid"], [1, "stat-card"], [1, "stat-icon", "stat-icon-status"], ["aria-hidden", "true", 1, "fa-solid", "fa-power-off"], [1, "stat-content"], [1, "stat-value"], [1, "stat-label"], [1, "stat-icon", "stat-icon-active"], ["aria-hidden", "true", 1, "fa-solid", "fa-play-circle"], [1, "stat-icon", "stat-icon-limit"], ["aria-hidden", "true", 1, "fa-solid", "fa-gauge-high"], [1, "stat-icon", "stat-icon-total"], ["aria-hidden", "true", 1, "fa-solid", "fa-database"], ["text", "Loading SQL logging configuration...", "size", "medium"], ["role", "alert", 1, "empty-state"], ["role", "status", 1, "empty-state"], [1, "sessions-layout"], ["aria-hidden", "true", 1, "fa-solid", "fa-lock", "empty-icon"], [1, "empty-text"], [1, "empty-subtext"], ["mjButton", "", "variant", "secondary", "size", "md", "aria-label", "Refresh user permissions", 2, "margin-top", "1rem", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-sync"], ["aria-hidden", "true", 1, "fa-solid", "fa-exclamation-triangle", "empty-icon", "warning"], [1, "info-box"], ["aria-hidden", "true", 1, "fa-solid", "fa-file-code", "empty-icon"], ["mjButton", "", "variant", "primary", "size", "md", "aria-label", "Start a new SQL logging session", 2, "margin-top", "1rem", 3, "click"], ["role", "region", "aria-label", "Active sessions list", 1, "sessions-panel"], [1, "panel-header"], [1, "panel-title"], ["mjButton", "", "variant", "danger", "size", "sm", "aria-label", "Stop all active sessions", 3, "disabled"], ["role", "list", "aria-label", "SQL logging sessions", 1, "sessions-list"], ["role", "listitem", "tabindex", "0", 1, "session-card", 3, "selected"], ["role", "region", "aria-label", "SQL log viewer", 1, "log-viewer-panel"], ["mjButton", "", "variant", "danger", "size", "sm", "aria-label", "Stop all active sessions", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-stop"], ["role", "listitem", "tabindex", "0", 1, "session-card", 3, "click", "keydown.enter", "keydown.space"], [1, "session-header"], [1, "session-info"], [1, "session-title"], [1, "session-meta"], [1, "meta-item"], ["aria-hidden", "true", 1, "fa-solid", "fa-clock"], ["mjButton", "", "variant", "danger", "size", "sm", 3, "click"], [1, "session-badges"], [1, "badge", "badge-user"], [1, "badge", "badge-migration"], [1, "badge", "badge-type"], ["aria-hidden", "true", 1, "fa-solid", "fa-user"], ["aria-hidden", "true", 1, "fa-solid", "fa-code-branch"], [1, "panel-actions"], [1, "checkbox-label"], ["type", "checkbox", "aria-label", "Enable auto-refresh", 3, "ngModelChange", "ngModel"], ["mjButton", "", "variant", "flat", "size", "sm", "aria-label", "Refresh log content", 3, "click"], ["mjButton", "", "variant", "flat", "size", "sm", 3, "click"], ["aria-hidden", "true", 1, "fa-solid"], [1, "log-content"], ["aria-label", "SQL log content", 2, "height", "100%", 3, "value", "readonly", "disabled", "language", "setup", "lineWrapping", "highlightWhitespace"], ["aria-hidden", "true", 1, "fa-solid", "fa-arrow-left", "empty-icon"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "start-session-dialog-title", 1, "modal-backdrop", 3, "click"], [1, "modal-dialog", "modal-large", 3, "click"], [1, "modal-header"], ["id", "start-session-dialog-title", 1, "modal-title"], [1, "modal-header-actions"], ["type", "button", 1, "modal-action-btn", 3, "click"], ["aria-label", "Close dialog", 1, "modal-close", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-times"], [1, "modal-body"], [1, "form-group"], ["for", "session-name", 1, "form-label"], ["type", "text", "id", "session-name", "placeholder", "Enter a descriptive name for this session", "aria-describedby", "session-name-hint", 1, "form-input", 3, "ngModelChange", "ngModel"], ["id", "session-name-hint", 1, "form-hint"], ["for", "file-name", 1, "form-label"], ["type", "text", "id", "file-name", "placeholder", "sql-log-2024-01-01.sql", "aria-describedby", "file-name-hint", 1, "form-input", 3, "ngModelChange", "ngModel"], ["id", "file-name-hint", 1, "form-hint"], ["for", "statement-types", 1, "form-label"], ["id", "statement-types", "aria-describedby", "statement-types-hint", 1, "form-select", 3, "ngModelChange", "ngModel"], [3, "value"], ["id", "statement-types-hint", 1, "form-hint"], ["role", "group", "aria-label", "Session options", 1, "form-checkboxes"], ["type", "checkbox", 3, "ngModelChange", "ngModel"], [1, "form-section"], [1, "form-section-title"], ["aria-hidden", "true", 1, "fa-solid", "fa-filter"], ["for", "filter-type", 1, "form-label"], ["id", "filter-type", "aria-describedby", "filter-type-hint", 1, "form-select", 3, "ngModelChange", "ngModel"], ["id", "filter-type-hint", 1, "form-hint"], ["for", "filter-patterns", 1, "form-label"], ["id", "filter-patterns", "rows", "4", "placeholder", "Examples:\n*AIPrompt*\n/spCreate.*Run/i\nSELECT * FROM vwMetadata", "aria-describedby", "filter-patterns-hint", 1, "form-textarea", 3, "ngModelChange", "ngModel"], ["id", "filter-patterns-hint", 1, "form-hint"], ["aria-hidden", "true", 1, "fa-solid", "fa-cog"], [1, "form-checkboxes"], [1, "form-hint", 2, "margin-left", "1.75rem"], [1, "modal-footer"], ["mjButton", "", "variant", "primary", "size", "md", "aria-label", "Start the SQL logging session", 3, "click", "disabled"], ["mjButton", "", "variant", "secondary", "size", "md", "aria-label", "Cancel and close dialog", 3, "click"], ["for", "default-schema", 1, "form-label"], ["type", "text", "id", "default-schema", "placeholder", "__mj", "aria-describedby", "default-schema-hint", 1, "form-input", 3, "ngModelChange", "ngModel"], ["id", "default-schema-hint", 1, "form-hint"], ["aria-hidden", "true", 1, "fa-solid", "fa-spinner", "fa-spin"], ["role", "alertdialog", "aria-modal", "true", "aria-labelledby", "stop-dialog-title", "aria-describedby", "stop-dialog-desc", 1, "modal-backdrop", 3, "click"], [1, "confirm-dialog", 3, "click"], [1, "confirm-dialog-icon"], ["aria-hidden", "true", 1, "fa-solid", "fa-exclamation-triangle"], [1, "confirm-dialog-content"], ["id", "stop-dialog-title", 1, "confirm-dialog-title"], ["id", "stop-dialog-desc", 1, "confirm-dialog-message"], [1, "confirm-dialog-actions"], ["mjButton", "", "variant", "danger", "size", "md", 3, "click", "disabled"], ["mjButton", "", "variant", "secondary", "size", "md", "aria-label", "Cancel and keep sessions running", 3, "click", "disabled"], [1, "confirm-dialog-details"]], template: function SqlLoggingComponent_Template(rf, ctx) { if (rf & 1) {
|
|
1249
1250
|
i0.ɵɵelementStart(0, "mj-page-header-interior", 0)(1, "div", 1)(2, "mj-refresh-button", 2);
|
|
1250
1251
|
i0.ɵɵlistener("Clicked", function SqlLoggingComponent_Template_mj_refresh_button_Clicked_2_listener() { return ctx.loadActiveSessions(); });
|
|
1251
1252
|
i0.ɵɵelementEnd();
|
|
1252
|
-
i0.ɵɵconditionalCreate(3, SqlLoggingComponent_Conditional_3_Template,
|
|
1253
|
+
i0.ɵɵconditionalCreate(3, SqlLoggingComponent_Conditional_3_Template, 4, 2, "button", 3);
|
|
1253
1254
|
i0.ɵɵelementEnd()();
|
|
1254
1255
|
i0.ɵɵelementStart(4, "mj-page-body-interior");
|
|
1255
1256
|
i0.ɵɵconditionalCreate(5, SqlLoggingComponent_Conditional_5_Template, 44, 13, "div", 4);
|
|
@@ -1281,7 +1282,7 @@ SqlLoggingComponent = __decorate([
|
|
|
1281
1282
|
export { SqlLoggingComponent };
|
|
1282
1283
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SqlLoggingComponent, [{
|
|
1283
1284
|
type: Component,
|
|
1284
|
-
args: [{ standalone: false, selector: 'mj-sql-logging', template: "<!--\n Interior chrome \u2014 sub-page of Admin's Monitoring left-nav shell. Single section\n (no L2 tab nav) so [toolbar] is empty; actions land in [actions]. See Section 10\n of plans/explorer-chrome-conventions.md.\n-->\n<mj-page-header-interior\n Role=\"region\"\n AriaLabel=\"SQL logging actions\"\n Title=\"SQL Logging\"\n Subtitle=\"Capture and download SQL statements executed by the API\">\n <div actions>\n <mj-refresh-button\n [Loading]=\"loading\"\n (Clicked)=\"loadActiveSessions()\">\n </mj-refresh-button>\n @if (isOwner && configEnabled) {\n <button mjButton\n variant=\"primary\"\n size=\"sm\"\n [disabled]=\"loading || activeSessions.length >= (sqlLoggingConfig?.maxActiveSessions || 5)\"\n (click)=\"openStartSessionDialog()\"\n [attr.aria-label]=\"'Start new SQL logging session' + (activeSessions.length >= (sqlLoggingConfig?.maxActiveSessions || 5) ? ' (maximum sessions reached)' : '')\">\n <i class=\"fa-solid fa-play\" aria-hidden=\"true\"></i>\n Start New Session\n </button>\n }\n </div>\n</mj-page-header-interior>\n\n<mj-page-body-interior>\n\n <!-- Stats Cards with Toggle -->\n @if (isOwner && configEnabled) {\n <div class=\"stats-toggle-container\">\n <!-- Toggle Button -->\n <button\n class=\"stats-toggle-button\"\n [class.expanded]=\"showStats\"\n (click)=\"showStats = !showStats\"\n [attr.aria-expanded]=\"showStats\"\n aria-controls=\"stats-grid-content\"\n >\n <div class=\"stats-toggle-left\">\n <div class=\"stats-toggle-icon\">\n <i class=\"fa-solid fa-chart-line\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"stats-toggle-text\">\n <h3 class=\"stats-toggle-title\">Statistics</h3>\n <p class=\"stats-toggle-subtitle\">View session statistics and metrics</p>\n </div>\n </div>\n <i class=\"fa-solid fa-chevron-down stats-toggle-arrow\" aria-hidden=\"true\"></i>\n </button>\n\n <!-- Stats Grid (Collapsible) -->\n <div\n id=\"stats-grid-content\"\n class=\"stats-grid\"\n [class.visible]=\"showStats\"\n role=\"region\"\n aria-label=\"SQL logging statistics\"\n >\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-status\">\n <i class=\"fa-solid fa-power-off\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" [attr.aria-label]=\"'Status: ' + (configEnabled ? 'Enabled' : 'Disabled')\">\n {{ configEnabled ? \"Enabled\" : \"Disabled\" }}\n </div>\n <div class=\"stat-label\">Status</div>\n </div>\n </div>\n\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-active\">\n <i class=\"fa-solid fa-play-circle\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" [attr.aria-label]=\"activeSessions.length + ' active sessions'\">\n {{ activeSessions.length }}\n </div>\n <div class=\"stat-label\">Active Sessions</div>\n </div>\n </div>\n\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-limit\">\n <i class=\"fa-solid fa-gauge-high\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" [attr.aria-label]=\"(sqlLoggingConfig?.maxActiveSessions || 5) + ' maximum sessions allowed'\">\n {{ sqlLoggingConfig?.maxActiveSessions || 5 }}\n </div>\n <div class=\"stat-label\">Max Sessions</div>\n </div>\n </div>\n\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-total\">\n <i class=\"fa-solid fa-database\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" [attr.aria-label]=\"getTotalStatementCount() + ' total SQL statements captured'\">\n {{ getTotalStatementCount() }}\n </div>\n <div class=\"stat-label\">Total Statements</div>\n </div>\n </div>\n </div>\n </div>\n }\n\n <!-- Loading State -->\n @if (loading && !activeSessions.length) {\n <div class=\"loading-container\" role=\"status\" aria-live=\"polite\" aria-busy=\"true\">\n <mj-loading text=\"Loading SQL logging configuration...\" size=\"medium\"></mj-loading>\n </div>\n }\n\n <!-- Content Area -->\n @if (!loading || activeSessions.length > 0) {\n <div class=\"content-area\">\n @if (!isOwner) {\n <!-- Not authorized -->\n <div class=\"empty-state\" role=\"alert\">\n <i class=\"fa-solid fa-lock empty-icon\" aria-hidden=\"true\"></i>\n <p class=\"empty-text\">Access Denied</p>\n <p class=\"empty-subtext\">SQL logging requires Owner privileges. Please contact your system administrator for access.</p>\n <button mjButton variant=\"secondary\" size=\"md\" (click)=\"refreshUserPermissions()\" style=\"margin-top: 1rem\" aria-label=\"Refresh user permissions\">\n <i class=\"fa-solid fa-sync\" aria-hidden=\"true\"></i>\n Refresh Permissions\n </button>\n </div>\n } @else if (!configEnabled) {\n <!-- Not enabled in config -->\n <div class=\"empty-state\" role=\"alert\">\n <i class=\"fa-solid fa-exclamation-triangle empty-icon warning\" aria-hidden=\"true\"></i>\n <p class=\"empty-text\">SQL Logging Disabled</p>\n <p class=\"empty-subtext\">SQL logging is not enabled in the server configuration.</p>\n <div class=\"info-box\">\n <h4>To enable SQL logging:</h4>\n <ol>\n <li>Set <code>sqlLogging.enabled = true</code> in mj.config.cjs</li>\n <li>Restart the MJ API server</li>\n <li>Refresh this page</li>\n </ol>\n </div>\n </div>\n } @else if (activeSessions.length === 0) {\n <!-- No active sessions -->\n <div class=\"empty-state\" role=\"status\">\n <i class=\"fa-solid fa-file-code empty-icon\" aria-hidden=\"true\"></i>\n <p class=\"empty-text\">No Active Sessions</p>\n <p class=\"empty-subtext\">Start a new SQL logging session to begin capturing SQL statements.</p>\n <button mjButton variant=\"primary\" size=\"md\" (click)=\"openStartSessionDialog()\" style=\"margin-top: 1rem\" aria-label=\"Start a new SQL logging session\">\n <i class=\"fa-solid fa-play\" aria-hidden=\"true\"></i>\n Start New Session\n </button>\n </div>\n } @else {\n <!-- Sessions layout -->\n <div class=\"sessions-layout\">\n <!-- Sessions panel -->\n <div class=\"sessions-panel\" role=\"region\" aria-label=\"Active sessions list\">\n <div class=\"panel-header\">\n <h3 class=\"panel-title\">Active Sessions</h3>\n @if (activeSessions.length > 0) {\n <button mjButton\n variant=\"danger\"\n size=\"sm\"\n (click)=\"openStopAllSessionsConfirm()\"\n [disabled]=\"loading\"\n aria-label=\"Stop all active sessions\"\n >\n <i class=\"fa-solid fa-stop\" aria-hidden=\"true\"></i>\n Stop All\n </button>\n }\n </div>\n <div class=\"sessions-list\" role=\"list\" aria-label=\"SQL logging sessions\">\n @for (session of activeSessions; track session.id) {\n <div\n class=\"session-card\"\n [class.selected]=\"selectedSession?.id === session.id\"\n (click)=\"selectSession(session)\"\n role=\"listitem\"\n tabindex=\"0\"\n (keydown.enter)=\"selectSession(session)\"\n (keydown.space)=\"selectSession(session); $event.preventDefault()\"\n [attr.aria-label]=\"'Session: ' + session.sessionName + ', ' + session.statementCount + ' statements, duration ' + getSessionDuration(session.startTime)\"\n [attr.aria-selected]=\"selectedSession?.id === session.id\"\n >\n <div class=\"session-header\">\n <div class=\"session-info\">\n <h4 class=\"session-title\">{{ session.sessionName }}</h4>\n <div class=\"session-meta\">\n <span class=\"meta-item\">\n <i class=\"fa-solid fa-clock\" aria-hidden=\"true\"></i>\n {{ getSessionDuration(session.startTime) }}\n </span>\n <span class=\"meta-item\">\n <i class=\"fa-solid fa-database\" aria-hidden=\"true\"></i>\n {{ session.statementCount }} statements\n </span>\n </div>\n </div>\n <button mjButton\n variant=\"danger\"\n size=\"sm\"\n (click)=\"openStopSessionConfirm(session, $event)\"\n [attr.aria-label]=\"'Stop session ' + session.sessionName\"\n >\n <i class=\"fa-solid fa-stop\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <div class=\"session-badges\">\n @if (session.filterByUserId) {\n <span class=\"badge badge-user\">\n <i class=\"fa-solid fa-user\" aria-hidden=\"true\"></i>\n User Filtered\n </span>\n }\n @if (session.options?.formatAsMigration) {\n <span class=\"badge badge-migration\">\n <i class=\"fa-solid fa-code-branch\" aria-hidden=\"true\"></i>\n Migration\n </span>\n }\n <span class=\"badge badge-type\">\n {{ session.options?.statementTypes || \"both\" }}\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Log viewer panel -->\n <div class=\"log-viewer-panel\" [class.expanded]=\"isLogViewerExpanded\" role=\"region\" aria-label=\"SQL log viewer\">\n @if (selectedSession) {\n <div class=\"panel-header\">\n <h3 class=\"panel-title\">{{ selectedSession.sessionName }}</h3>\n <div class=\"panel-actions\">\n <label class=\"checkbox-label\">\n <input\n type=\"checkbox\"\n [(ngModel)]=\"autoRefresh\"\n aria-label=\"Enable auto-refresh\"\n />\n Auto-refresh\n </label>\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n (click)=\"loadSessionLog(selectedSession)\"\n aria-label=\"Refresh log content\"\n >\n <i class=\"fa-solid fa-sync\" [class.fa-spin]=\"loading\" aria-hidden=\"true\"></i>\n </button>\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n (click)=\"toggleLogViewerExpand()\"\n [attr.aria-label]=\"isLogViewerExpanded ? 'Collapse log viewer' : 'Expand log viewer to fullscreen'\"\n [attr.aria-expanded]=\"isLogViewerExpanded\"\n >\n <i class=\"fa-solid\" [class.fa-expand]=\"!isLogViewerExpanded\" [class.fa-compress]=\"isLogViewerExpanded\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n <div class=\"log-content\">\n <mj-code-editor\n [value]=\"logContent\"\n [readonly]=\"true\"\n [disabled]=\"true\"\n [language]=\"'sql'\"\n [setup]=\"'basic'\"\n [lineWrapping]=\"true\"\n [highlightWhitespace]=\"false\"\n style=\"height: 100%\"\n aria-label=\"SQL log content\"\n ></mj-code-editor>\n </div>\n } @else {\n <div class=\"empty-state\" role=\"status\">\n <i class=\"fa-solid fa-arrow-left empty-icon\" aria-hidden=\"true\"></i>\n <p class=\"empty-text\">Select a Session</p>\n <p class=\"empty-subtext\">Choose a session from the list to view its SQL log.</p>\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Start Session Dialog -->\n @if (showStartSessionDialog) {\n <div\n class=\"modal-backdrop\"\n (click)=\"showStartSessionDialog = false\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"start-session-dialog-title\"\n >\n <div class=\"modal-dialog modal-large\" [class.fullscreen]=\"isStartDialogFullscreen\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3 class=\"modal-title\" id=\"start-session-dialog-title\">\n <i class=\"fa-solid fa-play\" aria-hidden=\"true\"></i>\n Start SQL Logging Session\n </h3>\n <div class=\"modal-header-actions\">\n <button\n class=\"modal-action-btn\"\n (click)=\"toggleStartDialogFullscreen()\"\n [attr.aria-label]=\"isStartDialogFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'\"\n type=\"button\"\n >\n <i class=\"fa-solid\" [class.fa-compress]=\"isStartDialogFullscreen\" [class.fa-expand]=\"!isStartDialogFullscreen\" aria-hidden=\"true\"></i>\n </button>\n <button class=\"modal-close\" (click)=\"showStartSessionDialog = false\" aria-label=\"Close dialog\">\n <i class=\"fa-solid fa-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n <div class=\"modal-body\">\n <div class=\"form-group\">\n <label class=\"form-label\" for=\"session-name\">Session Name</label>\n <input\n type=\"text\"\n id=\"session-name\"\n class=\"form-input\"\n [(ngModel)]=\"newSessionOptions.sessionName\"\n placeholder=\"Enter a descriptive name for this session\"\n aria-describedby=\"session-name-hint\"\n />\n <div id=\"session-name-hint\" class=\"form-hint\">A descriptive name to identify this logging session</div>\n </div>\n\n <div class=\"form-group\">\n <label class=\"form-label\" for=\"file-name\">File Name</label>\n <input\n type=\"text\"\n id=\"file-name\"\n class=\"form-input\"\n [(ngModel)]=\"newSessionOptions.fileName\"\n placeholder=\"sql-log-2024-01-01.sql\"\n aria-describedby=\"file-name-hint\"\n />\n <div id=\"file-name-hint\" class=\"form-hint\">The SQL log will be saved to this file</div>\n </div>\n\n <div class=\"form-group\">\n <label class=\"form-label\" for=\"statement-types\">Statement Types</label>\n <select\n id=\"statement-types\"\n class=\"form-select\"\n [(ngModel)]=\"newSessionOptions.statementTypes\"\n aria-describedby=\"statement-types-hint\"\n >\n @for (option of statementTypeOptions; track option.value) {\n <option [value]=\"option.value\">{{ option.text }}</option>\n }\n </select>\n <div id=\"statement-types-hint\" class=\"form-hint\">Filter which types of SQL statements to capture</div>\n </div>\n\n <div class=\"form-checkboxes\" role=\"group\" aria-label=\"Session options\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" [(ngModel)]=\"newSessionOptions.filterToCurrentUser\" />\n Filter to my SQL statements only\n </label>\n\n\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" [(ngModel)]=\"newSessionOptions.formatAsMigration\" />\n Format as migration file\n </label>\n\n\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" [(ngModel)]=\"newSessionOptions.prettyPrint\" />\n Pretty print SQL statements\n </label>\n </div>\n\n <!-- SQL Pattern Filtering Section -->\n <div class=\"form-section\">\n <h4 class=\"form-section-title\">\n <i class=\"fa-solid fa-filter\" aria-hidden=\"true\"></i>\n SQL Pattern Filtering\n </h4>\n\n <div class=\"form-group\">\n <label class=\"form-label\" for=\"filter-type\">Filter Mode</label>\n <select\n id=\"filter-type\"\n class=\"form-select\"\n [(ngModel)]=\"newSessionOptions.filterType\"\n aria-describedby=\"filter-type-hint\"\n >\n @for (option of filterTypeOptions; track option.value) {\n <option [value]=\"option.value\">{{ option.text }}</option>\n }\n </select>\n <div id=\"filter-type-hint\" class=\"form-hint\">Exclude: Skip SQL matching any pattern. Include: Only log SQL matching a pattern.</div>\n </div>\n\n <div class=\"form-group\">\n <label class=\"form-label\" for=\"filter-patterns\">Filter Patterns (one per line or comma-separated)</label>\n <textarea\n id=\"filter-patterns\"\n class=\"form-textarea\"\n [(ngModel)]=\"newSessionOptions.filterPatterns\"\n rows=\"4\"\n placeholder=\"Examples: *AIPrompt* /spCreate.*Run/i SELECT * FROM vwMetadata\"\n aria-describedby=\"filter-patterns-hint\"\n ></textarea>\n <div id=\"filter-patterns-hint\" class=\"form-hint\">Supports wildcards (*) and regex (/pattern/flags). Example: *spCreate* matches any SP starting with spCreate.</div>\n </div>\n </div>\n\n <!-- Advanced Options Section -->\n <div class=\"form-section\">\n <h4 class=\"form-section-title\">\n <i class=\"fa-solid fa-cog\" aria-hidden=\"true\"></i>\n Advanced Options\n </h4>\n\n @if (newSessionOptions.formatAsMigration) {\n <div class=\"form-group\">\n <label class=\"form-label\" for=\"default-schema\">Default Schema Name</label>\n <input\n type=\"text\"\n id=\"default-schema\"\n class=\"form-input\"\n [(ngModel)]=\"newSessionOptions.defaultSchemaName\"\n placeholder=\"__mj\"\n aria-describedby=\"default-schema-hint\"\n />\n <div id=\"default-schema-hint\" class=\"form-hint\">Schema name to replace with ${flyway:defaultSchema} placeholder.</div>\n </div>\n }\n\n <div class=\"form-checkboxes\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" [(ngModel)]=\"newSessionOptions.verboseOutput\" />\n Verbose Debug Output\n </label>\n <div class=\"form-hint\" style=\"margin-left: 1.75rem\">Log detailed filter decisions to server console (for debugging).</div>\n </div>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button mjButton variant=\"primary\" size=\"md\" (click)=\"startNewSession()\" [disabled]=\"loading\" aria-label=\"Start the SQL logging session\">\n @if (loading) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n Starting...\n } @else {\n <i class=\"fa-solid fa-play\" aria-hidden=\"true\"></i>\n Start Session\n }\n </button>\n <button mjButton variant=\"secondary\" size=\"md\" (click)=\"showStartSessionDialog = false\" aria-label=\"Cancel and close dialog\">Cancel</button>\n </div>\n </div>\n </div>\n }\n\n <!-- Stop Session Confirmation Dialog -->\n @if (showStopConfirmDialog) {\n <div\n class=\"modal-backdrop\"\n (click)=\"cancelStopConfirm()\"\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-labelledby=\"stop-dialog-title\"\n aria-describedby=\"stop-dialog-desc\"\n >\n <div class=\"confirm-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"confirm-dialog-icon\">\n <i class=\"fa-solid fa-exclamation-triangle\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"confirm-dialog-content\">\n <h3 class=\"confirm-dialog-title\" id=\"stop-dialog-title\">\n @if (isStoppingAll) {\n Stop All Sessions?\n } @else {\n Stop Session?\n }\n </h3>\n <p class=\"confirm-dialog-message\" id=\"stop-dialog-desc\">\n @if (isStoppingAll) {\n Are you sure you want to stop <strong>all {{ activeSessions.length }} active</strong> SQL logging sessions? This action cannot be undone.\n } @else if (sessionToStop) {\n Are you sure you want to stop the session <strong>\"{{ sessionToStop.sessionName }}\"</strong>?\n <span class=\"confirm-dialog-details\"> This session has captured {{ sessionToStop.statementCount }} SQL statements. </span>\n }\n </p>\n </div>\n <div class=\"confirm-dialog-actions\">\n <button mjButton variant=\"danger\" size=\"md\" (click)=\"confirmStopSession()\" [disabled]=\"loading\" [attr.aria-label]=\"isStoppingAll ? 'Confirm stop all sessions' : 'Confirm stop session'\">\n @if (loading) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n Stopping...\n } @else {\n <i class=\"fa-solid fa-stop\" aria-hidden=\"true\"></i>\n @if (isStoppingAll) {\n Stop All Sessions\n } @else {\n Stop Session\n }\n }\n </button>\n <button mjButton variant=\"secondary\" size=\"md\" (click)=\"cancelStopConfirm()\" [disabled]=\"loading\" aria-label=\"Cancel and keep sessions running\">Cancel</button>\n </div>\n </div>\n </div>\n }\n</mj-page-body-interior>\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 SQL Logging Component - Component-Specific Styles\n Shared patterns are in ../shared/styles/_admin-patterns.css\n ============================================================================= */\n\n/*\n Layout \u2014 host is flex column (from _admin-patterns.css :host).\n <mj-page-header-interior> sits pinned at the top; .scrollable-content\n (also from _admin-patterns) fills the rest with responsive padding.\n Buttons in this component use the mjButton directive \u2014 see Section 10\n of plans/explorer-chrome-conventions.md.\n*/\n\n/* Small-viewport adjustments for the collapsible stats toggle. */\n@media (max-width: 639px) {\n .stats-toggle-button {\n padding: 0.875rem 1rem;\n }\n\n .stats-toggle-icon {\n width: 40px;\n height: 40px;\n font-size: 1rem;\n }\n\n .stats-toggle-title {\n font-size: 0.9375rem;\n }\n\n .stats-toggle-subtitle {\n font-size: 0.75rem;\n }\n\n .stats-toggle-arrow {\n font-size: 1.125rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Statistics Grid - Collapsible Cards\n ----------------------------------------------------------------------------- */\n.stats-toggle-container {\n margin-bottom: 1.5rem;\n}\n\n.stats-toggle-button {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 1rem 1.5rem;\n background: var(--mj-bg-surface-sunken);\n border: 1px 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.stats-toggle-button:hover {\n background: var(--mj-bg-surface-active);\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.stats-toggle-button.expanded {\n border-color: var(--mj-brand-primary);\n background: var(--mj-brand-accent-subtle);\n margin-bottom: 1rem;\n}\n\n.stats-toggle-left {\n display: flex;\n align-items: center;\n gap: 1rem;\n}\n\n.stats-toggle-icon {\n width: 48px;\n height: 48px;\n border-radius: var(--mj-radius-lg);\n background: var(--mj-brand-accent-subtle);\n color: var(--mj-brand-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.25rem;\n flex-shrink: 0;\n}\n\n.stats-toggle-button.expanded .stats-toggle-icon {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.stats-toggle-text {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n}\n\n.stats-toggle-title {\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.stats-toggle-button.expanded .stats-toggle-title {\n color: var(--mj-brand-primary);\n}\n\n.stats-toggle-subtitle {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n.stats-toggle-arrow {\n font-size: 1.25rem;\n color: var(--mj-text-secondary);\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.stats-toggle-button.expanded .stats-toggle-arrow {\n transform: rotate(180deg);\n color: var(--mj-brand-primary);\n}\n\n.stats-grid {\n display: grid;\n grid-template-columns: 1fr;\n gap: 0.75rem;\n width: 100%;\n overflow: hidden;\n max-height: 0;\n opacity: 0;\n transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.stats-grid.visible {\n max-height: 1000px;\n opacity: 1;\n margin-bottom: 1.5rem;\n}\n\n@media (min-width: 640px) {\n .stats-grid {\n grid-template-columns: repeat(2, 1fr);\n gap: 1rem;\n }\n}\n\n@media (min-width: 768px) {\n .stats-toggle-button {\n display: none;\n }\n\n .stats-grid {\n max-height: none;\n opacity: 1;\n margin-bottom: 1.5rem;\n gap: 1.25rem;\n }\n}\n\n@media (min-width: 1024px) {\n .stats-grid {\n grid-template-columns: repeat(4, 1fr);\n gap: 1.5rem;\n }\n}\n\n@media (min-width: 1440px) {\n .stats-grid {\n gap: 2rem;\n }\n}\n\n/* Static Display Card */\n.stat-card {\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-lg);\n padding: 1.25rem;\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}\n\n/* Stat Icon Override - Different sizes */\n.stat-icon {\n width: 56px;\n height: 56px;\n font-size: 1.5rem;\n}\n\n@media (max-width: 639px) {\n .stat-icon {\n width: 48px;\n height: 48px;\n font-size: 1.25rem;\n }\n}\n\n.stat-icon-status {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success);\n}\n\n.stat-icon-active {\n background: var(--mj-brand-accent-subtle);\n color: var(--mj-brand-primary);\n}\n\n.stat-icon-limit {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n}\n\n.stat-icon-total {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success);\n}\n\n/* Stat Value Override for mobile */\n@media (max-width: 639px) {\n .stat-content .stat-value {\n font-size: 1.5rem;\n font-weight: 700;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Content Area Override - Different styling\n ----------------------------------------------------------------------------- */\n.content-area {\n flex: 1;\n min-height: 0;\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-lg);\n box-shadow: none;\n}\n\n@media (min-width: 640px) {\n .content-area {\n padding: 1.25rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Sessions Layout - Two-panel design\n ----------------------------------------------------------------------------- */\n.sessions-layout {\n display: flex;\n gap: 1.5rem;\n min-height: 400px;\n overflow: visible;\n}\n\n@media (max-width: 767px) {\n .sessions-layout {\n flex-direction: column;\n gap: 1rem;\n min-height: auto;\n }\n}\n\n@media (min-width: 768px) and (max-width: 1023px) {\n .sessions-layout {\n gap: 1.25rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Sessions Panel\n ----------------------------------------------------------------------------- */\n.sessions-panel {\n flex: 0 0 380px;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n background: var(--mj-bg-surface);\n max-height: 600px;\n}\n\n@media (max-width: 767px) {\n .sessions-panel {\n flex: none;\n max-height: 350px;\n }\n}\n\n@media (min-width: 768px) and (max-width: 1023px) {\n .sessions-panel {\n flex: 0 0 320px;\n }\n}\n\n@media (min-width: 1440px) {\n .sessions-panel {\n flex: 0 0 420px;\n }\n}\n\n.panel-header {\n padding: 1rem;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-shrink: 0;\n gap: 0.5rem;\n}\n\n@media (max-width: 639px) {\n .panel-header {\n padding: 0.75rem;\n flex-wrap: wrap;\n }\n}\n\n.panel-header .panel-title {\n margin: 0;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n flex-shrink: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n@media (max-width: 639px) {\n .panel-header .panel-title {\n font-size: 0.875rem;\n max-width: 50%;\n }\n}\n\n.panel-actions {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n\n@media (max-width: 639px) {\n .panel-actions {\n gap: 0.25rem;\n }\n\n .panel-actions .checkbox-label {\n font-size: 0;\n gap: 0;\n min-height: 36px;\n padding: 0;\n }\n\n .panel-actions .checkbox-label::after {\n content: \"Auto\";\n font-size: 0.75rem;\n margin-left: 0.25rem;\n }\n}\n\n.sessions-list {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n -webkit-overflow-scrolling: touch;\n min-height: 0;\n position: relative;\n padding: 0.75rem;\n}\n\n/* -----------------------------------------------------------------------------\n Session Cards\n ----------------------------------------------------------------------------- */\n.session-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 1rem;\n margin-bottom: 0.75rem;\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.session-card:last-child {\n margin-bottom: 0;\n}\n\n.session-card:hover {\n box-shadow: var(--mj-shadow-sm);\n border-color: var(--mj-brand-primary);\n}\n\n.session-card.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n background: var(--mj-brand-accent-subtle);\n}\n\n.session-card:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.session-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 0.75rem;\n}\n\n.session-info {\n flex: 1;\n min-width: 0;\n}\n\n.session-info .session-title {\n margin: 0 0 0.5rem 0;\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.session-meta {\n display: flex;\n gap: 1rem;\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n flex-wrap: wrap;\n}\n\n.session-meta .meta-item {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.session-badges {\n display: flex;\n gap: 0.5rem;\n flex-wrap: wrap;\n margin-top: 0.5rem;\n}\n\n/* -----------------------------------------------------------------------------\n Badges - Chips\n ----------------------------------------------------------------------------- */\n.badge {\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 display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n}\n\n.badge-user {\n background: var(--mj-brand-accent-subtle);\n color: var(--mj-brand-primary);\n border: 1px solid var(--mj-brand-primary);\n}\n\n.badge-migration {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border: 1px solid var(--mj-status-success);\n}\n\n.badge-type {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning);\n}\n\n/* -----------------------------------------------------------------------------\n Action Buttons (Icon buttons)\n ----------------------------------------------------------------------------- */\n.action-btn {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1rem;\n border-radius: var(--mj-radius-full);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.action-btn:hover {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.action-btn:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.action-btn-danger:hover {\n background: var(--mj-status-error);\n color: var(--mj-brand-on-primary);\n}\n\n/* -----------------------------------------------------------------------------\n Log Viewer Panel\n ----------------------------------------------------------------------------- */\n.log-viewer-panel {\n flex: 1;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n background: var(--mj-bg-surface);\n min-height: 400px;\n}\n\n@media (max-width: 767px) {\n .log-viewer-panel {\n min-height: 350px;\n }\n}\n\n/* Code editor keeps its dark theme for readability (intentionally hardcoded for code syntax) */\n.log-content {\n flex: 1;\n overflow: hidden;\n min-height: 300px;\n height: 100%;\n position: relative;\n background: var(--mj-text-primary);\n color: var(--mj-border-strong);\n}\n\n.log-content ::ng-deep .cm-editor {\n height: 100%;\n font-family: \"Consolas\", \"Monaco\", \"Courier New\", monospace;\n font-size: 0.875rem;\n}\n\n.log-content ::ng-deep .cm-scroller {\n font-family: inherit;\n}\n\n.log-content ::ng-deep .cm-content {\n background: var(--mj-text-primary);\n color: var(--mj-border-strong);\n}\n\n.log-content ::ng-deep .cm-gutters {\n background: var(--mj-text-primary);\n color: var(--mj-text-disabled);\n border-right: 1px solid var(--mj-text-secondary);\n}\n\n.log-content ::ng-deep .cm-activeLineGutter {\n background: var(--mj-text-primary);\n}\n\n.log-content ::ng-deep .cm-activeLine {\n background: rgba(255, 255, 255, 0.04);\n}\n\n/* Expanded Log Viewer */\n.log-viewer-panel.expanded {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 999;\n border-radius: 0;\n margin: 0;\n max-height: none;\n height: 100vh;\n animation: expandIn 0.3s cubic-bezier(0, 0, 0.2, 1);\n}\n\n.log-viewer-panel.expanded .panel-header {\n border-radius: 0;\n}\n\n.log-viewer-panel.expanded .log-content {\n max-height: calc(100vh - 60px);\n min-height: calc(100vh - 60px);\n}\n\n/* -----------------------------------------------------------------------------\n Empty State Override - Different icon.warning styling\n ----------------------------------------------------------------------------- */\n.empty-state .empty-icon.warning {\n color: var(--mj-status-warning);\n}\n\n.empty-state .empty-subtext {\n max-width: 400px;\n}\n\n/* -----------------------------------------------------------------------------\n Info Box\n ----------------------------------------------------------------------------- */\n.info-box {\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n padding: 1.5rem;\n margin-top: 1.5rem;\n text-align: left;\n max-width: 500px;\n border: 1px solid var(--mj-border-default);\n}\n\n.info-box h4 {\n margin: 0 0 1rem 0;\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.info-box ol {\n margin: 0;\n padding-left: 1.25rem;\n font-size: 1rem;\n color: var(--mj-text-primary);\n}\n\n.info-box ol li {\n margin-bottom: 0.5rem;\n}\n\n.info-box code {\n background: var(--mj-bg-surface-active);\n padding: 0.125rem 0.5rem;\n border-radius: var(--mj-radius-sm);\n font-size: 0.8125rem;\n font-family: \"Consolas\", \"Monaco\", \"Courier New\", monospace;\n color: var(--mj-text-primary);\n}\n\n/* -----------------------------------------------------------------------------\n Loading Container Override\n ----------------------------------------------------------------------------- */\n.loading-container {\n min-height: 300px;\n}\n\n.loading-text {\n font-size: 1rem;\n color: var(--mj-text-secondary);\n margin-top: 1rem;\n}\n\n/* -----------------------------------------------------------------------------\n Modal Dialog Overrides - SQL Logging has its own modal patterns\n ----------------------------------------------------------------------------- */\n.modal-backdrop {\n background: var(--mj-bg-overlay);\n}\n\n.modal-dialog {\n width: 90%;\n display: flex;\n flex-direction: column;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.modal-dialog.modal-large {\n max-width: 700px;\n}\n\n/* Fullscreen mode */\n.modal-dialog.fullscreen {\n max-width: 100vw;\n width: 100vw;\n max-height: 100vh;\n height: 100vh;\n border-radius: 0;\n margin: 0;\n}\n\n@media (max-width: 639px) {\n .modal-dialog {\n width: 95%;\n max-height: 85vh;\n border-radius: var(--mj-radius-2xl);\n }\n\n .modal-dialog.fullscreen {\n border-radius: 0;\n max-height: 100vh;\n }\n}\n\n.modal-header {\n flex-shrink: 0;\n}\n\n@media (max-width: 639px) {\n .modal-header {\n padding: 1rem 1.25rem;\n }\n}\n\n/* Modal Header Actions */\n.modal-header-actions {\n display: flex;\n gap: 0.5rem;\n align-items: center;\n flex-shrink: 0;\n}\n\n.modal-action-btn {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1.125rem;\n cursor: pointer;\n border-radius: var(--mj-radius-full);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\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.modal-action-btn:hover {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.modal-action-btn:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.modal-body {\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n flex: 1;\n}\n\n@media (max-width: 639px) {\n .modal-body {\n padding: 1rem 1.25rem;\n }\n}\n\n.modal-footer {\n justify-content: flex-end;\n flex-shrink: 0;\n}\n\n@media (max-width: 639px) {\n .modal-footer {\n padding: 1rem 1.25rem;\n }\n\n /* Stretch the action buttons to full width on small viewports. */\n .modal-footer button {\n width: 100%;\n justify-content: center;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Form Elements\n ----------------------------------------------------------------------------- */\n.form-group {\n margin-bottom: 1.5rem;\n}\n\n.form-label {\n display: block;\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n margin-bottom: 0.5rem;\n}\n\n.form-input,\n.form-select,\n.form-textarea {\n width: 100%;\n min-height: 44px;\n padding: 0.875rem 1rem;\n border: 2px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n font-size: 1rem;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.form-input:hover,\n.form-select:hover,\n.form-textarea:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n}\n\n.form-input:focus,\n.form-select:focus,\n.form-textarea:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n background: var(--mj-bg-surface);\n}\n\n.form-input::placeholder,\n.form-textarea::placeholder {\n color: var(--mj-text-secondary);\n opacity: 0.7;\n}\n\n.form-select {\n cursor: pointer;\n}\n\n.form-hint {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n margin-top: 0.375rem;\n}\n\n.form-checkboxes {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.checkbox-label {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n font-size: 1rem;\n color: var(--mj-text-primary);\n cursor: pointer;\n min-height: 44px;\n padding: 0.25rem 0;\n}\n\n.checkbox-label input[type=\"checkbox\"] {\n width: 20px;\n height: 20px;\n cursor: pointer;\n accent-color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n/* Form Section */\n.form-section {\n margin-top: 1.5rem;\n padding: 1.25rem;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n background: var(--mj-bg-surface-sunken);\n}\n\n.form-section-title {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n margin: 0 0 1rem 0;\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.form-section-title i {\n color: var(--mj-brand-primary);\n font-size: 1.125rem;\n}\n\n.form-textarea {\n font-family: \"Consolas\", \"Monaco\", \"Courier New\", monospace;\n resize: vertical;\n min-height: 100px;\n}\n\n/* -----------------------------------------------------------------------------\n Confirmation Dialog\n ----------------------------------------------------------------------------- */\n.confirm-dialog {\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-2xl);\n box-shadow: var(--mj-shadow-2xl);\n max-width: 420px;\n width: 90%;\n padding: 2rem;\n text-align: center;\n animation: dialogBounce 0.3s cubic-bezier(0, 0, 0.2, 1);\n}\n\n@media (max-width: 639px) {\n .confirm-dialog {\n padding: 1.5rem;\n border-radius: var(--mj-radius-2xl);\n }\n}\n\n.confirm-dialog-icon {\n width: 64px;\n height: 64px;\n border-radius: 50%;\n background: var(--mj-status-warning-bg);\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 1.5rem;\n}\n\n.confirm-dialog-icon i {\n font-size: 1.75rem;\n color: var(--mj-status-warning-text);\n}\n\n.confirm-dialog-content {\n margin-bottom: 1.5rem;\n}\n\n.confirm-dialog-title {\n margin: 0 0 0.75rem 0;\n font-size: 1.375rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.confirm-dialog-message {\n margin: 0;\n font-size: 1rem;\n color: var(--mj-text-secondary);\n line-height: 1.6;\n}\n\n.confirm-dialog-message strong {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n\n.confirm-dialog-details {\n display: block;\n margin-top: 0.75rem;\n padding: 0.75rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-md);\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n}\n\n.confirm-dialog-actions {\n display: flex;\n gap: 0.75rem;\n justify-content: center;\n}\n\n@media (max-width: 639px) {\n .confirm-dialog-actions {\n flex-direction: column-reverse;\n }\n\n /* Stretch the action buttons to full width on small viewports. */\n .confirm-dialog-actions button {\n width: 100%;\n justify-content: center;\n }\n}\n\n/* -----------------------------------------------------------------------------\n SQL-Logging-specific Animations\n ----------------------------------------------------------------------------- */\n@keyframes expandIn {\n from {\n transform: scale(0.95);\n opacity: 0;\n }\n to {\n transform: scale(1);\n opacity: 1;\n }\n}\n\n@keyframes dialogBounce {\n 0% {\n transform: scale(0.9) translateY(10px);\n opacity: 0;\n }\n 70% {\n transform: scale(1.02) translateY(-5px);\n }\n 100% {\n transform: scale(1) translateY(0);\n opacity: 1;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Screen Reader Only\n ----------------------------------------------------------------------------- */\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n"] }]
|
|
1285
|
+
args: [{ standalone: false, selector: 'mj-sql-logging', template: "<!--\n Interior chrome \u2014 sub-page of Admin's Monitoring left-nav shell. Single section\n (no L2 tab nav) so [toolbar] is empty; actions land in [actions]. See Section 10\n of plans/explorer-chrome-conventions.md.\n-->\n<mj-page-header-interior\n Role=\"region\"\n AriaLabel=\"SQL logging actions\"\n Title=\"SQL Logging\"\n Subtitle=\"Capture and download SQL statements executed by the API\">\n <div actions>\n <mj-refresh-button\n [Loading]=\"loading\"\n (Clicked)=\"loadActiveSessions()\">\n </mj-refresh-button>\n @if (isOwner && configEnabled) {\n <button mjButton\n variant=\"primary\"\n size=\"sm\"\n [disabled]=\"loading || activeSessions.length >= (sqlLoggingConfig?.maxActiveSessions || 5)\"\n (click)=\"openStartSessionDialog()\"\n [attr.aria-label]=\"'Start new SQL logging session' + (activeSessions.length >= (sqlLoggingConfig?.maxActiveSessions || 5) ? ' (maximum sessions reached)' : '')\">\n <i class=\"fa-solid fa-play\" aria-hidden=\"true\"></i>\n <span class=\"mj-action-label\">Start New Session</span>\n </button>\n }\n </div>\n</mj-page-header-interior>\n\n<mj-page-body-interior>\n\n <!-- Stats Cards with Toggle -->\n @if (isOwner && configEnabled) {\n <div class=\"stats-toggle-container\">\n <!-- Toggle Button -->\n <button\n class=\"stats-toggle-button\"\n [class.expanded]=\"showStats\"\n (click)=\"showStats = !showStats\"\n [attr.aria-expanded]=\"showStats\"\n aria-controls=\"stats-grid-content\"\n >\n <div class=\"stats-toggle-left\">\n <div class=\"stats-toggle-icon\">\n <i class=\"fa-solid fa-chart-line\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"stats-toggle-text\">\n <h3 class=\"stats-toggle-title\">Statistics</h3>\n <p class=\"stats-toggle-subtitle\">View session statistics and metrics</p>\n </div>\n </div>\n <i class=\"fa-solid fa-chevron-down stats-toggle-arrow\" aria-hidden=\"true\"></i>\n </button>\n\n <!-- Stats Grid (Collapsible) -->\n <div\n id=\"stats-grid-content\"\n class=\"stats-grid\"\n [class.visible]=\"showStats\"\n role=\"region\"\n aria-label=\"SQL logging statistics\"\n >\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-status\">\n <i class=\"fa-solid fa-power-off\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" [attr.aria-label]=\"'Status: ' + (configEnabled ? 'Enabled' : 'Disabled')\">\n {{ configEnabled ? \"Enabled\" : \"Disabled\" }}\n </div>\n <div class=\"stat-label\">Status</div>\n </div>\n </div>\n\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-active\">\n <i class=\"fa-solid fa-play-circle\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" [attr.aria-label]=\"activeSessions.length + ' active sessions'\">\n {{ activeSessions.length }}\n </div>\n <div class=\"stat-label\">Active Sessions</div>\n </div>\n </div>\n\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-limit\">\n <i class=\"fa-solid fa-gauge-high\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" [attr.aria-label]=\"(sqlLoggingConfig?.maxActiveSessions || 5) + ' maximum sessions allowed'\">\n {{ sqlLoggingConfig?.maxActiveSessions || 5 }}\n </div>\n <div class=\"stat-label\">Max Sessions</div>\n </div>\n </div>\n\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-total\">\n <i class=\"fa-solid fa-database\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\" [attr.aria-label]=\"getTotalStatementCount() + ' total SQL statements captured'\">\n {{ getTotalStatementCount() }}\n </div>\n <div class=\"stat-label\">Total Statements</div>\n </div>\n </div>\n </div>\n </div>\n }\n\n <!-- Loading State -->\n @if (loading && !activeSessions.length) {\n <div class=\"loading-container\" role=\"status\" aria-live=\"polite\" aria-busy=\"true\">\n <mj-loading text=\"Loading SQL logging configuration...\" size=\"medium\"></mj-loading>\n </div>\n }\n\n <!-- Content Area -->\n @if (!loading || activeSessions.length > 0) {\n <div class=\"content-area\">\n @if (!isOwner) {\n <!-- Not authorized -->\n <div class=\"empty-state\" role=\"alert\">\n <i class=\"fa-solid fa-lock empty-icon\" aria-hidden=\"true\"></i>\n <p class=\"empty-text\">Access Denied</p>\n <p class=\"empty-subtext\">SQL logging requires Owner privileges. Please contact your system administrator for access.</p>\n <button mjButton variant=\"secondary\" size=\"md\" (click)=\"refreshUserPermissions()\" style=\"margin-top: 1rem\" aria-label=\"Refresh user permissions\">\n <i class=\"fa-solid fa-sync\" aria-hidden=\"true\"></i>\n Refresh Permissions\n </button>\n </div>\n } @else if (!configEnabled) {\n <!-- Not enabled in config -->\n <div class=\"empty-state\" role=\"alert\">\n <i class=\"fa-solid fa-exclamation-triangle empty-icon warning\" aria-hidden=\"true\"></i>\n <p class=\"empty-text\">SQL Logging Disabled</p>\n <p class=\"empty-subtext\">SQL logging is not enabled in the server configuration.</p>\n <div class=\"info-box\">\n <h4>To enable SQL logging:</h4>\n <ol>\n <li>Set <code>sqlLogging.enabled = true</code> in mj.config.cjs</li>\n <li>Restart the MJ API server</li>\n <li>Refresh this page</li>\n </ol>\n </div>\n </div>\n } @else if (activeSessions.length === 0) {\n <!-- No active sessions -->\n <div class=\"empty-state\" role=\"status\">\n <i class=\"fa-solid fa-file-code empty-icon\" aria-hidden=\"true\"></i>\n <p class=\"empty-text\">No Active Sessions</p>\n <p class=\"empty-subtext\">Start a new SQL logging session to begin capturing SQL statements.</p>\n <button mjButton variant=\"primary\" size=\"md\" (click)=\"openStartSessionDialog()\" style=\"margin-top: 1rem\" aria-label=\"Start a new SQL logging session\">\n <i class=\"fa-solid fa-play\" aria-hidden=\"true\"></i>\n Start New Session\n </button>\n </div>\n } @else {\n <!-- Sessions layout -->\n <div class=\"sessions-layout\">\n <!-- Sessions panel -->\n <div class=\"sessions-panel\" role=\"region\" aria-label=\"Active sessions list\">\n <div class=\"panel-header\">\n <h3 class=\"panel-title\">Active Sessions</h3>\n @if (activeSessions.length > 0) {\n <button mjButton\n variant=\"danger\"\n size=\"sm\"\n (click)=\"openStopAllSessionsConfirm()\"\n [disabled]=\"loading\"\n aria-label=\"Stop all active sessions\"\n >\n <i class=\"fa-solid fa-stop\" aria-hidden=\"true\"></i>\n Stop All\n </button>\n }\n </div>\n <div class=\"sessions-list\" role=\"list\" aria-label=\"SQL logging sessions\">\n @for (session of activeSessions; track session.id) {\n <div\n class=\"session-card\"\n [class.selected]=\"selectedSession?.id === session.id\"\n (click)=\"selectSession(session)\"\n role=\"listitem\"\n tabindex=\"0\"\n (keydown.enter)=\"selectSession(session)\"\n (keydown.space)=\"selectSession(session); $event.preventDefault()\"\n [attr.aria-label]=\"'Session: ' + session.sessionName + ', ' + session.statementCount + ' statements, duration ' + getSessionDuration(session.startTime)\"\n [attr.aria-selected]=\"selectedSession?.id === session.id\"\n >\n <div class=\"session-header\">\n <div class=\"session-info\">\n <h4 class=\"session-title\">{{ session.sessionName }}</h4>\n <div class=\"session-meta\">\n <span class=\"meta-item\">\n <i class=\"fa-solid fa-clock\" aria-hidden=\"true\"></i>\n {{ getSessionDuration(session.startTime) }}\n </span>\n <span class=\"meta-item\">\n <i class=\"fa-solid fa-database\" aria-hidden=\"true\"></i>\n {{ session.statementCount }} statements\n </span>\n </div>\n </div>\n <button mjButton\n variant=\"danger\"\n size=\"sm\"\n (click)=\"openStopSessionConfirm(session, $event)\"\n [attr.aria-label]=\"'Stop session ' + session.sessionName\"\n >\n <i class=\"fa-solid fa-stop\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <div class=\"session-badges\">\n @if (session.filterByUserId) {\n <span class=\"badge badge-user\">\n <i class=\"fa-solid fa-user\" aria-hidden=\"true\"></i>\n User Filtered\n </span>\n }\n @if (session.options?.formatAsMigration) {\n <span class=\"badge badge-migration\">\n <i class=\"fa-solid fa-code-branch\" aria-hidden=\"true\"></i>\n Migration\n </span>\n }\n <span class=\"badge badge-type\">\n {{ session.options?.statementTypes || \"both\" }}\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Log viewer panel -->\n <div class=\"log-viewer-panel\" [class.expanded]=\"isLogViewerExpanded\" role=\"region\" aria-label=\"SQL log viewer\">\n @if (selectedSession) {\n <div class=\"panel-header\">\n <h3 class=\"panel-title\">{{ selectedSession.sessionName }}</h3>\n <div class=\"panel-actions\">\n <label class=\"checkbox-label\">\n <input\n type=\"checkbox\"\n [(ngModel)]=\"autoRefresh\"\n aria-label=\"Enable auto-refresh\"\n />\n Auto-refresh\n </label>\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n (click)=\"loadSessionLog(selectedSession)\"\n aria-label=\"Refresh log content\"\n >\n <i class=\"fa-solid fa-sync\" [class.fa-spin]=\"loading\" aria-hidden=\"true\"></i>\n </button>\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n (click)=\"toggleLogViewerExpand()\"\n [attr.aria-label]=\"isLogViewerExpanded ? 'Collapse log viewer' : 'Expand log viewer to fullscreen'\"\n [attr.aria-expanded]=\"isLogViewerExpanded\"\n >\n <i class=\"fa-solid\" [class.fa-expand]=\"!isLogViewerExpanded\" [class.fa-compress]=\"isLogViewerExpanded\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n <div class=\"log-content\">\n <mj-code-editor\n [value]=\"logContent\"\n [readonly]=\"true\"\n [disabled]=\"true\"\n [language]=\"'sql'\"\n [setup]=\"'basic'\"\n [lineWrapping]=\"true\"\n [highlightWhitespace]=\"false\"\n style=\"height: 100%\"\n aria-label=\"SQL log content\"\n ></mj-code-editor>\n </div>\n } @else {\n <div class=\"empty-state\" role=\"status\">\n <i class=\"fa-solid fa-arrow-left empty-icon\" aria-hidden=\"true\"></i>\n <p class=\"empty-text\">Select a Session</p>\n <p class=\"empty-subtext\">Choose a session from the list to view its SQL log.</p>\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Start Session Dialog -->\n @if (showStartSessionDialog) {\n <div\n class=\"modal-backdrop\"\n (click)=\"showStartSessionDialog = false\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"start-session-dialog-title\"\n >\n <div class=\"modal-dialog modal-large\" [class.fullscreen]=\"isStartDialogFullscreen\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3 class=\"modal-title\" id=\"start-session-dialog-title\">\n <i class=\"fa-solid fa-play\" aria-hidden=\"true\"></i>\n Start SQL Logging Session\n </h3>\n <div class=\"modal-header-actions\">\n <button\n class=\"modal-action-btn\"\n (click)=\"toggleStartDialogFullscreen()\"\n [attr.aria-label]=\"isStartDialogFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'\"\n type=\"button\"\n >\n <i class=\"fa-solid\" [class.fa-compress]=\"isStartDialogFullscreen\" [class.fa-expand]=\"!isStartDialogFullscreen\" aria-hidden=\"true\"></i>\n </button>\n <button class=\"modal-close\" (click)=\"showStartSessionDialog = false\" aria-label=\"Close dialog\">\n <i class=\"fa-solid fa-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n <div class=\"modal-body\">\n <div class=\"form-group\">\n <label class=\"form-label\" for=\"session-name\">Session Name</label>\n <input\n type=\"text\"\n id=\"session-name\"\n class=\"form-input\"\n [(ngModel)]=\"newSessionOptions.sessionName\"\n placeholder=\"Enter a descriptive name for this session\"\n aria-describedby=\"session-name-hint\"\n />\n <div id=\"session-name-hint\" class=\"form-hint\">A descriptive name to identify this logging session</div>\n </div>\n\n <div class=\"form-group\">\n <label class=\"form-label\" for=\"file-name\">File Name</label>\n <input\n type=\"text\"\n id=\"file-name\"\n class=\"form-input\"\n [(ngModel)]=\"newSessionOptions.fileName\"\n placeholder=\"sql-log-2024-01-01.sql\"\n aria-describedby=\"file-name-hint\"\n />\n <div id=\"file-name-hint\" class=\"form-hint\">The SQL log will be saved to this file</div>\n </div>\n\n <div class=\"form-group\">\n <label class=\"form-label\" for=\"statement-types\">Statement Types</label>\n <select\n id=\"statement-types\"\n class=\"form-select\"\n [(ngModel)]=\"newSessionOptions.statementTypes\"\n aria-describedby=\"statement-types-hint\"\n >\n @for (option of statementTypeOptions; track option.value) {\n <option [value]=\"option.value\">{{ option.text }}</option>\n }\n </select>\n <div id=\"statement-types-hint\" class=\"form-hint\">Filter which types of SQL statements to capture</div>\n </div>\n\n <div class=\"form-checkboxes\" role=\"group\" aria-label=\"Session options\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" [(ngModel)]=\"newSessionOptions.filterToCurrentUser\" />\n Filter to my SQL statements only\n </label>\n\n\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" [(ngModel)]=\"newSessionOptions.formatAsMigration\" />\n Format as migration file\n </label>\n\n\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" [(ngModel)]=\"newSessionOptions.prettyPrint\" />\n Pretty print SQL statements\n </label>\n </div>\n\n <!-- SQL Pattern Filtering Section -->\n <div class=\"form-section\">\n <h4 class=\"form-section-title\">\n <i class=\"fa-solid fa-filter\" aria-hidden=\"true\"></i>\n SQL Pattern Filtering\n </h4>\n\n <div class=\"form-group\">\n <label class=\"form-label\" for=\"filter-type\">Filter Mode</label>\n <select\n id=\"filter-type\"\n class=\"form-select\"\n [(ngModel)]=\"newSessionOptions.filterType\"\n aria-describedby=\"filter-type-hint\"\n >\n @for (option of filterTypeOptions; track option.value) {\n <option [value]=\"option.value\">{{ option.text }}</option>\n }\n </select>\n <div id=\"filter-type-hint\" class=\"form-hint\">Exclude: Skip SQL matching any pattern. Include: Only log SQL matching a pattern.</div>\n </div>\n\n <div class=\"form-group\">\n <label class=\"form-label\" for=\"filter-patterns\">Filter Patterns (one per line or comma-separated)</label>\n <textarea\n id=\"filter-patterns\"\n class=\"form-textarea\"\n [(ngModel)]=\"newSessionOptions.filterPatterns\"\n rows=\"4\"\n placeholder=\"Examples: *AIPrompt* /spCreate.*Run/i SELECT * FROM vwMetadata\"\n aria-describedby=\"filter-patterns-hint\"\n ></textarea>\n <div id=\"filter-patterns-hint\" class=\"form-hint\">Supports wildcards (*) and regex (/pattern/flags). Example: *spCreate* matches any SP starting with spCreate.</div>\n </div>\n </div>\n\n <!-- Advanced Options Section -->\n <div class=\"form-section\">\n <h4 class=\"form-section-title\">\n <i class=\"fa-solid fa-cog\" aria-hidden=\"true\"></i>\n Advanced Options\n </h4>\n\n @if (newSessionOptions.formatAsMigration) {\n <div class=\"form-group\">\n <label class=\"form-label\" for=\"default-schema\">Default Schema Name</label>\n <input\n type=\"text\"\n id=\"default-schema\"\n class=\"form-input\"\n [(ngModel)]=\"newSessionOptions.defaultSchemaName\"\n placeholder=\"__mj\"\n aria-describedby=\"default-schema-hint\"\n />\n <div id=\"default-schema-hint\" class=\"form-hint\">Schema name to replace with ${flyway:defaultSchema} placeholder.</div>\n </div>\n }\n\n <div class=\"form-checkboxes\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" [(ngModel)]=\"newSessionOptions.verboseOutput\" />\n Verbose Debug Output\n </label>\n <div class=\"form-hint\" style=\"margin-left: 1.75rem\">Log detailed filter decisions to server console (for debugging).</div>\n </div>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button mjButton variant=\"primary\" size=\"md\" (click)=\"startNewSession()\" [disabled]=\"loading\" aria-label=\"Start the SQL logging session\">\n @if (loading) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n Starting...\n } @else {\n <i class=\"fa-solid fa-play\" aria-hidden=\"true\"></i>\n Start Session\n }\n </button>\n <button mjButton variant=\"secondary\" size=\"md\" (click)=\"showStartSessionDialog = false\" aria-label=\"Cancel and close dialog\">Cancel</button>\n </div>\n </div>\n </div>\n }\n\n <!-- Stop Session Confirmation Dialog -->\n @if (showStopConfirmDialog) {\n <div\n class=\"modal-backdrop\"\n (click)=\"cancelStopConfirm()\"\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-labelledby=\"stop-dialog-title\"\n aria-describedby=\"stop-dialog-desc\"\n >\n <div class=\"confirm-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"confirm-dialog-icon\">\n <i class=\"fa-solid fa-exclamation-triangle\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"confirm-dialog-content\">\n <h3 class=\"confirm-dialog-title\" id=\"stop-dialog-title\">\n @if (isStoppingAll) {\n Stop All Sessions?\n } @else {\n Stop Session?\n }\n </h3>\n <p class=\"confirm-dialog-message\" id=\"stop-dialog-desc\">\n @if (isStoppingAll) {\n Are you sure you want to stop <strong>all {{ activeSessions.length }} active</strong> SQL logging sessions? This action cannot be undone.\n } @else if (sessionToStop) {\n Are you sure you want to stop the session <strong>\"{{ sessionToStop.sessionName }}\"</strong>?\n <span class=\"confirm-dialog-details\"> This session has captured {{ sessionToStop.statementCount }} SQL statements. </span>\n }\n </p>\n </div>\n <div class=\"confirm-dialog-actions\">\n <button mjButton variant=\"danger\" size=\"md\" (click)=\"confirmStopSession()\" [disabled]=\"loading\" [attr.aria-label]=\"isStoppingAll ? 'Confirm stop all sessions' : 'Confirm stop session'\">\n @if (loading) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n Stopping...\n } @else {\n <i class=\"fa-solid fa-stop\" aria-hidden=\"true\"></i>\n @if (isStoppingAll) {\n Stop All Sessions\n } @else {\n Stop Session\n }\n }\n </button>\n <button mjButton variant=\"secondary\" size=\"md\" (click)=\"cancelStopConfirm()\" [disabled]=\"loading\" aria-label=\"Cancel and keep sessions running\">Cancel</button>\n </div>\n </div>\n </div>\n }\n</mj-page-body-interior>\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 SQL Logging Component - Component-Specific Styles\n Shared patterns are in ../shared/styles/_admin-patterns.css\n ============================================================================= */\n\n/*\n Layout \u2014 host is flex column (from _admin-patterns.css :host).\n <mj-page-header-interior> sits pinned at the top; .scrollable-content\n (also from _admin-patterns) fills the rest with responsive padding.\n Buttons in this component use the mjButton directive \u2014 see Section 10\n of plans/explorer-chrome-conventions.md.\n*/\n\n/* Small-viewport adjustments for the collapsible stats toggle. */\n@media (max-width: 639px) {\n .stats-toggle-button {\n padding: 0.875rem 1rem;\n }\n\n .stats-toggle-icon {\n width: 40px;\n height: 40px;\n font-size: 1rem;\n }\n\n .stats-toggle-title {\n font-size: 0.9375rem;\n }\n\n .stats-toggle-subtitle {\n font-size: 0.75rem;\n }\n\n .stats-toggle-arrow {\n font-size: 1.125rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Statistics Grid - Collapsible Cards\n ----------------------------------------------------------------------------- */\n.stats-toggle-container {\n margin-bottom: 1.5rem;\n}\n\n.stats-toggle-button {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 1rem 1.5rem;\n background: var(--mj-bg-surface-sunken);\n border: 1px 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.stats-toggle-button:hover {\n background: var(--mj-bg-surface-active);\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.stats-toggle-button.expanded {\n border-color: var(--mj-brand-primary);\n background: var(--mj-brand-accent-subtle);\n margin-bottom: 1rem;\n}\n\n.stats-toggle-left {\n display: flex;\n align-items: center;\n gap: 1rem;\n}\n\n.stats-toggle-icon {\n width: 48px;\n height: 48px;\n border-radius: var(--mj-radius-lg);\n background: var(--mj-brand-accent-subtle);\n color: var(--mj-brand-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.25rem;\n flex-shrink: 0;\n}\n\n.stats-toggle-button.expanded .stats-toggle-icon {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.stats-toggle-text {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n}\n\n.stats-toggle-title {\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.stats-toggle-button.expanded .stats-toggle-title {\n color: var(--mj-brand-primary);\n}\n\n.stats-toggle-subtitle {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n.stats-toggle-arrow {\n font-size: 1.25rem;\n color: var(--mj-text-secondary);\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.stats-toggle-button.expanded .stats-toggle-arrow {\n transform: rotate(180deg);\n color: var(--mj-brand-primary);\n}\n\n.stats-grid {\n display: grid;\n grid-template-columns: 1fr;\n gap: 0.75rem;\n width: 100%;\n overflow: hidden;\n max-height: 0;\n opacity: 0;\n transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.stats-grid.visible {\n max-height: 1000px;\n opacity: 1;\n margin-bottom: 1.5rem;\n}\n\n@media (min-width: 640px) {\n .stats-grid {\n grid-template-columns: repeat(2, 1fr);\n gap: 1rem;\n }\n}\n\n@media (min-width: 768px) {\n .stats-toggle-button {\n display: none;\n }\n\n .stats-grid {\n max-height: none;\n opacity: 1;\n margin-bottom: 1.5rem;\n gap: 1.25rem;\n }\n}\n\n@media (min-width: 1024px) {\n .stats-grid {\n grid-template-columns: repeat(4, 1fr);\n gap: 1.5rem;\n }\n}\n\n@media (min-width: 1440px) {\n .stats-grid {\n gap: 2rem;\n }\n}\n\n/* Static Display Card */\n.stat-card {\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-lg);\n padding: 1.25rem;\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}\n\n/* Stat Icon Override - Different sizes */\n.stat-icon {\n width: 56px;\n height: 56px;\n font-size: 1.5rem;\n}\n\n@media (max-width: 639px) {\n .stat-icon {\n width: 48px;\n height: 48px;\n font-size: 1.25rem;\n }\n}\n\n.stat-icon-status {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success);\n}\n\n.stat-icon-active {\n background: var(--mj-brand-accent-subtle);\n color: var(--mj-brand-primary);\n}\n\n.stat-icon-limit {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n}\n\n.stat-icon-total {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success);\n}\n\n/* Stat Value Override for mobile */\n@media (max-width: 639px) {\n .stat-content .stat-value {\n font-size: 1.5rem;\n font-weight: 700;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Content Area Override - Different styling\n ----------------------------------------------------------------------------- */\n.content-area {\n flex: 1;\n min-height: 0;\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-lg);\n box-shadow: none;\n}\n\n@media (min-width: 640px) {\n .content-area {\n padding: 1.25rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Sessions Layout - Two-panel design\n ----------------------------------------------------------------------------- */\n.sessions-layout {\n display: flex;\n gap: 1.5rem;\n min-height: 400px;\n overflow: visible;\n}\n\n@media (max-width: 767px) {\n .sessions-layout {\n flex-direction: column;\n gap: 1rem;\n min-height: auto;\n }\n}\n\n@media (min-width: 768px) and (max-width: 1023px) {\n .sessions-layout {\n gap: 1.25rem;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Sessions Panel\n ----------------------------------------------------------------------------- */\n.sessions-panel {\n flex: 0 0 380px;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n background: var(--mj-bg-surface);\n max-height: 600px;\n}\n\n@media (max-width: 767px) {\n .sessions-panel {\n flex: none;\n max-height: 350px;\n }\n}\n\n@media (min-width: 768px) and (max-width: 1023px) {\n .sessions-panel {\n flex: 0 0 320px;\n }\n}\n\n@media (min-width: 1440px) {\n .sessions-panel {\n flex: 0 0 420px;\n }\n}\n\n.panel-header {\n padding: 1rem;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-shrink: 0;\n gap: 0.5rem;\n}\n\n@media (max-width: 639px) {\n .panel-header {\n padding: 0.75rem;\n flex-wrap: wrap;\n }\n}\n\n.panel-header .panel-title {\n margin: 0;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n flex-shrink: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n@media (max-width: 639px) {\n .panel-header .panel-title {\n font-size: 0.875rem;\n max-width: 50%;\n }\n}\n\n.panel-actions {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n\n@media (max-width: 639px) {\n .panel-actions {\n gap: 0.25rem;\n }\n\n .panel-actions .checkbox-label {\n font-size: 0;\n gap: 0;\n min-height: 36px;\n padding: 0;\n }\n\n .panel-actions .checkbox-label::after {\n content: \"Auto\";\n font-size: 0.75rem;\n margin-left: 0.25rem;\n }\n}\n\n.sessions-list {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n -webkit-overflow-scrolling: touch;\n min-height: 0;\n position: relative;\n padding: 0.75rem;\n}\n\n/* -----------------------------------------------------------------------------\n Session Cards\n ----------------------------------------------------------------------------- */\n.session-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 1rem;\n margin-bottom: 0.75rem;\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.session-card:last-child {\n margin-bottom: 0;\n}\n\n.session-card:hover {\n box-shadow: var(--mj-shadow-sm);\n border-color: var(--mj-brand-primary);\n}\n\n.session-card.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n background: var(--mj-brand-accent-subtle);\n}\n\n.session-card:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.session-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 0.75rem;\n}\n\n.session-info {\n flex: 1;\n min-width: 0;\n}\n\n.session-info .session-title {\n margin: 0 0 0.5rem 0;\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.session-meta {\n display: flex;\n gap: 1rem;\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n flex-wrap: wrap;\n}\n\n.session-meta .meta-item {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.session-badges {\n display: flex;\n gap: 0.5rem;\n flex-wrap: wrap;\n margin-top: 0.5rem;\n}\n\n/* -----------------------------------------------------------------------------\n Badges - Chips\n ----------------------------------------------------------------------------- */\n.badge {\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 display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n}\n\n.badge-user {\n background: var(--mj-brand-accent-subtle);\n color: var(--mj-brand-primary);\n border: 1px solid var(--mj-brand-primary);\n}\n\n.badge-migration {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border: 1px solid var(--mj-status-success);\n}\n\n.badge-type {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning);\n}\n\n/* -----------------------------------------------------------------------------\n Action Buttons (Icon buttons)\n ----------------------------------------------------------------------------- */\n.action-btn {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1rem;\n border-radius: var(--mj-radius-full);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.action-btn:hover {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.action-btn:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.action-btn-danger:hover {\n background: var(--mj-status-error);\n color: var(--mj-brand-on-primary);\n}\n\n/* -----------------------------------------------------------------------------\n Log Viewer Panel\n ----------------------------------------------------------------------------- */\n.log-viewer-panel {\n flex: 1;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n background: var(--mj-bg-surface);\n min-height: 400px;\n}\n\n@media (max-width: 767px) {\n .log-viewer-panel {\n min-height: 350px;\n }\n}\n\n/* Code editor keeps its dark theme for readability (intentionally hardcoded for code syntax) */\n.log-content {\n flex: 1;\n overflow: hidden;\n min-height: 300px;\n height: 100%;\n position: relative;\n background: var(--mj-text-primary);\n color: var(--mj-border-strong);\n}\n\n.log-content ::ng-deep .cm-editor {\n height: 100%;\n font-family: \"Consolas\", \"Monaco\", \"Courier New\", monospace;\n font-size: 0.875rem;\n}\n\n.log-content ::ng-deep .cm-scroller {\n font-family: inherit;\n}\n\n.log-content ::ng-deep .cm-content {\n background: var(--mj-text-primary);\n color: var(--mj-border-strong);\n}\n\n.log-content ::ng-deep .cm-gutters {\n background: var(--mj-text-primary);\n color: var(--mj-text-disabled);\n border-right: 1px solid var(--mj-text-secondary);\n}\n\n.log-content ::ng-deep .cm-activeLineGutter {\n background: var(--mj-text-primary);\n}\n\n.log-content ::ng-deep .cm-activeLine {\n background: rgba(255, 255, 255, 0.04);\n}\n\n/* Expanded Log Viewer */\n.log-viewer-panel.expanded {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 999;\n border-radius: 0;\n margin: 0;\n max-height: none;\n height: 100vh;\n animation: expandIn 0.3s cubic-bezier(0, 0, 0.2, 1);\n}\n\n.log-viewer-panel.expanded .panel-header {\n border-radius: 0;\n}\n\n.log-viewer-panel.expanded .log-content {\n max-height: calc(100vh - 60px);\n min-height: calc(100vh - 60px);\n}\n\n/* -----------------------------------------------------------------------------\n Empty State Override - Different icon.warning styling\n ----------------------------------------------------------------------------- */\n.empty-state .empty-icon.warning {\n color: var(--mj-status-warning);\n}\n\n.empty-state .empty-subtext {\n max-width: 400px;\n}\n\n/* -----------------------------------------------------------------------------\n Info Box\n ----------------------------------------------------------------------------- */\n.info-box {\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n padding: 1.5rem;\n margin-top: 1.5rem;\n text-align: left;\n max-width: 500px;\n border: 1px solid var(--mj-border-default);\n}\n\n.info-box h4 {\n margin: 0 0 1rem 0;\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.info-box ol {\n margin: 0;\n padding-left: 1.25rem;\n font-size: 1rem;\n color: var(--mj-text-primary);\n}\n\n.info-box ol li {\n margin-bottom: 0.5rem;\n}\n\n.info-box code {\n background: var(--mj-bg-surface-active);\n padding: 0.125rem 0.5rem;\n border-radius: var(--mj-radius-sm);\n font-size: 0.8125rem;\n font-family: \"Consolas\", \"Monaco\", \"Courier New\", monospace;\n color: var(--mj-text-primary);\n}\n\n/* -----------------------------------------------------------------------------\n Loading Container Override\n ----------------------------------------------------------------------------- */\n.loading-container {\n min-height: 300px;\n}\n\n.loading-text {\n font-size: 1rem;\n color: var(--mj-text-secondary);\n margin-top: 1rem;\n}\n\n/* -----------------------------------------------------------------------------\n Modal Dialog Overrides - SQL Logging has its own modal patterns\n ----------------------------------------------------------------------------- */\n.modal-backdrop {\n background: var(--mj-bg-overlay);\n}\n\n.modal-dialog {\n width: 90%;\n display: flex;\n flex-direction: column;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.modal-dialog.modal-large {\n max-width: 700px;\n}\n\n/* Fullscreen mode */\n.modal-dialog.fullscreen {\n max-width: 100vw;\n width: 100vw;\n max-height: 100vh;\n height: 100vh;\n border-radius: 0;\n margin: 0;\n}\n\n@media (max-width: 639px) {\n .modal-dialog {\n width: 95%;\n max-height: 85vh;\n border-radius: var(--mj-radius-2xl);\n }\n\n .modal-dialog.fullscreen {\n border-radius: 0;\n max-height: 100vh;\n }\n}\n\n.modal-header {\n flex-shrink: 0;\n}\n\n@media (max-width: 639px) {\n .modal-header {\n padding: 1rem 1.25rem;\n }\n}\n\n/* Modal Header Actions */\n.modal-header-actions {\n display: flex;\n gap: 0.5rem;\n align-items: center;\n flex-shrink: 0;\n}\n\n.modal-action-btn {\n padding: 0.625rem;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 1.125rem;\n cursor: pointer;\n border-radius: var(--mj-radius-full);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\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.modal-action-btn:hover {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n}\n\n.modal-action-btn:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.modal-body {\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n flex: 1;\n}\n\n@media (max-width: 639px) {\n .modal-body {\n padding: 1rem 1.25rem;\n }\n}\n\n.modal-footer {\n justify-content: flex-end;\n flex-shrink: 0;\n}\n\n@media (max-width: 639px) {\n .modal-footer {\n padding: 1rem 1.25rem;\n }\n\n /* Stretch the action buttons to full width on small viewports. */\n .modal-footer button {\n width: 100%;\n justify-content: center;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Form Elements\n ----------------------------------------------------------------------------- */\n.form-group {\n margin-bottom: 1.5rem;\n}\n\n.form-label {\n display: block;\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n margin-bottom: 0.5rem;\n}\n\n.form-input,\n.form-select,\n.form-textarea {\n width: 100%;\n min-height: 44px;\n padding: 0.875rem 1rem;\n border: 2px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n font-size: 1rem;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.form-input:hover,\n.form-select:hover,\n.form-textarea:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n}\n\n.form-input:focus,\n.form-select:focus,\n.form-textarea:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n background: var(--mj-bg-surface);\n}\n\n.form-input::placeholder,\n.form-textarea::placeholder {\n color: var(--mj-text-secondary);\n opacity: 0.7;\n}\n\n.form-select {\n cursor: pointer;\n}\n\n.form-hint {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n margin-top: 0.375rem;\n}\n\n.form-checkboxes {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.checkbox-label {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n font-size: 1rem;\n color: var(--mj-text-primary);\n cursor: pointer;\n min-height: 44px;\n padding: 0.25rem 0;\n}\n\n.checkbox-label input[type=\"checkbox\"] {\n width: 20px;\n height: 20px;\n cursor: pointer;\n accent-color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n/* Form Section */\n.form-section {\n margin-top: 1.5rem;\n padding: 1.25rem;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n background: var(--mj-bg-surface-sunken);\n}\n\n.form-section-title {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n margin: 0 0 1rem 0;\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.form-section-title i {\n color: var(--mj-brand-primary);\n font-size: 1.125rem;\n}\n\n.form-textarea {\n font-family: \"Consolas\", \"Monaco\", \"Courier New\", monospace;\n resize: vertical;\n min-height: 100px;\n}\n\n/* -----------------------------------------------------------------------------\n Confirmation Dialog\n ----------------------------------------------------------------------------- */\n.confirm-dialog {\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-2xl);\n box-shadow: var(--mj-shadow-2xl);\n max-width: 420px;\n width: 90%;\n padding: 2rem;\n text-align: center;\n animation: dialogBounce 0.3s cubic-bezier(0, 0, 0.2, 1);\n}\n\n@media (max-width: 639px) {\n .confirm-dialog {\n padding: 1.5rem;\n border-radius: var(--mj-radius-2xl);\n }\n}\n\n.confirm-dialog-icon {\n width: 64px;\n height: 64px;\n border-radius: 50%;\n background: var(--mj-status-warning-bg);\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 1.5rem;\n}\n\n.confirm-dialog-icon i {\n font-size: 1.75rem;\n color: var(--mj-status-warning-text);\n}\n\n.confirm-dialog-content {\n margin-bottom: 1.5rem;\n}\n\n.confirm-dialog-title {\n margin: 0 0 0.75rem 0;\n font-size: 1.375rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.confirm-dialog-message {\n margin: 0;\n font-size: 1rem;\n color: var(--mj-text-secondary);\n line-height: 1.6;\n}\n\n.confirm-dialog-message strong {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n\n.confirm-dialog-details {\n display: block;\n margin-top: 0.75rem;\n padding: 0.75rem;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-md);\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n}\n\n.confirm-dialog-actions {\n display: flex;\n gap: 0.75rem;\n justify-content: center;\n}\n\n@media (max-width: 639px) {\n .confirm-dialog-actions {\n flex-direction: column-reverse;\n }\n\n /* Stretch the action buttons to full width on small viewports. */\n .confirm-dialog-actions button {\n width: 100%;\n justify-content: center;\n }\n}\n\n/* -----------------------------------------------------------------------------\n SQL-Logging-specific Animations\n ----------------------------------------------------------------------------- */\n@keyframes expandIn {\n from {\n transform: scale(0.95);\n opacity: 0;\n }\n to {\n transform: scale(1);\n opacity: 1;\n }\n}\n\n@keyframes dialogBounce {\n 0% {\n transform: scale(0.9) translateY(10px);\n opacity: 0;\n }\n 70% {\n transform: scale(1.02) translateY(-5px);\n }\n 100% {\n transform: scale(1) translateY(0);\n opacity: 1;\n }\n}\n\n/* -----------------------------------------------------------------------------\n Screen Reader Only\n ----------------------------------------------------------------------------- */\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n"] }]
|
|
1285
1286
|
}], () => [{ type: i1.SharedService }, { type: i0.ChangeDetectorRef }], { onEscapeKey: [{
|
|
1286
1287
|
type: HostListener,
|
|
1287
1288
|
args: ['document:keydown.escape']
|