@memberjunction/ng-explorer-settings 2.50.0 → 2.51.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.
Files changed (66) hide show
  1. package/README.md +89 -1
  2. package/dist/lib/application-management/application-management.component.d.ts +59 -0
  3. package/dist/lib/application-management/application-management.component.d.ts.map +1 -0
  4. package/dist/lib/application-management/application-management.component.js +540 -0
  5. package/dist/lib/application-management/application-management.component.js.map +1 -0
  6. package/dist/lib/entity-permissions/entity-permissions.component.d.ts +71 -0
  7. package/dist/lib/entity-permissions/entity-permissions.component.d.ts.map +1 -0
  8. package/dist/lib/entity-permissions/entity-permissions.component.js +667 -0
  9. package/dist/lib/entity-permissions/entity-permissions.component.js.map +1 -0
  10. package/dist/lib/module.d.ts +19 -23
  11. package/dist/lib/module.d.ts.map +1 -1
  12. package/dist/lib/module.js +13 -38
  13. package/dist/lib/module.js.map +1 -1
  14. package/dist/lib/role-management/role-management.component.d.ts +56 -0
  15. package/dist/lib/role-management/role-management.component.d.ts.map +1 -0
  16. package/dist/lib/role-management/role-management.component.js +464 -0
  17. package/dist/lib/role-management/role-management.component.js.map +1 -0
  18. package/dist/lib/settings/settings.component.d.ts +42 -51
  19. package/dist/lib/settings/settings.component.d.ts.map +1 -1
  20. package/dist/lib/settings/settings.component.js +432 -198
  21. package/dist/lib/settings/settings.component.js.map +1 -1
  22. package/dist/lib/shared/components/settings-card/settings-card.component.d.ts +27 -0
  23. package/dist/lib/shared/components/settings-card/settings-card.component.d.ts.map +1 -0
  24. package/dist/lib/shared/components/settings-card/settings-card.component.js +167 -0
  25. package/dist/lib/shared/components/settings-card/settings-card.component.js.map +1 -0
  26. package/dist/lib/shared/settings-card.component.d.ts +11 -0
  27. package/dist/lib/shared/settings-card.component.d.ts.map +1 -0
  28. package/dist/lib/shared/settings-card.component.js +73 -0
  29. package/dist/lib/shared/settings-card.component.js.map +1 -0
  30. package/dist/lib/shared/shared-settings.module.d.ts +9 -0
  31. package/dist/lib/shared/shared-settings.module.d.ts.map +1 -0
  32. package/dist/lib/shared/shared-settings.module.js +25 -0
  33. package/dist/lib/shared/shared-settings.module.js.map +1 -0
  34. package/dist/lib/sql-logging/sql-logging.component.d.ts +176 -0
  35. package/dist/lib/sql-logging/sql-logging.component.d.ts.map +1 -0
  36. package/dist/lib/sql-logging/sql-logging.component.js +946 -0
  37. package/dist/lib/sql-logging/sql-logging.component.js.map +1 -0
  38. package/dist/lib/user-management/user-management.component.d.ts +65 -0
  39. package/dist/lib/user-management/user-management.component.d.ts.map +1 -0
  40. package/dist/lib/user-management/user-management.component.js +643 -0
  41. package/dist/lib/user-management/user-management.component.js.map +1 -0
  42. package/dist/public-api.d.ts +1 -5
  43. package/dist/public-api.d.ts.map +1 -1
  44. package/dist/public-api.js +1 -5
  45. package/dist/public-api.js.map +1 -1
  46. package/package.json +13 -13
  47. package/dist/lib/application-entities-grid/application-entities-grid.component.d.ts +0 -50
  48. package/dist/lib/application-entities-grid/application-entities-grid.component.d.ts.map +0 -1
  49. package/dist/lib/application-entities-grid/application-entities-grid.component.js +0 -342
  50. package/dist/lib/application-entities-grid/application-entities-grid.component.js.map +0 -1
  51. package/dist/lib/single-application/single-application.component.d.ts +0 -22
  52. package/dist/lib/single-application/single-application.component.d.ts.map +0 -1
  53. package/dist/lib/single-application/single-application.component.js +0 -130
  54. package/dist/lib/single-application/single-application.component.js.map +0 -1
  55. package/dist/lib/single-role/single-role.component.d.ts +0 -36
  56. package/dist/lib/single-role/single-role.component.d.ts.map +0 -1
  57. package/dist/lib/single-role/single-role.component.js +0 -188
  58. package/dist/lib/single-role/single-role.component.js.map +0 -1
  59. package/dist/lib/single-user/single-user.component.d.ts +0 -24
  60. package/dist/lib/single-user/single-user.component.d.ts.map +0 -1
  61. package/dist/lib/single-user/single-user.component.js +0 -176
  62. package/dist/lib/single-user/single-user.component.js.map +0 -1
  63. package/dist/lib/user-roles-grid/user-roles-grid.component.d.ts +0 -42
  64. package/dist/lib/user-roles-grid/user-roles-grid.component.d.ts.map +0 -1
  65. package/dist/lib/user-roles-grid/user-roles-grid.component.js +0 -326
  66. package/dist/lib/user-roles-grid/user-roles-grid.component.js.map +0 -1
@@ -0,0 +1,946 @@
1
+ import { Component } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormsModule } from '@angular/forms';
4
+ import { Subject, interval } from 'rxjs';
5
+ import { takeUntil } from 'rxjs/operators';
6
+ import { Metadata } from '@memberjunction/core';
7
+ import { SharedService } from '@memberjunction/ng-shared';
8
+ import { MJNotificationService } from '@memberjunction/ng-notifications';
9
+ import { SharedSettingsModule } from '../shared/shared-settings.module';
10
+ import { CodeEditorModule } from '@memberjunction/ng-code-editor';
11
+ import * as i0 from "@angular/core";
12
+ import * as i1 from "@memberjunction/ng-shared";
13
+ import * as i2 from "@angular/forms";
14
+ import * as i3 from "@memberjunction/ng-code-editor";
15
+ const _forTrack0 = ($index, $item) => $item.id;
16
+ const _forTrack1 = ($index, $item) => $item.value;
17
+ function SqlLoggingComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
18
+ const _r1 = i0.ɵɵgetCurrentView();
19
+ i0.ɵɵelementStart(0, "button", 9);
20
+ i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_5_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.openStartSessionDialog()); });
21
+ i0.ɵɵelement(1, "i", 10);
22
+ i0.ɵɵtext(2, " Start New Session ");
23
+ i0.ɵɵelementEnd();
24
+ } if (rf & 2) {
25
+ const ctx_r1 = i0.ɵɵnextContext();
26
+ i0.ɵɵproperty("disabled", ctx_r1.loading || ctx_r1.activeSessions.length >= ((ctx_r1.sqlLoggingConfig == null ? null : ctx_r1.sqlLoggingConfig.maxActiveSessions) || 5));
27
+ } }
28
+ function SqlLoggingComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
29
+ i0.ɵɵelementStart(0, "div", 5)(1, "div", 11)(2, "div", 12);
30
+ i0.ɵɵelement(3, "i", 13);
31
+ i0.ɵɵelementEnd();
32
+ i0.ɵɵelementStart(4, "div", 14)(5, "div", 15);
33
+ i0.ɵɵtext(6);
34
+ i0.ɵɵelementEnd();
35
+ i0.ɵɵelementStart(7, "div", 16);
36
+ i0.ɵɵtext(8, "Status");
37
+ i0.ɵɵelementEnd()()();
38
+ i0.ɵɵelementStart(9, "div", 11)(10, "div", 17);
39
+ i0.ɵɵelement(11, "i", 18);
40
+ i0.ɵɵelementEnd();
41
+ i0.ɵɵelementStart(12, "div", 14)(13, "div", 15);
42
+ i0.ɵɵtext(14);
43
+ i0.ɵɵelementEnd();
44
+ i0.ɵɵelementStart(15, "div", 16);
45
+ i0.ɵɵtext(16, "Active Sessions");
46
+ i0.ɵɵelementEnd()()();
47
+ i0.ɵɵelementStart(17, "div", 11)(18, "div", 19);
48
+ i0.ɵɵelement(19, "i", 20);
49
+ i0.ɵɵelementEnd();
50
+ i0.ɵɵelementStart(20, "div", 14)(21, "div", 15);
51
+ i0.ɵɵtext(22);
52
+ i0.ɵɵelementEnd();
53
+ i0.ɵɵelementStart(23, "div", 16);
54
+ i0.ɵɵtext(24, "Max Sessions");
55
+ i0.ɵɵelementEnd()()();
56
+ i0.ɵɵelementStart(25, "div", 11)(26, "div", 21);
57
+ i0.ɵɵelement(27, "i", 22);
58
+ i0.ɵɵelementEnd();
59
+ i0.ɵɵelementStart(28, "div", 14)(29, "div", 15);
60
+ i0.ɵɵtext(30);
61
+ i0.ɵɵelementEnd();
62
+ i0.ɵɵelementStart(31, "div", 16);
63
+ i0.ɵɵtext(32, "Total Statements");
64
+ i0.ɵɵelementEnd()()()();
65
+ } if (rf & 2) {
66
+ const ctx_r1 = i0.ɵɵnextContext();
67
+ i0.ɵɵadvance(6);
68
+ i0.ɵɵtextInterpolate(ctx_r1.configEnabled ? "Enabled" : "Disabled");
69
+ i0.ɵɵadvance(8);
70
+ i0.ɵɵtextInterpolate(ctx_r1.activeSessions.length);
71
+ i0.ɵɵadvance(8);
72
+ i0.ɵɵtextInterpolate((ctx_r1.sqlLoggingConfig == null ? null : ctx_r1.sqlLoggingConfig.maxActiveSessions) || 5);
73
+ i0.ɵɵadvance(8);
74
+ i0.ɵɵtextInterpolate(ctx_r1.getTotalStatementCount());
75
+ } }
76
+ function SqlLoggingComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
77
+ i0.ɵɵelementStart(0, "div", 6)(1, "div", 23);
78
+ i0.ɵɵelement(2, "div", 24)(3, "div", 24)(4, "div", 24);
79
+ i0.ɵɵelementEnd();
80
+ i0.ɵɵelementStart(5, "div", 25);
81
+ i0.ɵɵtext(6, "Loading SQL logging configuration...");
82
+ i0.ɵɵelementEnd()();
83
+ } }
84
+ function SqlLoggingComponent_Conditional_8_Conditional_1_Template(rf, ctx) { if (rf & 1) {
85
+ const _r3 = i0.ɵɵgetCurrentView();
86
+ i0.ɵɵelementStart(0, "div", 26);
87
+ i0.ɵɵelement(1, "i", 28);
88
+ i0.ɵɵelementStart(2, "p", 29);
89
+ i0.ɵɵtext(3, "Access Denied");
90
+ i0.ɵɵelementEnd();
91
+ i0.ɵɵelementStart(4, "p", 30);
92
+ i0.ɵɵtext(5, "SQL logging requires Owner privileges. Please contact your system administrator for access.");
93
+ i0.ɵɵelementEnd();
94
+ i0.ɵɵelementStart(6, "button", 31);
95
+ i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_8_Conditional_1_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.refreshUserPermissions()); });
96
+ i0.ɵɵelement(7, "i", 32);
97
+ i0.ɵɵtext(8, " Refresh Permissions ");
98
+ i0.ɵɵelementEnd()();
99
+ } }
100
+ function SqlLoggingComponent_Conditional_8_Conditional_2_Template(rf, ctx) { if (rf & 1) {
101
+ i0.ɵɵelementStart(0, "div", 26);
102
+ i0.ɵɵelement(1, "i", 33);
103
+ i0.ɵɵelementStart(2, "p", 29);
104
+ i0.ɵɵtext(3, "SQL Logging Disabled");
105
+ i0.ɵɵelementEnd();
106
+ i0.ɵɵelementStart(4, "p", 30);
107
+ i0.ɵɵtext(5, "SQL logging is not enabled in the server configuration.");
108
+ i0.ɵɵelementEnd();
109
+ i0.ɵɵelementStart(6, "div", 34)(7, "h4");
110
+ i0.ɵɵtext(8, "To enable SQL logging:");
111
+ i0.ɵɵelementEnd();
112
+ i0.ɵɵelementStart(9, "ol")(10, "li");
113
+ i0.ɵɵtext(11, "Set ");
114
+ i0.ɵɵelementStart(12, "code");
115
+ i0.ɵɵtext(13, "sqlLogging.enabled = true");
116
+ i0.ɵɵelementEnd();
117
+ i0.ɵɵtext(14, " in mj.config.cjs");
118
+ i0.ɵɵelementEnd();
119
+ i0.ɵɵelementStart(15, "li");
120
+ i0.ɵɵtext(16, "Restart the MJ API server");
121
+ i0.ɵɵelementEnd();
122
+ i0.ɵɵelementStart(17, "li");
123
+ i0.ɵɵtext(18, "Refresh this page");
124
+ i0.ɵɵelementEnd()()()();
125
+ } }
126
+ function SqlLoggingComponent_Conditional_8_Conditional_3_Template(rf, ctx) { if (rf & 1) {
127
+ const _r4 = i0.ɵɵgetCurrentView();
128
+ i0.ɵɵelementStart(0, "div", 26);
129
+ i0.ɵɵelement(1, "i", 35);
130
+ i0.ɵɵelementStart(2, "p", 29);
131
+ i0.ɵɵtext(3, "No Active Sessions");
132
+ i0.ɵɵelementEnd();
133
+ i0.ɵɵelementStart(4, "p", 30);
134
+ i0.ɵɵtext(5, "Start a new SQL logging session to begin capturing SQL statements.");
135
+ i0.ɵɵelementEnd();
136
+ i0.ɵɵelementStart(6, "button", 36);
137
+ i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_8_Conditional_3_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.openStartSessionDialog()); });
138
+ i0.ɵɵelement(7, "i", 10);
139
+ i0.ɵɵtext(8, " Start New Session ");
140
+ i0.ɵɵelementEnd()();
141
+ } }
142
+ function SqlLoggingComponent_Conditional_8_Conditional_4_Conditional_5_Template(rf, ctx) { if (rf & 1) {
143
+ const _r5 = i0.ɵɵgetCurrentView();
144
+ i0.ɵɵelementStart(0, "button", 44);
145
+ i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_8_Conditional_4_Conditional_5_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.stopAllSessions()); });
146
+ i0.ɵɵelement(1, "i", 45);
147
+ i0.ɵɵtext(2, " Stop All ");
148
+ i0.ɵɵelementEnd();
149
+ } if (rf & 2) {
150
+ const ctx_r1 = i0.ɵɵnextContext(3);
151
+ i0.ɵɵproperty("disabled", ctx_r1.loading);
152
+ } }
153
+ function SqlLoggingComponent_Conditional_8_Conditional_4_For_8_Conditional_15_Template(rf, ctx) { if (rf & 1) {
154
+ i0.ɵɵelementStart(0, "span", 55);
155
+ i0.ɵɵelement(1, "i", 58);
156
+ i0.ɵɵtext(2, " User Filtered ");
157
+ i0.ɵɵelementEnd();
158
+ } }
159
+ function SqlLoggingComponent_Conditional_8_Conditional_4_For_8_Conditional_16_Template(rf, ctx) { if (rf & 1) {
160
+ i0.ɵɵelementStart(0, "span", 56);
161
+ i0.ɵɵelement(1, "i", 59);
162
+ i0.ɵɵtext(2, " Migration ");
163
+ i0.ɵɵelementEnd();
164
+ } }
165
+ function SqlLoggingComponent_Conditional_8_Conditional_4_For_8_Template(rf, ctx) { if (rf & 1) {
166
+ const _r6 = i0.ɵɵgetCurrentView();
167
+ i0.ɵɵelementStart(0, "div", 46);
168
+ i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_8_Conditional_4_For_8_Template_div_click_0_listener() { const session_r7 = i0.ɵɵrestoreView(_r6).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.selectSession(session_r7)); });
169
+ i0.ɵɵelementStart(1, "div", 47)(2, "div", 48)(3, "h4", 49);
170
+ i0.ɵɵtext(4);
171
+ i0.ɵɵelementEnd();
172
+ i0.ɵɵelementStart(5, "div", 50)(6, "span", 51);
173
+ i0.ɵɵelement(7, "i", 52);
174
+ i0.ɵɵtext(8);
175
+ i0.ɵɵelementEnd();
176
+ i0.ɵɵelementStart(9, "span", 51);
177
+ i0.ɵɵelement(10, "i", 22);
178
+ i0.ɵɵtext(11);
179
+ i0.ɵɵelementEnd()()();
180
+ i0.ɵɵelementStart(12, "button", 53);
181
+ i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_8_Conditional_4_For_8_Template_button_click_12_listener($event) { const session_r7 = i0.ɵɵrestoreView(_r6).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.stopSession(session_r7, $event)); });
182
+ i0.ɵɵelement(13, "i", 45);
183
+ i0.ɵɵelementEnd()();
184
+ i0.ɵɵelementStart(14, "div", 54);
185
+ i0.ɵɵtemplate(15, SqlLoggingComponent_Conditional_8_Conditional_4_For_8_Conditional_15_Template, 3, 0, "span", 55)(16, SqlLoggingComponent_Conditional_8_Conditional_4_For_8_Conditional_16_Template, 3, 0, "span", 56);
186
+ i0.ɵɵelementStart(17, "span", 57);
187
+ i0.ɵɵtext(18);
188
+ i0.ɵɵelementEnd()()();
189
+ } if (rf & 2) {
190
+ const session_r7 = ctx.$implicit;
191
+ const ctx_r1 = i0.ɵɵnextContext(3);
192
+ i0.ɵɵclassProp("selected", (ctx_r1.selectedSession == null ? null : ctx_r1.selectedSession.id) === session_r7.id);
193
+ i0.ɵɵadvance(4);
194
+ i0.ɵɵtextInterpolate(session_r7.sessionName);
195
+ i0.ɵɵadvance(4);
196
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.getSessionDuration(session_r7.startTime), " ");
197
+ i0.ɵɵadvance(3);
198
+ i0.ɵɵtextInterpolate1(" ", session_r7.statementCount, " statements ");
199
+ i0.ɵɵadvance(4);
200
+ i0.ɵɵconditional(session_r7.filterByUserId ? 15 : -1);
201
+ i0.ɵɵadvance();
202
+ i0.ɵɵconditional((session_r7.options == null ? null : session_r7.options.formatAsMigration) ? 16 : -1);
203
+ i0.ɵɵadvance(2);
204
+ i0.ɵɵtextInterpolate1(" ", (session_r7.options == null ? null : session_r7.options.statementTypes) || "both", " ");
205
+ } }
206
+ function SqlLoggingComponent_Conditional_8_Conditional_4_Conditional_10_Template(rf, ctx) { if (rf & 1) {
207
+ const _r8 = i0.ɵɵgetCurrentView();
208
+ i0.ɵɵelementStart(0, "div", 38)(1, "h3", 39);
209
+ i0.ɵɵtext(2);
210
+ i0.ɵɵelementEnd();
211
+ i0.ɵɵelementStart(3, "div", 60)(4, "label", 61)(5, "input", 62);
212
+ i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_8_Conditional_4_Conditional_10_Template_input_ngModelChange_5_listener($event) { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(3); i0.ɵɵtwoWayBindingSet(ctx_r1.autoRefresh, $event) || (ctx_r1.autoRefresh = $event); return i0.ɵɵresetView($event); });
213
+ i0.ɵɵelementEnd();
214
+ i0.ɵɵtext(6, " Auto-refresh ");
215
+ i0.ɵɵelementEnd();
216
+ i0.ɵɵelementStart(7, "button", 63);
217
+ i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_8_Conditional_4_Conditional_10_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.loadSessionLog(ctx_r1.selectedSession)); });
218
+ i0.ɵɵelement(8, "i", 32);
219
+ i0.ɵɵelementEnd()()();
220
+ i0.ɵɵelementStart(9, "div", 64);
221
+ i0.ɵɵelement(10, "mj-code-editor", 65);
222
+ i0.ɵɵelementEnd();
223
+ } if (rf & 2) {
224
+ const ctx_r1 = i0.ɵɵnextContext(3);
225
+ i0.ɵɵadvance(2);
226
+ i0.ɵɵtextInterpolate(ctx_r1.selectedSession.sessionName);
227
+ i0.ɵɵadvance(3);
228
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.autoRefresh);
229
+ i0.ɵɵadvance(3);
230
+ i0.ɵɵclassProp("fa-spin", ctx_r1.loading);
231
+ i0.ɵɵadvance(2);
232
+ i0.ɵɵproperty("value", ctx_r1.logContent)("readonly", true)("disabled", true)("language", "sql")("setup", "basic")("lineWrapping", true)("highlightWhitespace", false);
233
+ } }
234
+ function SqlLoggingComponent_Conditional_8_Conditional_4_Conditional_11_Template(rf, ctx) { if (rf & 1) {
235
+ i0.ɵɵelementStart(0, "div", 26);
236
+ i0.ɵɵelement(1, "i", 66);
237
+ i0.ɵɵelementStart(2, "p", 29);
238
+ i0.ɵɵtext(3, "Select a Session");
239
+ i0.ɵɵelementEnd();
240
+ i0.ɵɵelementStart(4, "p", 30);
241
+ i0.ɵɵtext(5, "Choose a session from the list to view its SQL log.");
242
+ i0.ɵɵelementEnd()();
243
+ } }
244
+ function SqlLoggingComponent_Conditional_8_Conditional_4_Template(rf, ctx) { if (rf & 1) {
245
+ i0.ɵɵelementStart(0, "div", 27)(1, "div", 37)(2, "div", 38)(3, "h3", 39);
246
+ i0.ɵɵtext(4, "Active Sessions");
247
+ i0.ɵɵelementEnd();
248
+ i0.ɵɵtemplate(5, SqlLoggingComponent_Conditional_8_Conditional_4_Conditional_5_Template, 3, 1, "button", 40);
249
+ i0.ɵɵelementEnd();
250
+ i0.ɵɵelementStart(6, "div", 41);
251
+ i0.ɵɵrepeaterCreate(7, SqlLoggingComponent_Conditional_8_Conditional_4_For_8_Template, 19, 8, "div", 42, _forTrack0);
252
+ i0.ɵɵelementEnd()();
253
+ i0.ɵɵelementStart(9, "div", 43);
254
+ i0.ɵɵtemplate(10, SqlLoggingComponent_Conditional_8_Conditional_4_Conditional_10_Template, 11, 11)(11, SqlLoggingComponent_Conditional_8_Conditional_4_Conditional_11_Template, 6, 0, "div", 26);
255
+ i0.ɵɵelementEnd()();
256
+ } if (rf & 2) {
257
+ const ctx_r1 = i0.ɵɵnextContext(2);
258
+ i0.ɵɵadvance(5);
259
+ i0.ɵɵconditional(ctx_r1.activeSessions.length > 0 ? 5 : -1);
260
+ i0.ɵɵadvance(2);
261
+ i0.ɵɵrepeater(ctx_r1.activeSessions);
262
+ i0.ɵɵadvance(3);
263
+ i0.ɵɵconditional(ctx_r1.selectedSession ? 10 : 11);
264
+ } }
265
+ function SqlLoggingComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
266
+ i0.ɵɵelementStart(0, "div", 7);
267
+ i0.ɵɵtemplate(1, SqlLoggingComponent_Conditional_8_Conditional_1_Template, 9, 0, "div", 26)(2, SqlLoggingComponent_Conditional_8_Conditional_2_Template, 19, 0, "div", 26)(3, SqlLoggingComponent_Conditional_8_Conditional_3_Template, 9, 0, "div", 26)(4, SqlLoggingComponent_Conditional_8_Conditional_4_Template, 12, 2, "div", 27);
268
+ i0.ɵɵelementEnd();
269
+ } if (rf & 2) {
270
+ const ctx_r1 = i0.ɵɵnextContext();
271
+ i0.ɵɵadvance();
272
+ i0.ɵɵconditional(!ctx_r1.isOwner ? 1 : !ctx_r1.configEnabled ? 2 : ctx_r1.activeSessions.length === 0 ? 3 : 4);
273
+ } }
274
+ function SqlLoggingComponent_Conditional_9_For_24_Template(rf, ctx) { if (rf & 1) {
275
+ i0.ɵɵelementStart(0, "option", 80);
276
+ i0.ɵɵtext(1);
277
+ i0.ɵɵelementEnd();
278
+ } if (rf & 2) {
279
+ const option_r10 = ctx.$implicit;
280
+ i0.ɵɵproperty("value", option_r10.value);
281
+ i0.ɵɵadvance();
282
+ i0.ɵɵtextInterpolate(option_r10.text);
283
+ } }
284
+ function SqlLoggingComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
285
+ const _r9 = i0.ɵɵgetCurrentView();
286
+ i0.ɵɵelementStart(0, "div", 67);
287
+ i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_9_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.showStartSessionDialog = false); });
288
+ i0.ɵɵelementStart(1, "div", 68);
289
+ i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_9_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r9); return i0.ɵɵresetView($event.stopPropagation()); });
290
+ i0.ɵɵelementStart(2, "div", 69)(3, "h3", 70);
291
+ i0.ɵɵelement(4, "i", 10);
292
+ i0.ɵɵtext(5, " Start SQL Logging Session ");
293
+ i0.ɵɵelementEnd();
294
+ i0.ɵɵelementStart(6, "button", 71);
295
+ i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_9_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.showStartSessionDialog = false); });
296
+ i0.ɵɵelement(7, "i", 72);
297
+ i0.ɵɵelementEnd()();
298
+ i0.ɵɵelementStart(8, "div", 73)(9, "div", 74)(10, "label", 75);
299
+ i0.ɵɵtext(11, "Session Name");
300
+ i0.ɵɵelementEnd();
301
+ i0.ɵɵelementStart(12, "input", 76);
302
+ i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_9_Template_input_ngModelChange_12_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.sessionName, $event) || (ctx_r1.newSessionOptions.sessionName = $event); return i0.ɵɵresetView($event); });
303
+ i0.ɵɵelementEnd()();
304
+ i0.ɵɵelementStart(13, "div", 74)(14, "label", 75);
305
+ i0.ɵɵtext(15, "File Name");
306
+ i0.ɵɵelementEnd();
307
+ i0.ɵɵelementStart(16, "input", 77);
308
+ i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_9_Template_input_ngModelChange_16_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.fileName, $event) || (ctx_r1.newSessionOptions.fileName = $event); return i0.ɵɵresetView($event); });
309
+ i0.ɵɵelementEnd();
310
+ i0.ɵɵelementStart(17, "div", 78);
311
+ i0.ɵɵtext(18, "The SQL log will be saved to this file");
312
+ i0.ɵɵelementEnd()();
313
+ i0.ɵɵelementStart(19, "div", 74)(20, "label", 75);
314
+ i0.ɵɵtext(21, "Statement Types");
315
+ i0.ɵɵelementEnd();
316
+ i0.ɵɵelementStart(22, "select", 79);
317
+ i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_9_Template_select_ngModelChange_22_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.statementTypes, $event) || (ctx_r1.newSessionOptions.statementTypes = $event); return i0.ɵɵresetView($event); });
318
+ i0.ɵɵrepeaterCreate(23, SqlLoggingComponent_Conditional_9_For_24_Template, 2, 2, "option", 80, _forTrack1);
319
+ i0.ɵɵelementEnd()();
320
+ i0.ɵɵelementStart(25, "div", 81)(26, "label", 61)(27, "input", 62);
321
+ i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_9_Template_input_ngModelChange_27_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.filterToCurrentUser, $event) || (ctx_r1.newSessionOptions.filterToCurrentUser = $event); return i0.ɵɵresetView($event); });
322
+ i0.ɵɵelementEnd();
323
+ i0.ɵɵtext(28, " Filter to my SQL statements only ");
324
+ i0.ɵɵelementEnd();
325
+ i0.ɵɵelementStart(29, "label", 61)(30, "input", 62);
326
+ i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_9_Template_input_ngModelChange_30_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.formatAsMigration, $event) || (ctx_r1.newSessionOptions.formatAsMigration = $event); return i0.ɵɵresetView($event); });
327
+ i0.ɵɵelementEnd();
328
+ i0.ɵɵtext(31, " Format as migration file ");
329
+ i0.ɵɵelementEnd();
330
+ i0.ɵɵelementStart(32, "label", 61)(33, "input", 62);
331
+ i0.ɵɵtwoWayListener("ngModelChange", function SqlLoggingComponent_Conditional_9_Template_input_ngModelChange_33_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newSessionOptions.prettyPrint, $event) || (ctx_r1.newSessionOptions.prettyPrint = $event); return i0.ɵɵresetView($event); });
332
+ i0.ɵɵelementEnd();
333
+ i0.ɵɵtext(34, " Pretty print SQL statements ");
334
+ i0.ɵɵelementEnd()()();
335
+ i0.ɵɵelementStart(35, "div", 82)(36, "button", 83);
336
+ i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_9_Template_button_click_36_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.showStartSessionDialog = false); });
337
+ i0.ɵɵtext(37, " Cancel ");
338
+ i0.ɵɵelementEnd();
339
+ i0.ɵɵelementStart(38, "button", 84);
340
+ i0.ɵɵlistener("click", function SqlLoggingComponent_Conditional_9_Template_button_click_38_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.startNewSession()); });
341
+ i0.ɵɵelement(39, "i", 10);
342
+ i0.ɵɵtext(40, " Start Session ");
343
+ i0.ɵɵelementEnd()()()();
344
+ } if (rf & 2) {
345
+ const ctx_r1 = i0.ɵɵnextContext();
346
+ i0.ɵɵadvance(12);
347
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.newSessionOptions.sessionName);
348
+ i0.ɵɵadvance(4);
349
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.newSessionOptions.fileName);
350
+ i0.ɵɵadvance(6);
351
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.newSessionOptions.statementTypes);
352
+ i0.ɵɵadvance();
353
+ i0.ɵɵrepeater(ctx_r1.statementTypeOptions);
354
+ i0.ɵɵadvance(4);
355
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.newSessionOptions.filterToCurrentUser);
356
+ i0.ɵɵadvance(3);
357
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.newSessionOptions.formatAsMigration);
358
+ i0.ɵɵadvance(3);
359
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.newSessionOptions.prettyPrint);
360
+ i0.ɵɵadvance(5);
361
+ i0.ɵɵproperty("disabled", ctx_r1.loading);
362
+ } }
363
+ /**
364
+ * Angular component for managing SQL logging sessions in MemberJunction.
365
+ *
366
+ * This component provides a user interface for:
367
+ * - Viewing SQL logging configuration and status
368
+ * - Starting and stopping SQL logging sessions
369
+ * - Managing session options (filtering, formatting, etc.)
370
+ * - Real-time monitoring of active sessions
371
+ *
372
+ * **Security**: Only users with 'Owner' type can access SQL logging features.
373
+ *
374
+ * @example
375
+ * ```html
376
+ * <mj-sql-logging></mj-sql-logging>
377
+ * ```
378
+ *
379
+ * @requires Owner-level user privileges
380
+ * @requires SQL logging enabled in server configuration
381
+ */
382
+ export class SqlLoggingComponent {
383
+ sharedService;
384
+ destroy$ = new Subject();
385
+ /** Whether the component is currently performing an async operation */
386
+ loading = false;
387
+ /** Current error message to display to the user, if any */
388
+ error = null;
389
+ /** Whether the current user has Owner privileges to access SQL logging */
390
+ isOwner = false;
391
+ /** Whether SQL logging is enabled in the server configuration */
392
+ configEnabled = false;
393
+ /** Current SQL logging configuration from the server */
394
+ sqlLoggingConfig = null;
395
+ /** List of currently active SQL logging sessions */
396
+ activeSessions = [];
397
+ /** Currently selected session for viewing logs */
398
+ selectedSession = null;
399
+ /** Content of the currently viewed log file */
400
+ logContent = '';
401
+ /** Whether to automatically refresh session data */
402
+ autoRefresh = false;
403
+ /** Interval in milliseconds for auto-refresh functionality */
404
+ refreshInterval = 5000; // 5 seconds
405
+ /** Whether the start session dialog is currently visible */
406
+ showStartSessionDialog = false;
407
+ /** Options for creating a new SQL logging session */
408
+ newSessionOptions = {
409
+ /** Custom filename for the log file */
410
+ fileName: '',
411
+ /** Whether to filter SQL statements to current user only */
412
+ filterToCurrentUser: true,
413
+ /** Whether to format output as migration file */
414
+ formatAsMigration: false,
415
+ /** Types of SQL statements to capture */
416
+ statementTypes: 'both',
417
+ /** Whether to format SQL with proper indentation */
418
+ prettyPrint: true,
419
+ /** Human-readable name for the session */
420
+ sessionName: ''
421
+ };
422
+ /** Available options for SQL statement type filtering */
423
+ statementTypeOptions = [
424
+ { text: 'Both Queries and Mutations', value: 'both' },
425
+ { text: 'Queries Only', value: 'queries' },
426
+ { text: 'Mutations Only', value: 'mutations' }
427
+ ];
428
+ constructor(sharedService) {
429
+ this.sharedService = sharedService;
430
+ }
431
+ /**
432
+ * Component initialization.
433
+ * Checks user permissions and loads initial data if authorized.
434
+ */
435
+ async ngOnInit() {
436
+ await this.checkUserPermissions();
437
+ if (this.isOwner) {
438
+ await this.loadSqlLoggingConfig();
439
+ await this.loadActiveSessions();
440
+ this.startAutoRefresh();
441
+ }
442
+ }
443
+ /**
444
+ * Component cleanup.
445
+ * Stops auto-refresh and cleans up subscriptions.
446
+ */
447
+ ngOnDestroy() {
448
+ this.destroy$.next();
449
+ this.destroy$.complete();
450
+ }
451
+ /**
452
+ * Starts the auto-refresh timer for session data.
453
+ * Only refreshes when autoRefresh is enabled and user is Owner.
454
+ *
455
+ * @private
456
+ */
457
+ startAutoRefresh() {
458
+ interval(this.refreshInterval)
459
+ .pipe(takeUntil(this.destroy$))
460
+ .subscribe(() => {
461
+ if (this.autoRefresh && this.isOwner) {
462
+ this.loadActiveSessions();
463
+ if (this.selectedSession) {
464
+ this.loadSessionLog(this.selectedSession);
465
+ }
466
+ }
467
+ });
468
+ }
469
+ /**
470
+ * Checks if the current user has Owner privileges required for SQL logging.
471
+ * Updates the isOwner flag and handles error states.
472
+ *
473
+ * @private
474
+ */
475
+ async checkUserPermissions() {
476
+ try {
477
+ // Try multiple ways to get the current user
478
+ const md = new Metadata();
479
+ const currentUser = md.CurrentUser;
480
+ console.log('Method 1 - Metadata.CurrentUser:', {
481
+ email: currentUser?.Email,
482
+ type: currentUser?.Type,
483
+ name: currentUser?.Name,
484
+ id: currentUser?.ID
485
+ });
486
+ // Use the current user from Metadata
487
+ const userToCheck = currentUser;
488
+ if (userToCheck && userToCheck.Type?.trim().toLowerCase() === 'owner') {
489
+ this.isOwner = true;
490
+ console.log('User is an Owner - SQL logging features enabled');
491
+ }
492
+ else {
493
+ this.isOwner = false;
494
+ this.error = 'SQL logging requires Owner privileges';
495
+ console.log('User is NOT an Owner. Type:', userToCheck?.Type);
496
+ // Also check if it's a string comparison issue
497
+ if (userToCheck) {
498
+ console.log('Type value (raw):', JSON.stringify(userToCheck.Type));
499
+ console.log('Type trimmed:', JSON.stringify(userToCheck.Type?.trim()));
500
+ console.log('Type comparison:', userToCheck.Type?.trim().toLowerCase(), '===', 'owner', ':', userToCheck.Type?.trim().toLowerCase() === 'owner');
501
+ }
502
+ }
503
+ }
504
+ catch (error) {
505
+ console.error('Error checking user permissions:', error);
506
+ this.isOwner = false;
507
+ this.error = 'Error checking permissions';
508
+ }
509
+ }
510
+ /**
511
+ * Opens the dialog for creating a new SQL logging session.
512
+ * Sets default values for session name and filename.
513
+ */
514
+ openStartSessionDialog() {
515
+ // Set default session name
516
+ const currentUser = new Metadata().CurrentUser;
517
+ this.newSessionOptions.sessionName = `SQL Logging - ${currentUser?.Name || currentUser?.Email || 'Unknown'} - ${new Date().toLocaleString()}`;
518
+ this.newSessionOptions.fileName = `sql-log-${new Date().toISOString().replace(/[:.]/g, '-')}.sql`;
519
+ this.showStartSessionDialog = true;
520
+ }
521
+ /**
522
+ * Creates and starts a new SQL logging session with the configured options.
523
+ * Shows success/error notifications and refreshes the sessions list.
524
+ */
525
+ async startNewSession() {
526
+ try {
527
+ this.loading = true;
528
+ const dataProvider = Metadata.Provider;
529
+ const mutation = `
530
+ mutation StartSqlLogging($input: StartSqlLoggingInput!) {
531
+ startSqlLogging(input: $input) {
532
+ id
533
+ filePath
534
+ startTime
535
+ statementCount
536
+ sessionName
537
+ filterByUserId
538
+ options {
539
+ formatAsMigration
540
+ statementTypes
541
+ prettyPrint
542
+ }
543
+ }
544
+ }
545
+ `;
546
+ const variables = {
547
+ input: {
548
+ fileName: this.newSessionOptions.fileName,
549
+ filterToCurrentUser: this.newSessionOptions.filterToCurrentUser,
550
+ options: {
551
+ formatAsMigration: this.newSessionOptions.formatAsMigration,
552
+ statementTypes: this.newSessionOptions.statementTypes,
553
+ prettyPrint: this.newSessionOptions.prettyPrint,
554
+ sessionName: this.newSessionOptions.sessionName
555
+ }
556
+ }
557
+ };
558
+ const result = await dataProvider.ExecuteGQL(mutation, variables);
559
+ if (result.errors) {
560
+ throw new Error(result.errors[0].message);
561
+ }
562
+ const newSession = result?.startSqlLogging;
563
+ if (!newSession) {
564
+ throw new Error('Failed to start SQL logging session - no session data returned');
565
+ }
566
+ MJNotificationService.Instance.CreateSimpleNotification(`SQL logging session started: ${newSession.sessionName}`, 'success', 5000);
567
+ this.showStartSessionDialog = false;
568
+ await this.loadActiveSessions();
569
+ this.selectSession(newSession);
570
+ }
571
+ catch (error) {
572
+ console.error('Error starting SQL logging session:', error);
573
+ MJNotificationService.Instance.CreateSimpleNotification(`Error: ${error.message || 'Failed to start SQL logging session'}`, 'error', 5000);
574
+ }
575
+ finally {
576
+ this.loading = false;
577
+ }
578
+ }
579
+ /**
580
+ * Stops a specific SQL logging session.
581
+ *
582
+ * @param session - The session object to stop
583
+ * @param event - Optional event to stop propagation
584
+ */
585
+ async stopSession(session, event) {
586
+ if (event) {
587
+ event.stopPropagation();
588
+ }
589
+ if (!confirm(`Stop SQL logging session "${session.sessionName}"?`)) {
590
+ return;
591
+ }
592
+ try {
593
+ this.loading = true;
594
+ const dataProvider = Metadata.Provider;
595
+ const mutation = `
596
+ mutation StopSqlLogging($sessionId: String!) {
597
+ stopSqlLogging(sessionId: $sessionId)
598
+ }
599
+ `;
600
+ const result = await dataProvider.ExecuteGQL(mutation, { sessionId: session.id });
601
+ if (result.errors) {
602
+ throw new Error(result.errors[0].message);
603
+ }
604
+ MJNotificationService.Instance.CreateSimpleNotification('SQL logging session stopped', 'success', 3000);
605
+ if (this.selectedSession?.id === session.id) {
606
+ this.selectedSession = null;
607
+ this.logContent = '';
608
+ }
609
+ await this.loadActiveSessions();
610
+ }
611
+ catch (error) {
612
+ console.error('Error stopping SQL logging session:', error);
613
+ MJNotificationService.Instance.CreateSimpleNotification(`Error: ${error.message || 'Failed to stop SQL logging session'}`, 'error', 5000);
614
+ }
615
+ finally {
616
+ this.loading = false;
617
+ }
618
+ }
619
+ /**
620
+ * Stops all currently active SQL logging sessions.
621
+ * Prompts for confirmation before proceeding.
622
+ */
623
+ async stopAllSessions() {
624
+ if (!confirm('Stop ALL active SQL logging sessions?')) {
625
+ return;
626
+ }
627
+ try {
628
+ this.loading = true;
629
+ const dataProvider = Metadata.Provider;
630
+ const mutation = `
631
+ mutation StopAllSqlLogging {
632
+ stopAllSqlLogging
633
+ }
634
+ `;
635
+ const result = await dataProvider.ExecuteGQL(mutation, {});
636
+ if (result.errors) {
637
+ throw new Error(result.errors[0].message);
638
+ }
639
+ MJNotificationService.Instance.CreateSimpleNotification('All SQL logging sessions stopped', 'success', 3000);
640
+ this.selectedSession = null;
641
+ this.logContent = '';
642
+ await this.loadActiveSessions();
643
+ }
644
+ catch (error) {
645
+ console.error('Error stopping all SQL logging sessions:', error);
646
+ MJNotificationService.Instance.CreateSimpleNotification(`Error: ${error.message || 'Failed to stop all SQL logging sessions'}`, 'error', 5000);
647
+ }
648
+ finally {
649
+ this.loading = false;
650
+ }
651
+ }
652
+ /**
653
+ * Selects a session for viewing and loads its log content.
654
+ *
655
+ * @param session - The session to select
656
+ */
657
+ selectSession(session) {
658
+ this.selectedSession = session;
659
+ this.loadSessionLog(session);
660
+ }
661
+ /**
662
+ * Loads the log file content for a specific session using real-time GraphQL query.
663
+ * Reads actual SQL statements from the log file on the server.
664
+ *
665
+ * @param session - The session whose log to load
666
+ */
667
+ async loadSessionLog(session) {
668
+ try {
669
+ const dataProvider = Metadata.Provider;
670
+ const query = `
671
+ query ReadSqlLogFile($sessionId: String!, $maxLines: Int) {
672
+ readSqlLogFile(sessionId: $sessionId, maxLines: $maxLines)
673
+ }
674
+ `;
675
+ const variables = {
676
+ sessionId: session.id,
677
+ maxLines: 1000 // Limit to last 1000 lines for performance
678
+ };
679
+ const result = await dataProvider.ExecuteGQL(query, variables);
680
+ if (result.errors) {
681
+ throw new Error(result.errors[0].message);
682
+ }
683
+ const logContent = result?.readSqlLogFile || '';
684
+ // Add session header information (show only filename for security)
685
+ const fileName = session.filePath ? session.filePath.split(/[\\\/]/).pop() : 'unknown';
686
+ const header = `-- =====================================================\n` +
687
+ `-- SQL Log File: ${fileName}\n` +
688
+ `-- Session: ${session.sessionName}\n` +
689
+ `-- Started: ${new Date(session.startTime).toLocaleString()}\n` +
690
+ `-- Statements Captured: ${session.statementCount}\n` +
691
+ `-- User Filter: ${session.filterByUserId || 'All Users'}\n` +
692
+ `-- Statement Types: ${session.options?.statementTypes || 'both'}\n` +
693
+ `-- Pretty Print: ${session.options?.prettyPrint ? 'Yes' : 'No'}\n` +
694
+ `-- Migration Format: ${session.options?.formatAsMigration ? 'Yes' : 'No'}\n` +
695
+ `-- =====================================================\n\n`;
696
+ this.logContent = header + (logContent || '-- No SQL statements captured yet --');
697
+ }
698
+ catch (error) {
699
+ console.error('Error loading session log:', error);
700
+ this.logContent = `-- Error loading log file --\n-- ${error.message || 'Unknown error occurred'} --\n\n` +
701
+ `-- Session Info --\n` +
702
+ `-- File: ${session.filePath}\n` +
703
+ `-- Session: ${session.sessionName}\n` +
704
+ `-- Started: ${new Date(session.startTime).toLocaleString()}\n`;
705
+ }
706
+ }
707
+ /**
708
+ * Loads the SQL logging configuration from the server.
709
+ * Updates component state with current settings and capabilities.
710
+ *
711
+ * @private
712
+ */
713
+ async loadSqlLoggingConfig() {
714
+ try {
715
+ const dataProvider = Metadata.Provider;
716
+ const query = `
717
+ query SqlLoggingConfig {
718
+ sqlLoggingConfig {
719
+ enabled
720
+ allowedLogDirectory
721
+ maxActiveSessions
722
+ autoCleanupEmptyFiles
723
+ sessionTimeout
724
+ activeSessionCount
725
+ defaultOptions {
726
+ formatAsMigration
727
+ statementTypes
728
+ batchSeparator
729
+ prettyPrint
730
+ logRecordChangeMetadata
731
+ retainEmptyLogFiles
732
+ }
733
+ }
734
+ }
735
+ `;
736
+ const result = await dataProvider.ExecuteGQL(query, {});
737
+ if (result.errors) {
738
+ throw new Error(result.errors[0].message);
739
+ }
740
+ // Debug logging to understand the response structure
741
+ console.log('SQL Logging Config Result:', result);
742
+ console.log('Result keys:', Object.keys(result || {}));
743
+ console.log('Direct result.sqlLoggingConfig:', result?.sqlLoggingConfig);
744
+ // Access the data directly from the result, matching AI prompt pattern
745
+ const configData = result?.sqlLoggingConfig;
746
+ console.log('Extracted config data:', configData);
747
+ this.sqlLoggingConfig = configData || null;
748
+ this.configEnabled = this.sqlLoggingConfig?.enabled || false;
749
+ console.log('Component state after update:');
750
+ console.log(' this.sqlLoggingConfig:', this.sqlLoggingConfig);
751
+ console.log(' this.configEnabled:', this.configEnabled);
752
+ console.log(' this.isOwner:', this.isOwner);
753
+ }
754
+ catch (error) {
755
+ console.error('Error loading SQL logging config:', error);
756
+ this.error = error.message || 'Failed to load SQL logging configuration';
757
+ }
758
+ }
759
+ /**
760
+ * Loads the list of currently active SQL logging sessions.
761
+ * Updates the activeSessions array and handles session selection state.
762
+ *
763
+ * @private
764
+ */
765
+ async loadActiveSessions() {
766
+ try {
767
+ const dataProvider = Metadata.Provider;
768
+ const query = `
769
+ query ActiveSqlLoggingSessions {
770
+ activeSqlLoggingSessions {
771
+ id
772
+ filePath
773
+ startTime
774
+ statementCount
775
+ sessionName
776
+ filterByUserId
777
+ options {
778
+ formatAsMigration
779
+ statementTypes
780
+ prettyPrint
781
+ }
782
+ }
783
+ }
784
+ `;
785
+ const result = await dataProvider.ExecuteGQL(query, {});
786
+ if (result.errors) {
787
+ throw new Error(result.errors[0].message);
788
+ }
789
+ // Debug logging to understand the response structure
790
+ console.log('Active Sessions Result:', result);
791
+ console.log('Result keys:', Object.keys(result || {}));
792
+ // Access the data directly from the result, matching AI prompt pattern
793
+ const sessionsData = result?.activeSqlLoggingSessions;
794
+ console.log('Extracted sessions data:', sessionsData);
795
+ this.activeSessions = sessionsData || [];
796
+ // Update selected session if it still exists
797
+ if (this.selectedSession) {
798
+ const stillExists = this.activeSessions.find(s => s.id === this.selectedSession.id);
799
+ if (stillExists) {
800
+ this.selectedSession = stillExists;
801
+ }
802
+ else {
803
+ this.selectedSession = null;
804
+ this.logContent = '';
805
+ }
806
+ }
807
+ }
808
+ catch (error) {
809
+ console.error('Error loading active sessions:', error);
810
+ }
811
+ }
812
+ /**
813
+ * Calculates and formats the duration of a logging session.
814
+ *
815
+ * @param startTime - ISO string of when the session started
816
+ * @returns Formatted duration string (e.g., "2h 30m", "45m 23s", "12s")
817
+ */
818
+ getSessionDuration(startTime) {
819
+ const start = new Date(startTime);
820
+ const now = new Date();
821
+ const diff = now.getTime() - start.getTime();
822
+ const hours = Math.floor(diff / (1000 * 60 * 60));
823
+ const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
824
+ const seconds = Math.floor((diff % (1000 * 60)) / 1000);
825
+ if (hours > 0) {
826
+ return `${hours}h ${minutes}m`;
827
+ }
828
+ else if (minutes > 0) {
829
+ return `${minutes}m ${seconds}s`;
830
+ }
831
+ else {
832
+ return `${seconds}s`;
833
+ }
834
+ }
835
+ /**
836
+ * Refreshes user data and re-checks permissions.
837
+ * Useful when user privileges have been updated.
838
+ */
839
+ async refreshUserPermissions() {
840
+ console.log('Refreshing user permissions...');
841
+ this.loading = true;
842
+ try {
843
+ // Try to refresh SharedService data
844
+ await SharedService.RefreshData(false);
845
+ // Wait a moment for data to propagate
846
+ await new Promise(resolve => setTimeout(resolve, 1000));
847
+ // Re-check permissions
848
+ await this.checkUserPermissions();
849
+ console.log('Permissions refreshed');
850
+ }
851
+ catch (error) {
852
+ console.error('Error refreshing permissions:', error);
853
+ }
854
+ finally {
855
+ this.loading = false;
856
+ }
857
+ }
858
+ /**
859
+ * Extracts the filename from a full file path for security purposes.
860
+ * Only shows the filename, not the full server path.
861
+ *
862
+ * @param filePath - Full file path from server
863
+ * @returns Just the filename portion
864
+ */
865
+ getFileName(filePath) {
866
+ if (!filePath)
867
+ return 'unknown';
868
+ return filePath.split(/[\\\/]/).pop() || 'unknown';
869
+ }
870
+ /**
871
+ * Calculates the total number of SQL statements across all active sessions.
872
+ *
873
+ * @returns Total statement count
874
+ */
875
+ getTotalStatementCount() {
876
+ return this.activeSessions.reduce((sum, session) => sum + (session.statementCount || 0), 0);
877
+ }
878
+ /**
879
+ * Debug method to test contextUser flow for SQL filtering.
880
+ * This shows how the new architecture handles user context without storing email in provider.
881
+ */
882
+ async debugUserEmail() {
883
+ try {
884
+ this.loading = true;
885
+ const dataProvider = Metadata.Provider;
886
+ const query = `
887
+ query DebugCurrentUserEmail {
888
+ debugCurrentUserEmail
889
+ }
890
+ `;
891
+ const result = await dataProvider.ExecuteGQL(query, {});
892
+ if (result.errors) {
893
+ throw new Error(result.errors[0].message);
894
+ }
895
+ const debugInfo = result?.debugCurrentUserEmail || 'No debug info returned';
896
+ alert(`Context User Info:\n\n${debugInfo}\n\nThe system now passes user context through method calls instead of storing it in the provider.`);
897
+ }
898
+ catch (error) {
899
+ console.error('Error getting context user info:', error);
900
+ alert(`Error: ${error.message || 'Failed to get debug info'}`);
901
+ }
902
+ finally {
903
+ this.loading = false;
904
+ }
905
+ }
906
+ static ɵfac = function SqlLoggingComponent_Factory(t) { return new (t || SqlLoggingComponent)(i0.ɵɵdirectiveInject(i1.SharedService)); };
907
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SqlLoggingComponent, selectors: [["mj-sql-logging"]], standalone: true, features: [i0.ɵɵStandaloneFeature], decls: 10, vars: 8, consts: [[1, "sql-logging-container"], [1, "action-buttons"], [1, "btn-secondary", 3, "click", "disabled"], [1, "fa-solid", "fa-refresh"], ["title", "Start SQL logging session", 1, "btn-primary", 3, "disabled"], [1, "stats-grid", 2, "display", "flex"], [1, "loading-container"], [1, "content-area"], [1, "modal-backdrop"], ["title", "Start SQL logging session", 1, "btn-primary", 3, "click", "disabled"], [1, "fa-solid", "fa-play"], [1, "stat-card"], [1, "stat-icon", "stat-icon-status"], [1, "fa-solid", "fa-power-off"], [1, "stat-content"], [1, "stat-value"], [1, "stat-label"], [1, "stat-icon", "stat-icon-active"], [1, "fa-solid", "fa-play-circle"], [1, "stat-icon", "stat-icon-limit"], [1, "fa-solid", "fa-gauge-high"], [1, "stat-icon", "stat-icon-total"], [1, "fa-solid", "fa-database"], [1, "loading-spinner"], [1, "spinner-ring"], [1, "loading-text"], [1, "empty-state"], [1, "sessions-layout"], [1, "fa-solid", "fa-lock", "empty-icon"], [1, "empty-text"], [1, "empty-subtext"], [1, "btn-secondary", 2, "margin-top", "1rem", 3, "click"], [1, "fa-solid", "fa-sync"], [1, "fa-solid", "fa-exclamation-triangle", "empty-icon", "warning"], [1, "info-box"], [1, "fa-solid", "fa-file-code", "empty-icon"], [1, "btn-primary", 2, "margin-top", "1rem", 3, "click"], [1, "sessions-panel"], [1, "panel-header"], [1, "panel-title"], ["title", "Stop all sessions", 1, "btn-danger", "btn-small", 3, "disabled"], [1, "sessions-list"], [1, "session-card", 3, "selected"], [1, "log-viewer-panel"], ["title", "Stop all sessions", 1, "btn-danger", "btn-small", 3, "click", "disabled"], [1, "fa-solid", "fa-stop"], [1, "session-card", 3, "click"], [1, "session-header"], [1, "session-info"], [1, "session-title"], [1, "session-meta"], [1, "meta-item"], [1, "fa-solid", "fa-clock"], ["title", "Stop session", 1, "action-btn", "action-btn-danger", 3, "click"], [1, "session-badges"], [1, "badge", "badge-user"], [1, "badge", "badge-migration"], [1, "badge", "badge-type"], [1, "fa-solid", "fa-user"], [1, "fa-solid", "fa-code-branch"], [1, "panel-actions"], [1, "checkbox-label"], ["type", "checkbox", 3, "ngModelChange", "ngModel"], ["title", "Refresh log", 1, "action-btn", 3, "click"], [1, "log-content"], [2, "height", "100%", 3, "value", "readonly", "disabled", "language", "setup", "lineWrapping", "highlightWhitespace"], [1, "fa-solid", "fa-arrow-left", "empty-icon"], [1, "modal-backdrop", 3, "click"], [1, "modal-dialog", "modal-large", 3, "click"], [1, "modal-header"], [1, "modal-title"], [1, "modal-close", 3, "click"], [1, "fa-solid", "fa-times"], [1, "modal-body"], [1, "form-group"], [1, "form-label"], ["type", "text", "placeholder", "Enter a descriptive name for this session", 1, "form-input", 3, "ngModelChange", "ngModel"], ["type", "text", "placeholder", "sql-log-2024-01-01.sql", 1, "form-input", 3, "ngModelChange", "ngModel"], [1, "form-hint"], [1, "form-select", 3, "ngModelChange", "ngModel"], [3, "value"], [1, "form-checkboxes"], [1, "modal-footer"], [1, "btn-secondary", 3, "click"], [1, "btn-primary", 3, "click", "disabled"]], template: function SqlLoggingComponent_Template(rf, ctx) { if (rf & 1) {
908
+ i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "button", 2);
909
+ i0.ɵɵlistener("click", function SqlLoggingComponent_Template_button_click_2_listener() { return ctx.loadActiveSessions(); });
910
+ i0.ɵɵelement(3, "i", 3);
911
+ i0.ɵɵtext(4, " Refresh ");
912
+ i0.ɵɵelementEnd();
913
+ i0.ɵɵtemplate(5, SqlLoggingComponent_Conditional_5_Template, 3, 1, "button", 4);
914
+ i0.ɵɵelementEnd();
915
+ i0.ɵɵtemplate(6, SqlLoggingComponent_Conditional_6_Template, 33, 4, "div", 5)(7, SqlLoggingComponent_Conditional_7_Template, 7, 0, "div", 6)(8, SqlLoggingComponent_Conditional_8_Template, 5, 1, "div", 7)(9, SqlLoggingComponent_Conditional_9_Template, 41, 7, "div", 8);
916
+ i0.ɵɵelementEnd();
917
+ } if (rf & 2) {
918
+ i0.ɵɵadvance(2);
919
+ i0.ɵɵproperty("disabled", ctx.loading);
920
+ i0.ɵɵadvance();
921
+ i0.ɵɵclassProp("fa-spin", ctx.loading);
922
+ i0.ɵɵadvance(2);
923
+ i0.ɵɵconditional(ctx.isOwner && ctx.configEnabled ? 5 : -1);
924
+ i0.ɵɵadvance();
925
+ i0.ɵɵconditional(ctx.isOwner && ctx.configEnabled ? 6 : -1);
926
+ i0.ɵɵadvance();
927
+ i0.ɵɵconditional(ctx.loading && !ctx.activeSessions.length ? 7 : -1);
928
+ i0.ɵɵadvance();
929
+ i0.ɵɵconditional(!ctx.loading || ctx.activeSessions.length > 0 ? 8 : -1);
930
+ i0.ɵɵadvance();
931
+ i0.ɵɵconditional(ctx.showStartSessionDialog ? 9 : -1);
932
+ } }, dependencies: [CommonModule,
933
+ FormsModule, i2.NgSelectOption, i2.ɵNgSelectMultipleOption, i2.DefaultValueAccessor, i2.CheckboxControlValueAccessor, i2.SelectControlValueAccessor, i2.NgControlStatus, i2.NgModel, SharedSettingsModule,
934
+ CodeEditorModule, i3.CodeEditorComponent], styles: ["@import '../shared/styles/variables';\n@import '../shared/styles/mixins';\n\n.sql-logging-container[_ngcontent-%COMP%] {\n @include scrollable-container;\n max-width: 1400px;\n margin: 0 auto;\n padding: 2rem;\n}\n\n//[_ngcontent-%COMP%] Action[_ngcontent-%COMP%] Buttons\n.action-buttons[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.75rem;\n justify-content: flex-end;\n margin-bottom: 1.5rem;\n\n @media (max-width: 768px) {\n justify-content: center;\n flex-wrap: wrap;\n }\n}\n\n//[_ngcontent-%COMP%] Buttons\n.btn-primary[_ngcontent-%COMP%] {\n @include button-base;\n background-color: #2196f3;\n color: white;\n \n &:hover {\n background-color: #1976d2;\n transform: translateY(-1px);\n box-shadow: 0 4px 12px rgba(33, 150, 243, 0.3);\n }\n}\n\n.btn-secondary[_ngcontent-%COMP%] {\n @include button-base;\n background-color: #ffffff;\n color: #374151;\n border: 1px solid #e5e7eb;\n \n &:hover {\n background-color: #f9fafb;\n border-color: #2196f3;\n color: #2196f3;\n }\n}\n\n.btn-danger[_ngcontent-%COMP%] {\n @include button-base;\n background-color: #f44336;\n color: white;\n \n &:hover {\n background-color: #d32f2f;\n }\n \n &.btn-small {\n padding: 0.375rem 0.75rem;\n font-size: 0.875rem;\n }\n}\n\n//[_ngcontent-%COMP%] Stats[_ngcontent-%COMP%] Grid\n.stats-grid[_ngcontent-%COMP%] {\n display: grid !important;\n grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n gap: 1.5rem;\n margin-bottom: 2rem;\n width: 100%;\n\n @media (max-width: 768px) {\n grid-template-columns: repeat(2, 1fr);\n gap: 1rem;\n }\n}\n\n.stat-card[_ngcontent-%COMP%] {\n background: white;\n border-radius: 12px;\n padding: 1.5rem;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n display: flex;\n margin-right: 10px;\n align-items: center;\n gap: 1rem;\n transition: all 0.3s ease;\n min-width: 0; // Prevent grid blowout\n\n &:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);\n }\n}\n\n.stat-icon[_ngcontent-%COMP%] {\n width: 60px;\n height: 60px;\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.5rem;\n\n &-status {\n background: rgba(76, 175, 80, 0.1);\n color: #4caf50;\n }\n\n &-active {\n background: rgba(33, 150, 243, 0.1);\n color: #2196f3;\n }\n\n &-limit {\n background: rgba(255, 152, 0, 0.1);\n color: #ff9800;\n }\n\n &-total {\n background: rgba(156, 39, 176, 0.1);\n color: #9c27b0;\n }\n}\n\n.stat-content[_ngcontent-%COMP%] {\n flex: 1;\n\n .stat-value {\n font-size: 2rem;\n font-weight: 700;\n color: #1f2937;\n line-height: 1;\n }\n\n .stat-label {\n color: #6b7280;\n font-size: 0.875rem;\n margin-top: 0.25rem;\n }\n}\n\n//[_ngcontent-%COMP%] Content[_ngcontent-%COMP%] Area\n.content-area[_ngcontent-%COMP%] {\n @include scrollable-content;\n background: white;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n padding: 1.5rem;\n min-height: 400px;\n}\n\n//[_ngcontent-%COMP%] Sessions[_ngcontent-%COMP%] Layout\n.sessions-layout[_ngcontent-%COMP%] {\n display: flex;\n gap: 1.5rem;\n height: calc(100vh - 450px); // Dynamic height based on viewport\n min-height: 400px;\n max-height: 600px;\n\n @media (max-width: 1024px) {\n flex-direction: column;\n height: auto;\n max-height: none;\n }\n}\n\n.sessions-panel[_ngcontent-%COMP%] {\n flex: 0 0 400px;\n display: flex;\n flex-direction: column;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n overflow: hidden;\n\n @media (max-width: 1024px) {\n flex: 1;\n max-height: 400px;\n }\n}\n\n.panel-header[_ngcontent-%COMP%] {\n padding: 1rem;\n background: #f9fafb;\n border-bottom: 1px solid #e5e7eb;\n display: flex;\n justify-content: space-between;\n align-items: center;\n\n .panel-title {\n margin: 0;\n font-size: 1.125rem;\n font-weight: 600;\n color: #1f2937;\n }\n}\n\n.panel-actions[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 1rem;\n}\n\n.sessions-list[_ngcontent-%COMP%] {\n @include scrollable-content;\n padding: 0.75rem;\n}\n\n.session-card[_ngcontent-%COMP%] {\n background: white;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n padding: 1rem;\n margin-bottom: 0.75rem;\n cursor: pointer;\n transition: all 0.2s;\n\n &:hover {\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n &.selected {\n border-color: #2196f3;\n box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1);\n }\n}\n\n.session-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 0.75rem;\n}\n\n.session-info[_ngcontent-%COMP%] {\n flex: 1;\n \n .session-title {\n margin: 0 0 0.5rem 0;\n font-size: 0.95rem;\n font-weight: 600;\n color: #1f2937;\n }\n}\n\n.session-meta[_ngcontent-%COMP%] {\n display: flex;\n gap: 1rem;\n font-size: 0.75rem;\n color: #6b7280;\n\n .meta-item {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n }\n}\n\n.session-badges[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.5rem;\n flex-wrap: wrap;\n}\n\n.badge[_ngcontent-%COMP%] {\n padding: 0.25rem 0.5rem;\n border-radius: 12px;\n font-size: 0.625rem;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 0.25rem;\n\n &-user {\n background: rgba(33, 150, 243, 0.1);\n color: #2196f3;\n }\n\n &-migration {\n background: rgba(156, 39, 176, 0.1);\n color: #9c27b0;\n }\n\n &-type {\n background: rgba(255, 152, 0, 0.1);\n color: #ff9800;\n }\n}\n\n.action-btn[_ngcontent-%COMP%] {\n padding: 0.375rem;\n border: none;\n background: transparent;\n color: #6b7280;\n font-size: 0.875rem;\n border-radius: 4px;\n cursor: pointer;\n transition: all 0.2s;\n\n &:hover {\n background: #f3f4f6;\n color: #374151;\n }\n\n &-danger:hover {\n color: #f44336;\n }\n}\n\n//[_ngcontent-%COMP%] Log[_ngcontent-%COMP%] Viewer[_ngcontent-%COMP%] Panel\n.log-viewer-panel[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n overflow: hidden;\n}\n\n.log-content[_ngcontent-%COMP%] {\n @include scrollable-content;\n background: #1e1e1e;\n color: #d4d4d4;\n padding: 0; // Remove padding for code editor\n min-height: 300px;\n height: 100%;\n position: relative;\n \n // Code editor styles\n ::ng-deep {\n .cm-editor {\n height: 100%;\n font-family: 'Consolas', 'Monaco', 'Courier New', monospace;\n font-size: 0.875rem;\n }\n \n .cm-scroller {\n font-family: inherit;\n }\n \n // Dark theme adjustments to match previous style\n .cm-content {\n background: #1e1e1e;\n color: #d4d4d4;\n }\n \n .cm-gutters {\n background: #252526;\n color: #858585;\n border-right: 1px solid #464647;\n }\n \n .cm-activeLineGutter {\n background: #2a2a2a;\n }\n \n .cm-activeLine {\n background: rgba(255, 255, 255, 0.04);\n }\n }\n}\n\n\n//[_ngcontent-%COMP%] Empty[_ngcontent-%COMP%] State\n.empty-state[_ngcontent-%COMP%] {\n text-align: center;\n padding: 4rem 2rem;\n\n .empty-icon {\n font-size: 4rem;\n color: #e5e7eb;\n margin-bottom: 1rem;\n\n &.warning {\n color: #ffc107;\n }\n }\n\n .empty-text {\n font-size: 1.25rem;\n font-weight: 600;\n color: #374151;\n margin: 0 0 0.5rem 0;\n }\n\n .empty-subtext {\n color: #6b7280;\n margin: 0;\n }\n}\n\n.info-box[_ngcontent-%COMP%] {\n background: #f3f4f6;\n border-radius: 8px;\n padding: 1.5rem;\n margin-top: 2rem;\n text-align: left;\n max-width: 500px;\n margin-left: auto;\n margin-right: auto;\n\n h4 {\n margin: 0 0 1rem 0;\n color: #1f2937;\n }\n\n ol {\n margin: 0;\n padding-left: 1.25rem;\n color: #374151;\n \n li {\n margin-bottom: 0.5rem;\n }\n }\n\n code {\n background: #e5e7eb;\n padding: 0.125rem 0.375rem;\n border-radius: 4px;\n font-size: 0.875rem;\n }\n}\n\n//[_ngcontent-%COMP%] Loading[_ngcontent-%COMP%] State\n.loading-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 4rem 2rem;\n min-height: 400px;\n}\n\n.loading-spinner[_ngcontent-%COMP%] {\n position: relative;\n width: 60px;\n height: 60px;\n margin-bottom: 1rem;\n\n .spinner-ring {\n position: absolute;\n width: 100%;\n height: 100%;\n border: 3px solid transparent;\n border-radius: 50%;\n animation: _ngcontent-%COMP%_spin 1.5s cubic-bezier(0.5, 0, 0.5, 1) infinite;\n\n &:nth-child(1) {\n border-color: #2196f3 transparent transparent transparent;\n animation-delay: -0.45s;\n }\n\n &:nth-child(2) {\n border-color: transparent #9c27b0 transparent transparent;\n animation-delay: -0.3s;\n }\n\n &:nth-child(3) {\n border-color: transparent transparent #4caf50 transparent;\n animation-delay: -0.15s;\n }\n }\n}\n\n@keyframes _ngcontent-%COMP%_spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}\n\n.loading-text[_ngcontent-%COMP%] {\n color: #6b7280;\n font-size: 0.95rem;\n}\n\n//[_ngcontent-%COMP%] Modal[_ngcontent-%COMP%] Styles\n.modal-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n animation: fadeIn 0.2s ease;\n}\n\n.modal-dialog[_ngcontent-%COMP%] {\n background: white;\n border-radius: 12px;\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);\n max-width: 500px;\n width: 90%;\n max-height: 90vh;\n overflow: hidden;\n animation: _ngcontent-%COMP%_slideUp 0.3s ease;\n\n &.modal-large {\n max-width: 700px;\n }\n}\n\n.modal-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1.5rem;\n border-bottom: 1px solid #e5e7eb;\n\n .modal-title {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 1.25rem;\n font-weight: 600;\n color: #1f2937;\n margin: 0;\n }\n\n .modal-close {\n padding: 0.5rem;\n border: none;\n background: transparent;\n color: #6b7280;\n font-size: 1.25rem;\n cursor: pointer;\n border-radius: 6px;\n transition: all 0.2s;\n\n &:hover {\n background: #f3f4f6;\n color: #374151;\n }\n }\n}\n\n.modal-body[_ngcontent-%COMP%] {\n padding: 1.5rem;\n max-height: 60vh;\n overflow-y: auto;\n}\n\n.modal-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-end;\n gap: 0.75rem;\n padding: 1.5rem;\n border-top: 1px solid #e5e7eb;\n background: #f9fafb;\n}\n\n//[_ngcontent-%COMP%] Form[_ngcontent-%COMP%] Styles\n.form-group[_ngcontent-%COMP%] {\n margin-bottom: 1.5rem;\n}\n\n.form-label[_ngcontent-%COMP%] {\n display: block;\n font-size: 0.875rem;\n font-weight: 500;\n color: #374151;\n margin-bottom: 0.5rem;\n}\n\n.form-input[_ngcontent-%COMP%] {\n width: 100%;\n padding: 0.75rem 1rem;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n font-size: 0.95rem;\n transition: all 0.2s;\n\n &:focus {\n outline: none;\n border-color: #2196f3;\n box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1);\n }\n}\n\n.form-select[_ngcontent-%COMP%] {\n width: 100%;\n padding: 0.75rem 1rem;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n font-size: 0.95rem;\n background: white;\n cursor: pointer;\n transition: all 0.2s;\n\n &:focus {\n outline: none;\n border-color: #2196f3;\n box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1);\n }\n}\n\n.form-hint[_ngcontent-%COMP%] {\n font-size: 0.75rem;\n color: #6b7280;\n margin-top: 0.25rem;\n}\n\n.form-checkboxes[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.checkbox-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n color: #374151;\n font-size: 0.95rem;\n cursor: pointer;\n\n input[type=\"checkbox\"] {\n cursor: pointer;\n }\n}\n\n//[_ngcontent-%COMP%] Animations\n@keyframes[_ngcontent-%COMP%] fadeIn[_ngcontent-%COMP%] {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes _ngcontent-%COMP%_slideUp {\n from {\n transform: translateY(20px);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}"] });
935
+ }
936
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SqlLoggingComponent, [{
937
+ type: Component,
938
+ args: [{ selector: 'mj-sql-logging', standalone: true, imports: [
939
+ CommonModule,
940
+ FormsModule,
941
+ SharedSettingsModule,
942
+ CodeEditorModule
943
+ ], template: "<div class=\"sql-logging-container\">\n <!-- Action Buttons -->\n <div class=\"action-buttons\">\n <button class=\"btn-secondary\" (click)=\"loadActiveSessions()\" [disabled]=\"loading\">\n <i class=\"fa-solid fa-refresh\" [class.fa-spin]=\"loading\"></i>\n Refresh\n </button>\n @if (isOwner && configEnabled) {\n <button \n class=\"btn-primary\"\n [disabled]=\"loading || activeSessions.length >= (sqlLoggingConfig?.maxActiveSessions || 5)\"\n (click)=\"openStartSessionDialog()\"\n title=\"Start SQL logging session\"\n >\n <i class=\"fa-solid fa-play\"></i>\n Start New Session\n </button>\n }\n </div>\n\n <!-- Stats Cards -->\n @if (isOwner && configEnabled) {\n <div class=\"stats-grid\" style=\"display: flex\">\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-status\">\n <i class=\"fa-solid fa-power-off\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\">{{ configEnabled ? 'Enabled' : 'Disabled' }}</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\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\">{{ activeSessions.length }}</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\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\">{{ sqlLoggingConfig?.maxActiveSessions || 5 }}</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\"></i>\n </div>\n <div class=\"stat-content\">\n <div class=\"stat-value\">{{ getTotalStatementCount() }}</div>\n <div class=\"stat-label\">Total Statements</div>\n </div>\n </div>\n </div>\n }\n\n <!-- Loading State -->\n @if (loading && !activeSessions.length) {\n <div class=\"loading-container\">\n <div class=\"loading-spinner\">\n <div class=\"spinner-ring\"></div>\n <div class=\"spinner-ring\"></div>\n <div class=\"spinner-ring\"></div>\n </div>\n <div class=\"loading-text\">Loading SQL logging configuration...</div>\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\">\n <i class=\"fa-solid fa-lock empty-icon\"></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 \n class=\"btn-secondary\"\n (click)=\"refreshUserPermissions()\"\n style=\"margin-top: 1rem\"\n >\n <i class=\"fa-solid fa-sync\"></i>\n Refresh Permissions\n </button>\n </div>\n } @else if (!configEnabled) {\n <!-- Not enabled in config -->\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-exclamation-triangle empty-icon warning\"></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\">\n <i class=\"fa-solid fa-file-code empty-icon\"></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 \n class=\"btn-primary\"\n (click)=\"openStartSessionDialog()\"\n style=\"margin-top: 1rem\"\n >\n <i class=\"fa-solid fa-play\"></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\">\n <div class=\"panel-header\">\n <h3 class=\"panel-title\">Active Sessions</h3>\n @if (activeSessions.length > 0) {\n <button \n class=\"btn-danger btn-small\"\n (click)=\"stopAllSessions()\"\n [disabled]=\"loading\"\n title=\"Stop all sessions\"\n >\n <i class=\"fa-solid fa-stop\"></i>\n Stop All\n </button>\n }\n </div>\n <div class=\"sessions-list\">\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 >\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\"></i>\n {{ getSessionDuration(session.startTime) }}\n </span>\n <span class=\"meta-item\">\n <i class=\"fa-solid fa-database\"></i>\n {{ session.statementCount }} statements\n </span>\n </div>\n </div>\n <button \n class=\"action-btn action-btn-danger\"\n (click)=\"stopSession(session, $event)\"\n title=\"Stop session\"\n >\n <i class=\"fa-solid fa-stop\"></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\"></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\"></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\">\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 />\n Auto-refresh\n </label>\n <button \n class=\"action-btn\"\n (click)=\"loadSessionLog(selectedSession)\"\n title=\"Refresh log\"\n >\n <i class=\"fa-solid fa-sync\" [class.fa-spin]=\"loading\"></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 ></mj-code-editor>\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-arrow-left empty-icon\"></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 class=\"modal-backdrop\" (click)=\"showStartSessionDialog = false\">\n <div class=\"modal-dialog modal-large\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3 class=\"modal-title\">\n <i class=\"fa-solid fa-play\"></i>\n Start SQL Logging Session\n </h3>\n <button class=\"modal-close\" (click)=\"showStartSessionDialog = false\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"modal-body\">\n <div class=\"form-group\">\n <label class=\"form-label\">Session Name</label>\n <input \n type=\"text\"\n class=\"form-input\" \n [(ngModel)]=\"newSessionOptions.sessionName\"\n placeholder=\"Enter a descriptive name for this session\"\n />\n </div>\n \n <div class=\"form-group\">\n <label class=\"form-label\">File Name</label>\n <input \n type=\"text\"\n class=\"form-input\" \n [(ngModel)]=\"newSessionOptions.fileName\"\n placeholder=\"sql-log-2024-01-01.sql\"\n />\n <div 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\">Statement Types</label>\n <select\n class=\"form-select\"\n [(ngModel)]=\"newSessionOptions.statementTypes\"\n >\n @for (option of statementTypeOptions; track option.value) {\n <option [value]=\"option.value\">{{ option.text }}</option>\n }\n </select>\n </div>\n \n <div class=\"form-checkboxes\">\n <label class=\"checkbox-label\">\n <input \n type=\"checkbox\" \n [(ngModel)]=\"newSessionOptions.filterToCurrentUser\"\n />\n Filter to my SQL statements only\n </label>\n \n <label class=\"checkbox-label\">\n <input \n type=\"checkbox\" \n [(ngModel)]=\"newSessionOptions.formatAsMigration\"\n />\n Format as migration file\n </label>\n \n <label class=\"checkbox-label\">\n <input \n type=\"checkbox\" \n [(ngModel)]=\"newSessionOptions.prettyPrint\"\n />\n Pretty print SQL statements\n </label>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button class=\"btn-secondary\" (click)=\"showStartSessionDialog = false\">\n Cancel\n </button>\n <button class=\"btn-primary\" (click)=\"startNewSession()\" [disabled]=\"loading\">\n <i class=\"fa-solid fa-play\"></i>\n Start Session\n </button>\n </div>\n </div>\n </div>\n }\n</div>", styles: ["@import '../shared/styles/variables';\n@import '../shared/styles/mixins';\n\n.sql-logging-container {\n @include scrollable-container;\n max-width: 1400px;\n margin: 0 auto;\n padding: 2rem;\n}\n\n// Action Buttons\n.action-buttons {\n display: flex;\n gap: 0.75rem;\n justify-content: flex-end;\n margin-bottom: 1.5rem;\n\n @media (max-width: 768px) {\n justify-content: center;\n flex-wrap: wrap;\n }\n}\n\n// Buttons\n.btn-primary {\n @include button-base;\n background-color: #2196f3;\n color: white;\n \n &:hover {\n background-color: #1976d2;\n transform: translateY(-1px);\n box-shadow: 0 4px 12px rgba(33, 150, 243, 0.3);\n }\n}\n\n.btn-secondary {\n @include button-base;\n background-color: #ffffff;\n color: #374151;\n border: 1px solid #e5e7eb;\n \n &:hover {\n background-color: #f9fafb;\n border-color: #2196f3;\n color: #2196f3;\n }\n}\n\n.btn-danger {\n @include button-base;\n background-color: #f44336;\n color: white;\n \n &:hover {\n background-color: #d32f2f;\n }\n \n &.btn-small {\n padding: 0.375rem 0.75rem;\n font-size: 0.875rem;\n }\n}\n\n// Stats Grid\n.stats-grid {\n display: grid !important;\n grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n gap: 1.5rem;\n margin-bottom: 2rem;\n width: 100%;\n\n @media (max-width: 768px) {\n grid-template-columns: repeat(2, 1fr);\n gap: 1rem;\n }\n}\n\n.stat-card {\n background: white;\n border-radius: 12px;\n padding: 1.5rem;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n display: flex;\n margin-right: 10px;\n align-items: center;\n gap: 1rem;\n transition: all 0.3s ease;\n min-width: 0; // Prevent grid blowout\n\n &:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);\n }\n}\n\n.stat-icon {\n width: 60px;\n height: 60px;\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.5rem;\n\n &-status {\n background: rgba(76, 175, 80, 0.1);\n color: #4caf50;\n }\n\n &-active {\n background: rgba(33, 150, 243, 0.1);\n color: #2196f3;\n }\n\n &-limit {\n background: rgba(255, 152, 0, 0.1);\n color: #ff9800;\n }\n\n &-total {\n background: rgba(156, 39, 176, 0.1);\n color: #9c27b0;\n }\n}\n\n.stat-content {\n flex: 1;\n\n .stat-value {\n font-size: 2rem;\n font-weight: 700;\n color: #1f2937;\n line-height: 1;\n }\n\n .stat-label {\n color: #6b7280;\n font-size: 0.875rem;\n margin-top: 0.25rem;\n }\n}\n\n// Content Area\n.content-area {\n @include scrollable-content;\n background: white;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n padding: 1.5rem;\n min-height: 400px;\n}\n\n// Sessions Layout\n.sessions-layout {\n display: flex;\n gap: 1.5rem;\n height: calc(100vh - 450px); // Dynamic height based on viewport\n min-height: 400px;\n max-height: 600px;\n\n @media (max-width: 1024px) {\n flex-direction: column;\n height: auto;\n max-height: none;\n }\n}\n\n.sessions-panel {\n flex: 0 0 400px;\n display: flex;\n flex-direction: column;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n overflow: hidden;\n\n @media (max-width: 1024px) {\n flex: 1;\n max-height: 400px;\n }\n}\n\n.panel-header {\n padding: 1rem;\n background: #f9fafb;\n border-bottom: 1px solid #e5e7eb;\n display: flex;\n justify-content: space-between;\n align-items: center;\n\n .panel-title {\n margin: 0;\n font-size: 1.125rem;\n font-weight: 600;\n color: #1f2937;\n }\n}\n\n.panel-actions {\n display: flex;\n align-items: center;\n gap: 1rem;\n}\n\n.sessions-list {\n @include scrollable-content;\n padding: 0.75rem;\n}\n\n.session-card {\n background: white;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n padding: 1rem;\n margin-bottom: 0.75rem;\n cursor: pointer;\n transition: all 0.2s;\n\n &:hover {\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n &.selected {\n border-color: #2196f3;\n box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1);\n }\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 \n .session-title {\n margin: 0 0 0.5rem 0;\n font-size: 0.95rem;\n font-weight: 600;\n color: #1f2937;\n }\n}\n\n.session-meta {\n display: flex;\n gap: 1rem;\n font-size: 0.75rem;\n color: #6b7280;\n\n .meta-item {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n }\n}\n\n.session-badges {\n display: flex;\n gap: 0.5rem;\n flex-wrap: wrap;\n}\n\n.badge {\n padding: 0.25rem 0.5rem;\n border-radius: 12px;\n font-size: 0.625rem;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 0.25rem;\n\n &-user {\n background: rgba(33, 150, 243, 0.1);\n color: #2196f3;\n }\n\n &-migration {\n background: rgba(156, 39, 176, 0.1);\n color: #9c27b0;\n }\n\n &-type {\n background: rgba(255, 152, 0, 0.1);\n color: #ff9800;\n }\n}\n\n.action-btn {\n padding: 0.375rem;\n border: none;\n background: transparent;\n color: #6b7280;\n font-size: 0.875rem;\n border-radius: 4px;\n cursor: pointer;\n transition: all 0.2s;\n\n &:hover {\n background: #f3f4f6;\n color: #374151;\n }\n\n &-danger:hover {\n color: #f44336;\n }\n}\n\n// Log Viewer Panel\n.log-viewer-panel {\n flex: 1;\n display: flex;\n flex-direction: column;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n overflow: hidden;\n}\n\n.log-content {\n @include scrollable-content;\n background: #1e1e1e;\n color: #d4d4d4;\n padding: 0; // Remove padding for code editor\n min-height: 300px;\n height: 100%;\n position: relative;\n \n // Code editor styles\n ::ng-deep {\n .cm-editor {\n height: 100%;\n font-family: 'Consolas', 'Monaco', 'Courier New', monospace;\n font-size: 0.875rem;\n }\n \n .cm-scroller {\n font-family: inherit;\n }\n \n // Dark theme adjustments to match previous style\n .cm-content {\n background: #1e1e1e;\n color: #d4d4d4;\n }\n \n .cm-gutters {\n background: #252526;\n color: #858585;\n border-right: 1px solid #464647;\n }\n \n .cm-activeLineGutter {\n background: #2a2a2a;\n }\n \n .cm-activeLine {\n background: rgba(255, 255, 255, 0.04);\n }\n }\n}\n\n\n// Empty State\n.empty-state {\n text-align: center;\n padding: 4rem 2rem;\n\n .empty-icon {\n font-size: 4rem;\n color: #e5e7eb;\n margin-bottom: 1rem;\n\n &.warning {\n color: #ffc107;\n }\n }\n\n .empty-text {\n font-size: 1.25rem;\n font-weight: 600;\n color: #374151;\n margin: 0 0 0.5rem 0;\n }\n\n .empty-subtext {\n color: #6b7280;\n margin: 0;\n }\n}\n\n.info-box {\n background: #f3f4f6;\n border-radius: 8px;\n padding: 1.5rem;\n margin-top: 2rem;\n text-align: left;\n max-width: 500px;\n margin-left: auto;\n margin-right: auto;\n\n h4 {\n margin: 0 0 1rem 0;\n color: #1f2937;\n }\n\n ol {\n margin: 0;\n padding-left: 1.25rem;\n color: #374151;\n \n li {\n margin-bottom: 0.5rem;\n }\n }\n\n code {\n background: #e5e7eb;\n padding: 0.125rem 0.375rem;\n border-radius: 4px;\n font-size: 0.875rem;\n }\n}\n\n// Loading State\n.loading-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 4rem 2rem;\n min-height: 400px;\n}\n\n.loading-spinner {\n position: relative;\n width: 60px;\n height: 60px;\n margin-bottom: 1rem;\n\n .spinner-ring {\n position: absolute;\n width: 100%;\n height: 100%;\n border: 3px solid transparent;\n border-radius: 50%;\n animation: spin 1.5s cubic-bezier(0.5, 0, 0.5, 1) infinite;\n\n &:nth-child(1) {\n border-color: #2196f3 transparent transparent transparent;\n animation-delay: -0.45s;\n }\n\n &:nth-child(2) {\n border-color: transparent #9c27b0 transparent transparent;\n animation-delay: -0.3s;\n }\n\n &:nth-child(3) {\n border-color: transparent transparent #4caf50 transparent;\n animation-delay: -0.15s;\n }\n }\n}\n\n@keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}\n\n.loading-text {\n color: #6b7280;\n font-size: 0.95rem;\n}\n\n// Modal Styles\n.modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n animation: fadeIn 0.2s ease;\n}\n\n.modal-dialog {\n background: white;\n border-radius: 12px;\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);\n max-width: 500px;\n width: 90%;\n max-height: 90vh;\n overflow: hidden;\n animation: slideUp 0.3s ease;\n\n &.modal-large {\n max-width: 700px;\n }\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 #e5e7eb;\n\n .modal-title {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n font-size: 1.25rem;\n font-weight: 600;\n color: #1f2937;\n margin: 0;\n }\n\n .modal-close {\n padding: 0.5rem;\n border: none;\n background: transparent;\n color: #6b7280;\n font-size: 1.25rem;\n cursor: pointer;\n border-radius: 6px;\n transition: all 0.2s;\n\n &:hover {\n background: #f3f4f6;\n color: #374151;\n }\n }\n}\n\n.modal-body {\n padding: 1.5rem;\n max-height: 60vh;\n overflow-y: auto;\n}\n\n.modal-footer {\n display: flex;\n justify-content: flex-end;\n gap: 0.75rem;\n padding: 1.5rem;\n border-top: 1px solid #e5e7eb;\n background: #f9fafb;\n}\n\n// Form Styles\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: #374151;\n margin-bottom: 0.5rem;\n}\n\n.form-input {\n width: 100%;\n padding: 0.75rem 1rem;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n font-size: 0.95rem;\n transition: all 0.2s;\n\n &:focus {\n outline: none;\n border-color: #2196f3;\n box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1);\n }\n}\n\n.form-select {\n width: 100%;\n padding: 0.75rem 1rem;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n font-size: 0.95rem;\n background: white;\n cursor: pointer;\n transition: all 0.2s;\n\n &:focus {\n outline: none;\n border-color: #2196f3;\n box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1);\n }\n}\n\n.form-hint {\n font-size: 0.75rem;\n color: #6b7280;\n margin-top: 0.25rem;\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.5rem;\n color: #374151;\n font-size: 0.95rem;\n cursor: pointer;\n\n input[type=\"checkbox\"] {\n cursor: pointer;\n }\n}\n\n// Animations\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\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}"] }]
944
+ }], () => [{ type: i1.SharedService }], null); })();
945
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SqlLoggingComponent, { className: "SqlLoggingComponent", filePath: "src/lib/sql-logging/sql-logging.component.ts", lineNumber: 44 }); })();
946
+ //# sourceMappingURL=sql-logging.component.js.map