@memberjunction/ng-artifacts 5.2.0 → 5.3.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/components/artifact-viewer-panel.component.d.ts.map +1 -1
- package/dist/lib/components/artifact-viewer-panel.component.js +6 -0
- package/dist/lib/components/artifact-viewer-panel.component.js.map +1 -1
- package/dist/lib/components/plugins/data-artifact-viewer.component.d.ts +80 -4
- package/dist/lib/components/plugins/data-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/data-artifact-viewer.component.js +414 -178
- package/dist/lib/components/plugins/data-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/save-query-dialog.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/save-query-dialog.component.js +2 -0
- package/dist/lib/components/plugins/save-query-dialog.component.js.map +1 -1
- package/package.json +14 -14
|
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { Component, Output, EventEmitter } from '@angular/core';
|
|
8
8
|
import { RegisterClass } from '@memberjunction/global';
|
|
9
9
|
import { Metadata, RunQuery, CompositeKey, KeyValuePair } from '@memberjunction/core';
|
|
10
|
+
import { QueryEngine, ArtifactMetadataEngine } from '@memberjunction/core-entities';
|
|
10
11
|
import { resolveTargetEntity } from '@memberjunction/ng-query-viewer';
|
|
11
12
|
import { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';
|
|
12
13
|
import * as i0 from "@angular/core";
|
|
@@ -51,37 +52,146 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_9_T
|
|
|
51
52
|
i0.ɵɵadvance();
|
|
52
53
|
i0.ɵɵclassProp("fa-spin", ctx_r0.IsLoading);
|
|
53
54
|
} }
|
|
54
|
-
function
|
|
55
|
+
function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_10_Template(rf, ctx) { if (rf & 1) {
|
|
55
56
|
const _r3 = i0.ɵɵgetCurrentView();
|
|
56
57
|
i0.ɵɵelementStart(0, "button", 19);
|
|
57
|
-
i0.ɵɵlistener("click", function
|
|
58
|
+
i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_10_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnShowSaveDialog()); });
|
|
58
59
|
i0.ɵɵelement(1, "i", 20);
|
|
59
60
|
i0.ɵɵtext(2, " Save Query ");
|
|
60
61
|
i0.ɵɵelementEnd();
|
|
61
62
|
} }
|
|
62
|
-
function
|
|
63
|
+
function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_11_Template(rf, ctx) { if (rf & 1) {
|
|
63
64
|
const _r4 = i0.ɵɵgetCurrentView();
|
|
64
|
-
i0.ɵɵelementStart(0, "
|
|
65
|
-
i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_11_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnOpenSavedQuery()); });
|
|
65
|
+
i0.ɵɵelementStart(0, "span", 21);
|
|
66
66
|
i0.ɵɵelement(1, "i", 22);
|
|
67
|
-
i0.ɵɵtext(2
|
|
67
|
+
i0.ɵɵtext(2);
|
|
68
68
|
i0.ɵɵelementEnd();
|
|
69
|
+
i0.ɵɵelementStart(3, "button", 23);
|
|
70
|
+
i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_11_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnOpenSavedQuery()); });
|
|
71
|
+
i0.ɵɵelement(4, "i", 24);
|
|
72
|
+
i0.ɵɵtext(5, " Open Query ");
|
|
73
|
+
i0.ɵɵelementEnd();
|
|
74
|
+
} if (rf & 2) {
|
|
75
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
76
|
+
i0.ɵɵadvance(2);
|
|
77
|
+
i0.ɵɵtextInterpolate1(" Saved at v", ctx_r0.SavedAtVersion, " ");
|
|
69
78
|
} }
|
|
70
|
-
function
|
|
71
|
-
i0.ɵɵ
|
|
79
|
+
function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
80
|
+
const _r6 = i0.ɵɵgetCurrentView();
|
|
81
|
+
i0.ɵɵelementStart(0, "div", 31);
|
|
82
|
+
i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Conditional_8_Template_div_click_0_listener($event) { i0.ɵɵrestoreView(_r6); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
83
|
+
i0.ɵɵelementStart(1, "div", 32);
|
|
84
|
+
i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Conditional_8_Template_div_click_1_listener() { i0.ɵɵrestoreView(_r6); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnUpdateExistingQuery()); });
|
|
85
|
+
i0.ɵɵelement(2, "i", 33);
|
|
86
|
+
i0.ɵɵelementStart(3, "div")(4, "div", 34);
|
|
87
|
+
i0.ɵɵtext(5);
|
|
88
|
+
i0.ɵɵelementEnd();
|
|
89
|
+
i0.ɵɵelementStart(6, "div", 35);
|
|
90
|
+
i0.ɵɵtext(7, "Replace saved query SQL with this version's SQL");
|
|
91
|
+
i0.ɵɵelementEnd()()();
|
|
92
|
+
i0.ɵɵelementStart(8, "div", 32);
|
|
93
|
+
i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Conditional_8_Template_div_click_8_listener() { i0.ɵɵrestoreView(_r6); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnSaveAsNewQuery()); });
|
|
94
|
+
i0.ɵɵelement(9, "i", 36);
|
|
95
|
+
i0.ɵɵelementStart(10, "div")(11, "div", 34);
|
|
96
|
+
i0.ɵɵtext(12, "Save as New Query");
|
|
97
|
+
i0.ɵɵelementEnd();
|
|
98
|
+
i0.ɵɵelementStart(13, "div", 35);
|
|
99
|
+
i0.ɵɵtext(14, "Create a new query, keep the original unchanged");
|
|
100
|
+
i0.ɵɵelementEnd()()();
|
|
101
|
+
i0.ɵɵelementStart(15, "div", 32);
|
|
102
|
+
i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Conditional_8_Template_div_click_15_listener() { i0.ɵɵrestoreView(_r6); const ctx_r0 = i0.ɵɵnextContext(4); ctx_r0.OnOpenSavedQuery(); return i0.ɵɵresetView(ctx_r0.OnCloseDropdown()); });
|
|
103
|
+
i0.ɵɵelement(16, "i", 37);
|
|
104
|
+
i0.ɵɵelementStart(17, "div")(18, "div", 34);
|
|
105
|
+
i0.ɵɵtext(19, "Open Saved Query");
|
|
106
|
+
i0.ɵɵelementEnd();
|
|
107
|
+
i0.ɵɵelementStart(20, "div", 35);
|
|
108
|
+
i0.ɵɵtext(21);
|
|
109
|
+
i0.ɵɵelementEnd()()()();
|
|
110
|
+
} if (rf & 2) {
|
|
111
|
+
const ctx_r0 = i0.ɵɵnextContext(4);
|
|
112
|
+
i0.ɵɵadvance(5);
|
|
113
|
+
i0.ɵɵtextInterpolate1("Update \"", ctx_r0.SavedQueryDisplayName, "\"");
|
|
114
|
+
i0.ɵɵadvance(16);
|
|
115
|
+
i0.ɵɵtextInterpolate2("View \"", ctx_r0.SavedQueryDisplayName, "\" (saved at v", ctx_r0.SavedAtVersion, ")");
|
|
72
116
|
} }
|
|
73
|
-
function
|
|
117
|
+
function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Template(rf, ctx) { if (rf & 1) {
|
|
74
118
|
const _r5 = i0.ɵɵgetCurrentView();
|
|
75
|
-
i0.ɵɵelementStart(0, "
|
|
76
|
-
i0.ɵɵelement(1, "i",
|
|
119
|
+
i0.ɵɵelementStart(0, "span", 25);
|
|
120
|
+
i0.ɵɵelement(1, "i", 26);
|
|
121
|
+
i0.ɵɵtext(2);
|
|
122
|
+
i0.ɵɵelementEnd();
|
|
123
|
+
i0.ɵɵelementStart(3, "div", 27)(4, "button", 28);
|
|
124
|
+
i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r5); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnToggleUpdateDropdown()); });
|
|
125
|
+
i0.ɵɵelement(5, "i", 20);
|
|
126
|
+
i0.ɵɵtext(6, " Update Query ");
|
|
127
|
+
i0.ɵɵelement(7, "i", 29);
|
|
128
|
+
i0.ɵɵelementEnd();
|
|
129
|
+
i0.ɵɵconditionalCreate(8, DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Conditional_8_Template, 22, 3, "div", 30);
|
|
130
|
+
i0.ɵɵelementEnd();
|
|
131
|
+
} if (rf & 2) {
|
|
132
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
133
|
+
i0.ɵɵadvance(2);
|
|
134
|
+
i0.ɵɵtextInterpolate1(" Saved at v", ctx_r0.SavedAtVersion, " ");
|
|
135
|
+
i0.ɵɵadvance(6);
|
|
136
|
+
i0.ɵɵconditional(ctx_r0.ShowUpdateDropdown ? 8 : -1);
|
|
137
|
+
} }
|
|
138
|
+
function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_13_Template(rf, ctx) { if (rf & 1) {
|
|
139
|
+
const _r7 = i0.ɵɵgetCurrentView();
|
|
140
|
+
i0.ɵɵelementStart(0, "span", 38);
|
|
141
|
+
i0.ɵɵelement(1, "i", 39);
|
|
142
|
+
i0.ɵɵtext(2);
|
|
143
|
+
i0.ɵɵelementEnd();
|
|
144
|
+
i0.ɵɵelementStart(3, "button", 40);
|
|
145
|
+
i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_13_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r7); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnOpenSavedQuery()); });
|
|
146
|
+
i0.ɵɵelement(4, "i", 24);
|
|
147
|
+
i0.ɵɵtext(5, " Open Query ");
|
|
148
|
+
i0.ɵɵelementEnd();
|
|
149
|
+
} if (rf & 2) {
|
|
150
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
151
|
+
i0.ɵɵadvance(2);
|
|
152
|
+
i0.ɵɵtextInterpolate1(" Query updated at v", ctx_r0.SavedAtVersion, " ");
|
|
153
|
+
} }
|
|
154
|
+
function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_14_Template(rf, ctx) { if (rf & 1) {
|
|
155
|
+
const _r8 = i0.ɵɵgetCurrentView();
|
|
156
|
+
i0.ɵɵelementStart(0, "span", 38);
|
|
157
|
+
i0.ɵɵelement(1, "i", 39);
|
|
158
|
+
i0.ɵɵtext(2);
|
|
159
|
+
i0.ɵɵelementEnd();
|
|
160
|
+
i0.ɵɵelementStart(3, "button", 40);
|
|
161
|
+
i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_14_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r8); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnOpenSavedQuery()); });
|
|
162
|
+
i0.ɵɵelement(4, "i", 24);
|
|
163
|
+
i0.ɵɵtext(5, " Open Query ");
|
|
164
|
+
i0.ɵɵelementEnd();
|
|
165
|
+
} if (rf & 2) {
|
|
166
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
167
|
+
i0.ɵɵadvance(2);
|
|
168
|
+
i0.ɵɵtextInterpolate1(" Saved at v", ctx_r0.SavedAtVersion, " ");
|
|
169
|
+
} }
|
|
170
|
+
function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_15_Template(rf, ctx) { }
|
|
171
|
+
function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_17_Template(rf, ctx) { if (rf & 1) {
|
|
172
|
+
i0.ɵɵelementStart(0, "div", 14);
|
|
173
|
+
i0.ɵɵelement(1, "mj-loading", 41);
|
|
174
|
+
i0.ɵɵelementEnd();
|
|
175
|
+
} if (rf & 2) {
|
|
176
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
177
|
+
i0.ɵɵadvance();
|
|
178
|
+
i0.ɵɵproperty("text", ctx_r0.SavingMessage);
|
|
179
|
+
} }
|
|
180
|
+
function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_18_Template(rf, ctx) { if (rf & 1) {
|
|
181
|
+
i0.ɵɵelement(0, "mj-loading", 15);
|
|
182
|
+
} }
|
|
183
|
+
function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_19_Template(rf, ctx) { if (rf & 1) {
|
|
184
|
+
const _r9 = i0.ɵɵgetCurrentView();
|
|
185
|
+
i0.ɵɵelementStart(0, "div", 42);
|
|
186
|
+
i0.ɵɵelement(1, "i", 43);
|
|
77
187
|
i0.ɵɵelementStart(2, "span");
|
|
78
188
|
i0.ɵɵtext(3);
|
|
79
189
|
i0.ɵɵelementEnd();
|
|
80
|
-
i0.ɵɵelementStart(4, "span",
|
|
190
|
+
i0.ɵɵelementStart(4, "span", 44);
|
|
81
191
|
i0.ɵɵtext(5, "(Showing cached data)");
|
|
82
192
|
i0.ɵɵelementEnd()();
|
|
83
|
-
i0.ɵɵelementStart(6, "mj-query-data-grid",
|
|
84
|
-
i0.ɵɵlistener("EntityLinkClick", function
|
|
193
|
+
i0.ɵɵelementStart(6, "mj-query-data-grid", 45);
|
|
194
|
+
i0.ɵɵlistener("EntityLinkClick", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_19_Template_mj_query_data_grid_EntityLinkClick_6_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnEntityLinkClick($event)); });
|
|
85
195
|
i0.ɵɵelementEnd();
|
|
86
196
|
} if (rf & 2) {
|
|
87
197
|
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
@@ -90,9 +200,9 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_14_
|
|
|
90
200
|
i0.ɵɵadvance(3);
|
|
91
201
|
i0.ɵɵproperty("ColumnConfigs", ctx_r0.GridColumnConfigs)("Data", ctx_r0.GridData)("ShowToolbar", false)("ShowRefresh", false)("PersistState", false)("SelectionMode", "none");
|
|
92
202
|
} }
|
|
93
|
-
function
|
|
203
|
+
function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_20_Template(rf, ctx) { if (rf & 1) {
|
|
94
204
|
i0.ɵɵelementStart(0, "div", 1);
|
|
95
|
-
i0.ɵɵelement(1, "i",
|
|
205
|
+
i0.ɵɵelement(1, "i", 43);
|
|
96
206
|
i0.ɵɵelementStart(2, "p");
|
|
97
207
|
i0.ɵɵtext(3);
|
|
98
208
|
i0.ɵɵelementEnd()();
|
|
@@ -101,10 +211,10 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_15_
|
|
|
101
211
|
i0.ɵɵadvance(3);
|
|
102
212
|
i0.ɵɵtextInterpolate(ctx_r0.ErrorMessage);
|
|
103
213
|
} }
|
|
104
|
-
function
|
|
105
|
-
const
|
|
106
|
-
i0.ɵɵelementStart(0, "mj-query-data-grid",
|
|
107
|
-
i0.ɵɵlistener("EntityLinkClick", function
|
|
214
|
+
function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template(rf, ctx) { if (rf & 1) {
|
|
215
|
+
const _r10 = i0.ɵɵgetCurrentView();
|
|
216
|
+
i0.ɵɵelementStart(0, "mj-query-data-grid", 45);
|
|
217
|
+
i0.ɵɵlistener("EntityLinkClick", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template_mj_query_data_grid_EntityLinkClick_0_listener($event) { i0.ɵɵrestoreView(_r10); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnEntityLinkClick($event)); });
|
|
108
218
|
i0.ɵɵelementEnd();
|
|
109
219
|
} if (rf & 2) {
|
|
110
220
|
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
@@ -122,13 +232,14 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Template(rf, ct
|
|
|
122
232
|
i0.ɵɵelementEnd();
|
|
123
233
|
i0.ɵɵelementStart(8, "div", 10);
|
|
124
234
|
i0.ɵɵconditionalCreate(9, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_9_Template, 3, 3, "button", 11);
|
|
125
|
-
i0.ɵɵconditionalCreate(10,
|
|
126
|
-
i0.ɵɵconditionalCreate(11, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_11_Template, 3, 0, "button", 13);
|
|
235
|
+
i0.ɵɵconditionalCreate(10, DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_10_Template, 3, 0, "button", 12)(11, DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_11_Template, 6, 1)(12, DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Template, 9, 2)(13, DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_13_Template, 6, 1)(14, DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_14_Template, 6, 1)(15, DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_15_Template, 0, 0);
|
|
127
236
|
i0.ɵɵelementEnd()();
|
|
128
|
-
i0.ɵɵelementStart(
|
|
129
|
-
i0.ɵɵconditionalCreate(
|
|
237
|
+
i0.ɵɵelementStart(16, "div", 13);
|
|
238
|
+
i0.ɵɵconditionalCreate(17, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_17_Template, 2, 1, "div", 14);
|
|
239
|
+
i0.ɵɵconditionalCreate(18, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_18_Template, 1, 0, "mj-loading", 15)(19, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_19_Template, 7, 7)(20, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_20_Template, 4, 1, "div", 1)(21, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template, 1, 6, "mj-query-data-grid", 16);
|
|
130
240
|
i0.ɵɵelementEnd();
|
|
131
241
|
} if (rf & 2) {
|
|
242
|
+
let tmp_7_0;
|
|
132
243
|
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
133
244
|
i0.ɵɵadvance(4);
|
|
134
245
|
i0.ɵɵtextInterpolate(ctx_r0.spec.title || "Data Results");
|
|
@@ -141,20 +252,20 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Template(rf, ct
|
|
|
141
252
|
i0.ɵɵadvance(2);
|
|
142
253
|
i0.ɵɵconditional(ctx_r0.IsLive ? 9 : -1);
|
|
143
254
|
i0.ɵɵadvance();
|
|
144
|
-
i0.ɵɵconditional(ctx_r0.
|
|
255
|
+
i0.ɵɵconditional((tmp_7_0 = ctx_r0.QuerySyncState) === "no-query-latest" ? 10 : tmp_7_0 === "synced" ? 11 : tmp_7_0 === "outdated-latest" ? 12 : tmp_7_0 === "query-ahead" ? 13 : tmp_7_0 === "query-behind" ? 14 : tmp_7_0 === "no-query-older" ? 15 : -1);
|
|
256
|
+
i0.ɵɵadvance(7);
|
|
257
|
+
i0.ɵɵconditional(ctx_r0.IsSaving ? 17 : -1);
|
|
145
258
|
i0.ɵɵadvance();
|
|
146
|
-
i0.ɵɵconditional(ctx_r0.
|
|
147
|
-
i0.ɵɵadvance(2);
|
|
148
|
-
i0.ɵɵconditional(ctx_r0.IsLoading ? 13 : ctx_r0.HasError && ctx_r0.HasData ? 14 : ctx_r0.HasError ? 15 : 16);
|
|
259
|
+
i0.ɵɵconditional(ctx_r0.IsLoading ? 18 : ctx_r0.HasError && ctx_r0.HasData ? 19 : ctx_r0.HasError ? 20 : 21);
|
|
149
260
|
} }
|
|
150
261
|
function DataArtifactViewerComponent_Conditional_1_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
151
262
|
i0.ɵɵelementStart(0, "div", 4)(1, "div", 5);
|
|
152
|
-
i0.ɵɵelement(2, "i",
|
|
263
|
+
i0.ɵɵelement(2, "i", 46);
|
|
153
264
|
i0.ɵɵelementStart(3, "span");
|
|
154
265
|
i0.ɵɵtext(4);
|
|
155
266
|
i0.ɵɵelementEnd()()();
|
|
156
|
-
i0.ɵɵelementStart(5, "div",
|
|
157
|
-
i0.ɵɵelement(6, "mj-markdown",
|
|
267
|
+
i0.ɵɵelementStart(5, "div", 47);
|
|
268
|
+
i0.ɵɵelement(6, "mj-markdown", 48);
|
|
158
269
|
i0.ɵɵelementEnd();
|
|
159
270
|
} if (rf & 2) {
|
|
160
271
|
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
@@ -165,20 +276,20 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_1_Template(rf, ct
|
|
|
165
276
|
} }
|
|
166
277
|
function DataArtifactViewerComponent_Conditional_1_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
167
278
|
i0.ɵɵelementStart(0, "div", 2);
|
|
168
|
-
i0.ɵɵelement(1, "i",
|
|
279
|
+
i0.ɵɵelement(1, "i", 49);
|
|
169
280
|
i0.ɵɵelementStart(2, "p");
|
|
170
281
|
i0.ɵɵtext(3, "No data to display");
|
|
171
282
|
i0.ɵɵelementEnd()();
|
|
172
283
|
} }
|
|
173
284
|
function DataArtifactViewerComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
174
|
-
i0.ɵɵconditionalCreate(0, DataArtifactViewerComponent_Conditional_1_Conditional_0_Template,
|
|
285
|
+
i0.ɵɵconditionalCreate(0, DataArtifactViewerComponent_Conditional_1_Conditional_0_Template, 22, 8)(1, DataArtifactViewerComponent_Conditional_1_Conditional_1_Template, 7, 6)(2, DataArtifactViewerComponent_Conditional_1_Conditional_2_Template, 4, 0, "div", 2);
|
|
175
286
|
} if (rf & 2) {
|
|
176
287
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
177
288
|
i0.ɵɵconditional(ctx_r0.HasData || ctx_r0.IsLoading ? 0 : ctx_r0.spec.plan ? 1 : 2);
|
|
178
289
|
} }
|
|
179
290
|
function DataArtifactViewerComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
180
291
|
i0.ɵɵelementStart(0, "div", 1);
|
|
181
|
-
i0.ɵɵelement(1, "i",
|
|
292
|
+
i0.ɵɵelement(1, "i", 43);
|
|
182
293
|
i0.ɵɵelementStart(2, "p");
|
|
183
294
|
i0.ɵɵtext(3);
|
|
184
295
|
i0.ɵɵelementEnd()();
|
|
@@ -189,15 +300,15 @@ function DataArtifactViewerComponent_Conditional_2_Template(rf, ctx) { if (rf &
|
|
|
189
300
|
} }
|
|
190
301
|
function DataArtifactViewerComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
191
302
|
i0.ɵɵelementStart(0, "div", 2);
|
|
192
|
-
i0.ɵɵelement(1, "i",
|
|
303
|
+
i0.ɵɵelement(1, "i", 49);
|
|
193
304
|
i0.ɵɵelementStart(2, "p");
|
|
194
305
|
i0.ɵɵtext(3, "No data to display");
|
|
195
306
|
i0.ɵɵelementEnd()();
|
|
196
307
|
} }
|
|
197
308
|
function DataArtifactViewerComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
198
|
-
const
|
|
199
|
-
i0.ɵɵelementStart(0, "mj-save-query-panel",
|
|
200
|
-
i0.ɵɵlistener("Saved", function DataArtifactViewerComponent_Conditional_4_Template_mj_save_query_panel_Saved_0_listener($event) { i0.ɵɵrestoreView(
|
|
309
|
+
const _r11 = i0.ɵɵgetCurrentView();
|
|
310
|
+
i0.ɵɵelementStart(0, "mj-save-query-panel", 50);
|
|
311
|
+
i0.ɵɵlistener("Saved", function DataArtifactViewerComponent_Conditional_4_Template_mj_save_query_panel_Saved_0_listener($event) { i0.ɵɵrestoreView(_r11); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnQuerySaved($event)); })("Cancelled", function DataArtifactViewerComponent_Conditional_4_Template_mj_save_query_panel_Cancelled_0_listener() { i0.ɵɵrestoreView(_r11); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.ShowSaveDialog = false); });
|
|
201
312
|
i0.ɵɵelementEnd();
|
|
202
313
|
} if (rf & 2) {
|
|
203
314
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
@@ -217,6 +328,7 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
|
|
|
217
328
|
cdr;
|
|
218
329
|
openEntityRecord = new EventEmitter();
|
|
219
330
|
navigationRequest = new EventEmitter();
|
|
331
|
+
tabsChanged = new EventEmitter();
|
|
220
332
|
spec = null;
|
|
221
333
|
GridData = [];
|
|
222
334
|
GridColumnConfigs = null;
|
|
@@ -225,15 +337,24 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
|
|
|
225
337
|
HasError = false;
|
|
226
338
|
ErrorMessage = '';
|
|
227
339
|
ShowSaveDialog = false;
|
|
340
|
+
ShowUpdateDropdown = false;
|
|
341
|
+
IsSaving = false;
|
|
342
|
+
SavingMessage = '';
|
|
343
|
+
/** Query sync state — drives the toolbar UI for saved query actions */
|
|
344
|
+
QuerySyncState = 'no-query-latest';
|
|
345
|
+
/** Latest version number for this artifact (from cache) */
|
|
346
|
+
LatestVersionNumber = 0;
|
|
228
347
|
/** Metadata from live execution (overrides spec.metadata when live) */
|
|
229
348
|
liveRowCount = null;
|
|
230
349
|
liveExecutionTime = null;
|
|
350
|
+
/** SQL from the saved query record (for comparison) */
|
|
351
|
+
savedQuerySql = null;
|
|
231
352
|
constructor(cdr) {
|
|
232
353
|
super();
|
|
233
354
|
this.cdr = cdr;
|
|
234
355
|
}
|
|
235
356
|
get hasDisplayContent() {
|
|
236
|
-
return this.spec != null && (this.HasData || this.IsLoading
|
|
357
|
+
return this.spec != null && (this.HasData || this.IsLoading);
|
|
237
358
|
}
|
|
238
359
|
get parentShouldShowRawContent() {
|
|
239
360
|
return true;
|
|
@@ -250,6 +371,30 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
|
|
|
250
371
|
get DisplayExecutionTime() {
|
|
251
372
|
return this.liveExecutionTime ?? this.spec?.metadata?.executionTimeMs ?? null;
|
|
252
373
|
}
|
|
374
|
+
/** Current artifact version number */
|
|
375
|
+
get CurrentVersionNumber() {
|
|
376
|
+
return this.artifactVersion?.VersionNumber || 1;
|
|
377
|
+
}
|
|
378
|
+
/** Whether this is the latest version of the artifact */
|
|
379
|
+
get IsLatestVersion() {
|
|
380
|
+
return this.CurrentVersionNumber === this.LatestVersionNumber;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* The effective version number at which the query was last saved/updated.
|
|
384
|
+
* When viewing an older version, this looks ahead through newer versions
|
|
385
|
+
* to find the most recent savedAtVersionNumber for the same query,
|
|
386
|
+
* giving the user accurate context (e.g., "Query updated at v3" instead
|
|
387
|
+
* of the stale "Saved at v1" from this version's snapshot).
|
|
388
|
+
*/
|
|
389
|
+
EffectiveSavedAtVersion = null;
|
|
390
|
+
/** Alias used by the template */
|
|
391
|
+
get SavedAtVersion() {
|
|
392
|
+
return this.EffectiveSavedAtVersion;
|
|
393
|
+
}
|
|
394
|
+
/** Display name for the saved query */
|
|
395
|
+
get SavedQueryDisplayName() {
|
|
396
|
+
return this.spec?.savedQueryName || 'Saved Query';
|
|
397
|
+
}
|
|
253
398
|
async ngOnInit() {
|
|
254
399
|
try {
|
|
255
400
|
this.spec = this.parseJsonContent();
|
|
@@ -260,6 +405,14 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
|
|
|
260
405
|
}
|
|
261
406
|
// Build enriched column configs from agent metadata (if available)
|
|
262
407
|
this.GridColumnConfigs = this.BuildColumnConfigs();
|
|
408
|
+
// Set loading early (synchronously) so hasDisplayContent is true immediately.
|
|
409
|
+
// This ensures the Display tab is available when onPluginLoaded fires,
|
|
410
|
+
// showing a loading indicator instead of briefly flashing the Plan tab.
|
|
411
|
+
if (this.spec.metadata?.sql || this.HasInlineData) {
|
|
412
|
+
this.IsLoading = true;
|
|
413
|
+
}
|
|
414
|
+
// Load cached metadata and resolve query sync state
|
|
415
|
+
await this.InitQuerySyncState();
|
|
263
416
|
// If SQL is available, execute it live
|
|
264
417
|
if (this.spec.metadata?.sql) {
|
|
265
418
|
await this.LoadLiveData();
|
|
@@ -267,7 +420,10 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
|
|
|
267
420
|
else if (this.HasInlineData) {
|
|
268
421
|
// Fall back to embedded rows
|
|
269
422
|
this.GridData = this.spec.rows;
|
|
423
|
+
this.IsLoading = false;
|
|
270
424
|
}
|
|
425
|
+
// Signal parent that tabs/display content may have changed after async load
|
|
426
|
+
this.tabsChanged.emit();
|
|
271
427
|
}
|
|
272
428
|
catch (error) {
|
|
273
429
|
this.HasError = true;
|
|
@@ -380,10 +536,133 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
|
|
|
380
536
|
this.GridData = this.spec.rows;
|
|
381
537
|
}
|
|
382
538
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
539
|
+
// ─── Query Sync State ────────────────────────────────────────────
|
|
540
|
+
/**
|
|
541
|
+
* Initialize query sync state by loading cached metadata.
|
|
542
|
+
* Resolves the latest version number and compares saved query SQL.
|
|
543
|
+
*/
|
|
544
|
+
async InitQuerySyncState() {
|
|
545
|
+
// Ensure artifact cache is loaded (not registered for startup)
|
|
546
|
+
await ArtifactMetadataEngine.Instance.Config(false);
|
|
547
|
+
this.LatestVersionNumber = this.resolveLatestVersionNumber();
|
|
548
|
+
// If cache is stale (doesn't know about the version we're viewing),
|
|
549
|
+
// force-refresh and re-resolve. This happens when new versions are
|
|
550
|
+
// created during a conversation after the cache was first loaded.
|
|
551
|
+
if (this.LatestVersionNumber < this.CurrentVersionNumber) {
|
|
552
|
+
await ArtifactMetadataEngine.Instance.Config(true);
|
|
553
|
+
this.LatestVersionNumber = this.resolveLatestVersionNumber();
|
|
554
|
+
}
|
|
555
|
+
this.EffectiveSavedAtVersion = this.resolveEffectiveSavedAtVersion();
|
|
556
|
+
await this.resolveSavedQuerySql();
|
|
557
|
+
this.QuerySyncState = this.computeQuerySyncState();
|
|
386
558
|
}
|
|
559
|
+
/**
|
|
560
|
+
* Determine the latest version number for this artifact from cache.
|
|
561
|
+
* Falls back to current version if cache miss.
|
|
562
|
+
*/
|
|
563
|
+
resolveLatestVersionNumber() {
|
|
564
|
+
if (!this.artifactVersion?.ArtifactID) {
|
|
565
|
+
return this.CurrentVersionNumber;
|
|
566
|
+
}
|
|
567
|
+
const versions = ArtifactMetadataEngine.Instance.GetVersionsForArtifact(this.artifactVersion.ArtifactID);
|
|
568
|
+
if (versions.length > 0) {
|
|
569
|
+
// GetVersionsForArtifact returns DESC sorted
|
|
570
|
+
return versions[0].VersionNumber || 1;
|
|
571
|
+
}
|
|
572
|
+
// Cache miss — current version is our best guess
|
|
573
|
+
return this.CurrentVersionNumber;
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Scan all versions of this artifact to find the most recent savedAtVersionNumber
|
|
577
|
+
* for the same savedQueryId. When viewing an older version, this tells the user
|
|
578
|
+
* where the query was *actually* last updated, not just what this version's
|
|
579
|
+
* snapshot recorded at the time.
|
|
580
|
+
*/
|
|
581
|
+
resolveEffectiveSavedAtVersion() {
|
|
582
|
+
const queryId = this.spec?.savedQueryId;
|
|
583
|
+
if (!queryId)
|
|
584
|
+
return null;
|
|
585
|
+
// Start with this version's own value
|
|
586
|
+
let effective = this.spec?.savedAtVersionNumber ?? null;
|
|
587
|
+
if (!this.artifactVersion?.ArtifactID)
|
|
588
|
+
return effective;
|
|
589
|
+
// GetVersionsForArtifact returns DESC sorted — scan all versions
|
|
590
|
+
const versions = ArtifactMetadataEngine.Instance.GetVersionsForArtifact(this.artifactVersion.ArtifactID);
|
|
591
|
+
for (const v of versions) {
|
|
592
|
+
try {
|
|
593
|
+
if (!v.Content)
|
|
594
|
+
continue;
|
|
595
|
+
const content = typeof v.Content === 'string' ? JSON.parse(v.Content) : v.Content;
|
|
596
|
+
if (content.savedQueryId === queryId && content.savedAtVersionNumber != null) {
|
|
597
|
+
const vSavedAt = content.savedAtVersionNumber;
|
|
598
|
+
if (effective == null || vSavedAt > effective) {
|
|
599
|
+
effective = vSavedAt;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
catch {
|
|
604
|
+
// Skip versions with unparseable content
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return effective;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Look up the saved query's SQL from QueryEngine cache for comparison.
|
|
611
|
+
* Only fetches if savedQueryId is present.
|
|
612
|
+
*/
|
|
613
|
+
async resolveSavedQuerySql() {
|
|
614
|
+
if (!this.spec?.savedQueryId) {
|
|
615
|
+
this.savedQuerySql = null;
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
// QueryEngine is registered for startup, should already be loaded
|
|
619
|
+
let query = QueryEngine.Instance.FindQueryByID(this.spec.savedQueryId);
|
|
620
|
+
if (!query) {
|
|
621
|
+
// Cache miss — force refresh and retry
|
|
622
|
+
await QueryEngine.Instance.Config(true);
|
|
623
|
+
query = QueryEngine.Instance.FindQueryByID(this.spec.savedQueryId);
|
|
624
|
+
}
|
|
625
|
+
this.savedQuerySql = query?.SQL ?? null;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Compute the query sync state from current spec, version, and saved query SQL.
|
|
629
|
+
* Implements the decision tree from the UX design.
|
|
630
|
+
*/
|
|
631
|
+
computeQuerySyncState() {
|
|
632
|
+
const hasSavedQuery = !!this.spec?.savedQueryId;
|
|
633
|
+
const isLatest = this.IsLatestVersion;
|
|
634
|
+
if (!hasSavedQuery) {
|
|
635
|
+
return isLatest ? 'no-query-latest' : 'no-query-older';
|
|
636
|
+
}
|
|
637
|
+
// Compare SQL (normalize whitespace for reliable comparison)
|
|
638
|
+
const specSql = this.normalizeSql(this.spec?.metadata?.sql);
|
|
639
|
+
const querySql = this.normalizeSql(this.savedQuerySql);
|
|
640
|
+
const sqlMatches = specSql != null && querySql != null && specSql === querySql;
|
|
641
|
+
// Only truly synced if SQL matches AND this is the version it was saved at.
|
|
642
|
+
// Even with identical SQL, a newer version should show the dropdown so the
|
|
643
|
+
// user can update the version association (re-save at current version).
|
|
644
|
+
const savedAtCurrent = this.EffectiveSavedAtVersion === this.CurrentVersionNumber;
|
|
645
|
+
if (sqlMatches && savedAtCurrent) {
|
|
646
|
+
return 'synced';
|
|
647
|
+
}
|
|
648
|
+
// SQL differs — determine position relative to saved version
|
|
649
|
+
if (isLatest) {
|
|
650
|
+
return 'outdated-latest';
|
|
651
|
+
}
|
|
652
|
+
// Use effective saved-at (looks ahead through newer versions)
|
|
653
|
+
const savedAt = this.EffectiveSavedAtVersion;
|
|
654
|
+
if (savedAt != null && this.CurrentVersionNumber < savedAt) {
|
|
655
|
+
return 'query-ahead'; // query was updated at a newer version
|
|
656
|
+
}
|
|
657
|
+
return 'query-behind'; // ahead of saved but not latest
|
|
658
|
+
}
|
|
659
|
+
/** Normalize SQL for comparison: trim and collapse whitespace */
|
|
660
|
+
normalizeSql(sql) {
|
|
661
|
+
if (sql == null)
|
|
662
|
+
return null;
|
|
663
|
+
return sql.trim().replace(/\s+/g, ' ');
|
|
664
|
+
}
|
|
665
|
+
// ─── Actions ────────────────────────────────────────────────────
|
|
387
666
|
/** Navigate to the saved query in the Data Explorer's Queries browser */
|
|
388
667
|
OnOpenSavedQuery() {
|
|
389
668
|
if (!this.spec?.savedQueryId)
|
|
@@ -394,23 +673,105 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
|
|
|
394
673
|
queryParams: { queryId: this.spec.savedQueryId }
|
|
395
674
|
});
|
|
396
675
|
}
|
|
397
|
-
/**
|
|
676
|
+
/** Show the save panel for creating a brand-new query */
|
|
677
|
+
OnShowSaveDialog() {
|
|
678
|
+
this.ShowSaveDialog = true;
|
|
679
|
+
this.ShowUpdateDropdown = false;
|
|
680
|
+
}
|
|
681
|
+
/** Toggle the update query dropdown */
|
|
682
|
+
OnToggleUpdateDropdown() {
|
|
683
|
+
this.ShowUpdateDropdown = !this.ShowUpdateDropdown;
|
|
684
|
+
}
|
|
685
|
+
/** Close the dropdown (e.g., on outside click) */
|
|
686
|
+
OnCloseDropdown() {
|
|
687
|
+
this.ShowUpdateDropdown = false;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Update the existing saved query's SQL to match this version.
|
|
691
|
+
* Only available from the latest version (Scenario 3).
|
|
692
|
+
*/
|
|
693
|
+
async OnUpdateExistingQuery() {
|
|
694
|
+
this.ShowUpdateDropdown = false;
|
|
695
|
+
if (!this.spec?.savedQueryId || !this.spec.metadata?.sql)
|
|
696
|
+
return;
|
|
697
|
+
this.IsSaving = true;
|
|
698
|
+
this.SavingMessage = `Updating "${this.SavedQueryDisplayName}"...`;
|
|
699
|
+
this.cdr.detectChanges();
|
|
700
|
+
try {
|
|
701
|
+
const md = new Metadata();
|
|
702
|
+
const query = await md.GetEntityObject('MJ: Queries');
|
|
703
|
+
const loaded = await query.Load(this.spec.savedQueryId);
|
|
704
|
+
if (!loaded) {
|
|
705
|
+
console.error('Failed to load saved query for update');
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
query.SQL = this.spec.metadata.sql;
|
|
709
|
+
const saved = await query.Save();
|
|
710
|
+
if (saved) {
|
|
711
|
+
// Update spec with new version tracking
|
|
712
|
+
this.spec.savedAtVersionNumber = this.CurrentVersionNumber;
|
|
713
|
+
await this.PersistArtifactContent();
|
|
714
|
+
// Refresh caches so future lookups see the updated data
|
|
715
|
+
await QueryEngine.Instance.Config(true);
|
|
716
|
+
await ArtifactMetadataEngine.Instance.Config(true);
|
|
717
|
+
await md.Refresh();
|
|
718
|
+
this.savedQuerySql = this.spec.metadata.sql;
|
|
719
|
+
this.EffectiveSavedAtVersion = this.CurrentVersionNumber;
|
|
720
|
+
this.QuerySyncState = 'synced';
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
catch (error) {
|
|
724
|
+
console.error('Failed to update saved query:', error);
|
|
725
|
+
}
|
|
726
|
+
finally {
|
|
727
|
+
this.IsSaving = false;
|
|
728
|
+
this.cdr.detectChanges();
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Save as a new query (from the dropdown).
|
|
733
|
+
* Opens the save dialog which creates a fresh query record.
|
|
734
|
+
*/
|
|
735
|
+
OnSaveAsNewQuery() {
|
|
736
|
+
this.ShowUpdateDropdown = false;
|
|
737
|
+
this.ShowSaveDialog = true;
|
|
738
|
+
}
|
|
739
|
+
/** Handle successful save from the dialog (both initial save and "save as new") */
|
|
398
740
|
async OnQuerySaved(event) {
|
|
399
741
|
this.ShowSaveDialog = false;
|
|
400
|
-
|
|
401
|
-
this.
|
|
402
|
-
this.spec.savedQueryName = event.queryName;
|
|
403
|
-
// Persist updated content to artifact version
|
|
404
|
-
await this.UpdateArtifactContent();
|
|
742
|
+
this.IsSaving = true;
|
|
743
|
+
this.SavingMessage = 'Saving query...';
|
|
405
744
|
this.cdr.detectChanges();
|
|
745
|
+
try {
|
|
746
|
+
// Update spec with saved query info and version tracking
|
|
747
|
+
this.spec.savedQueryId = event.queryId;
|
|
748
|
+
this.spec.savedQueryName = event.queryName;
|
|
749
|
+
this.spec.savedAtVersionNumber = this.CurrentVersionNumber;
|
|
750
|
+
// Persist and refresh caches
|
|
751
|
+
await this.PersistArtifactContent();
|
|
752
|
+
await QueryEngine.Instance.Config(true);
|
|
753
|
+
await ArtifactMetadataEngine.Instance.Config(true);
|
|
754
|
+
await new Metadata().Refresh();
|
|
755
|
+
this.savedQuerySql = this.spec.metadata?.sql ?? null;
|
|
756
|
+
this.EffectiveSavedAtVersion = this.CurrentVersionNumber;
|
|
757
|
+
this.QuerySyncState = 'synced';
|
|
758
|
+
}
|
|
759
|
+
catch (error) {
|
|
760
|
+
console.error('Failed to save query:', error);
|
|
761
|
+
}
|
|
762
|
+
finally {
|
|
763
|
+
this.IsSaving = false;
|
|
764
|
+
this.cdr.detectChanges();
|
|
765
|
+
}
|
|
406
766
|
}
|
|
407
767
|
/** Persist updated spec back to the artifact version entity */
|
|
408
|
-
async
|
|
768
|
+
async PersistArtifactContent() {
|
|
409
769
|
if (!this.artifactVersion || !this.spec)
|
|
410
770
|
return;
|
|
411
771
|
this.artifactVersion.Content = JSON.stringify(this.spec);
|
|
412
772
|
await this.artifactVersion.Save();
|
|
413
773
|
}
|
|
774
|
+
// ─── Tabs ───────────────────────────────────────────────────────
|
|
414
775
|
/**
|
|
415
776
|
* Provide Plan tab (markdown) and SQL tab (code) when available
|
|
416
777
|
*/
|
|
@@ -436,7 +797,7 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
|
|
|
436
797
|
return tabs;
|
|
437
798
|
}
|
|
438
799
|
static ɵfac = function DataArtifactViewerComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || DataArtifactViewerComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
|
|
439
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DataArtifactViewerComponent, selectors: [["mj-data-artifact-viewer"]], outputs: { openEntityRecord: "openEntityRecord", navigationRequest: "navigationRequest" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 5, vars: 3, consts: [[1, "data-artifact-viewer", 3, "ngClass"], [1, "error-state"], [1, "empty-state"], [3, "QueryName", "QueryDescription", "SQL"], [1, "data-toolbar"], [1, "data-title"], [1, "fas", "fa-table"], [1, "live-badge"], [1, "row-count"], [1, "exec-time"], [1, "data-actions"], ["title", "Refresh data", 1, "btn-icon", 3, "disabled"], ["title", "Save as reusable query", 1, "btn-icon", "btn-save"], [
|
|
800
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DataArtifactViewerComponent, selectors: [["mj-data-artifact-viewer"]], outputs: { openEntityRecord: "openEntityRecord", navigationRequest: "navigationRequest" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 5, vars: 3, consts: [[1, "data-artifact-viewer", 3, "ngClass"], [1, "error-state"], [1, "empty-state"], [3, "QueryName", "QueryDescription", "SQL"], [1, "data-toolbar"], [1, "data-title"], [1, "fas", "fa-table"], [1, "live-badge"], [1, "row-count"], [1, "exec-time"], [1, "data-actions"], ["title", "Refresh data", 1, "btn-icon", 3, "disabled"], ["title", "Save as reusable query", 1, "btn-icon", "btn-save"], [1, "grid-container"], [1, "saving-overlay"], ["text", "Loading data..."], ["Height", "100%", 3, "ColumnConfigs", "Data", "ShowToolbar", "ShowRefresh", "PersistState", "SelectionMode"], ["title", "Refresh data", 1, "btn-icon", 3, "click", "disabled"], [1, "fas", "fa-sync-alt"], ["title", "Save as reusable query", 1, "btn-icon", "btn-save", 3, "click"], [1, "fas", "fa-save"], [1, "query-badge", "query-badge-synced"], [1, "fas", "fa-check"], ["title", "Open saved query record", 1, "btn-icon", "btn-open", 3, "click"], [1, "fas", "fa-external-link-alt"], [1, "query-badge", "query-badge-outdated"], [1, "fas", "fa-circle-exclamation"], [1, "dropdown-wrapper"], ["title", "Update or save query", 1, "btn-icon", "btn-warning", 3, "click"], [1, "fas", "fa-caret-down"], [1, "query-dropdown"], [1, "query-dropdown", 3, "click"], [1, "query-dropdown-item", 3, "click"], [1, "fas", "fa-arrow-up-from-bracket", 2, "color", "#d97706"], [1, "dropdown-label"], [1, "dropdown-desc"], [1, "fas", "fa-plus", 2, "color", "#16a34a"], [1, "fas", "fa-external-link-alt", 2, "color", "#6c757d"], [1, "query-badge", "query-badge-muted"], [1, "fas", "fa-clock-rotate-left"], ["title", "Open saved query record", 1, "btn-icon", "btn-muted", 3, "click"], [3, "text"], [1, "error-banner"], [1, "fas", "fa-exclamation-triangle"], [1, "fallback-note"], ["Height", "100%", 3, "EntityLinkClick", "ColumnConfigs", "Data", "ShowToolbar", "ShowRefresh", "PersistState", "SelectionMode"], [1, "fas", "fa-diagram-project"], [1, "plan-content"], [3, "data", "enableMermaid", "enableHighlight", "enableCollapsibleHeadings", "enableSmartypants"], [1, "fas", "fa-inbox"], [3, "Saved", "Cancelled", "QueryName", "QueryDescription", "SQL"]], template: function DataArtifactViewerComponent_Template(rf, ctx) { if (rf & 1) {
|
|
440
801
|
i0.ɵɵelementStart(0, "div", 0);
|
|
441
802
|
i0.ɵɵconditionalCreate(1, DataArtifactViewerComponent_Conditional_1_Template, 3, 1)(2, DataArtifactViewerComponent_Conditional_2_Template, 4, 1, "div", 1)(3, DataArtifactViewerComponent_Conditional_3_Template, 4, 0, "div", 2);
|
|
442
803
|
i0.ɵɵconditionalCreate(4, DataArtifactViewerComponent_Conditional_4_Template, 1, 3, "mj-save-query-panel", 3);
|
|
@@ -447,7 +808,7 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
|
|
|
447
808
|
i0.ɵɵconditional(ctx.spec ? 1 : ctx.HasError ? 2 : 3);
|
|
448
809
|
i0.ɵɵadvance(3);
|
|
449
810
|
i0.ɵɵconditional(ctx.ShowSaveDialog ? 4 : -1);
|
|
450
|
-
} }, dependencies: [i1.NgClass, i2.MarkdownComponent, i3.QueryDataGridComponent, i4.LoadingComponent, i5.SaveQueryPanelComponent], styles: [".data-artifact-viewer[_ngcontent-%COMP%] {\n
|
|
811
|
+
} }, dependencies: [i1.NgClass, i2.MarkdownComponent, i3.QueryDataGridComponent, i4.LoadingComponent, i5.SaveQueryPanelComponent], styles: [".data-artifact-viewer[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.data-toolbar[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n background: #f8f9fa;\n border-bottom: 1px solid #dee2e6;\n}\n\n.data-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n font-size: 13px;\n color: #333;\n}\n\n.data-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #6c757d;\n}\n\n.row-count[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: #e9ecef;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: #495057;\n}\n\n.exec-time[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: #d4edda;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: #155724;\n}\n\n.live-badge[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: #cce5ff;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n color: #004085;\n}\n\n.data-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 6px;\n}\n\n.btn-icon[_ngcontent-%COMP%] {\n padding: 4px 10px;\n background: white;\n border: 1px solid #ced4da;\n border-radius: 4px;\n cursor: pointer;\n font-size: 11px;\n display: flex;\n align-items: center;\n gap: 4px;\n color: #495057;\n}\n\n.btn-icon[_ngcontent-%COMP%]:hover {\n background: #e9ecef;\n border-color: #adb5bd;\n}\n\n.btn-icon[_ngcontent-%COMP%]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.btn-icon.btn-save[_ngcontent-%COMP%] {\n color: #16a34a;\n border-color: #86efac;\n}\n\n.btn-icon.btn-save[_ngcontent-%COMP%]:hover {\n background: #f0fdf4;\n border-color: #16a34a;\n}\n\n.btn-icon.btn-open[_ngcontent-%COMP%] {\n color: #2563eb;\n border-color: #93c5fd;\n}\n\n.btn-icon.btn-open[_ngcontent-%COMP%]:hover {\n background: #eff6ff;\n border-color: #2563eb;\n}\n\n.grid-container[_ngcontent-%COMP%] {\n flex: 1;\n overflow: hidden;\n min-height: 200px;\n height: 500px;\n position: relative;\n}\n\n\n\n.saving-overlay[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n background: rgba(255, 255, 255, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 50;\n}\n\n.error-banner[_ngcontent-%COMP%] {\n padding: 8px 12px;\n background: #fff3cd;\n border-bottom: 1px solid #ffc107;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: #856404;\n}\n\n.fallback-note[_ngcontent-%COMP%] {\n font-style: italic;\n color: #6c757d;\n}\n\n.empty-state[_ngcontent-%COMP%], .error-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 200px;\n color: #6c757d;\n text-align: center;\n gap: 12px;\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%], .error-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 32px;\n}\n\n.error-state[_ngcontent-%COMP%] {\n color: #dc3545;\n}\n\n.plan-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow: auto;\n padding: 16px;\n}\n\n\n\n.query-badge[_ngcontent-%COMP%] {\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n\n.query-badge-synced[_ngcontent-%COMP%] {\n background: #d1fae5;\n color: #065f46;\n}\n\n.query-badge-outdated[_ngcontent-%COMP%] {\n background: #fff3cd;\n color: #856404;\n}\n\n.query-badge-muted[_ngcontent-%COMP%] {\n background: #f3f4f6;\n color: #9ca3af;\n}\n\n\n\n.dropdown-wrapper[_ngcontent-%COMP%] {\n position: relative;\n}\n\n.btn-icon.btn-warning[_ngcontent-%COMP%] {\n color: #d97706;\n border-color: #fcd34d;\n}\n\n.btn-icon.btn-warning[_ngcontent-%COMP%]:hover {\n background: #fffbeb;\n border-color: #d97706;\n}\n\n.btn-icon.btn-muted[_ngcontent-%COMP%] {\n color: #9ca3af;\n border-color: #e5e7eb;\n}\n\n\n\n.query-dropdown[_ngcontent-%COMP%] {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.12);\n overflow: hidden;\n min-width: 280px;\n z-index: 100;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%] {\n padding: 10px 14px;\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n cursor: pointer;\n border-bottom: 1px solid #f0f0f0;\n transition: background 0.1s;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%]:hover {\n background: #f8f9fa;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n}\n\n.dropdown-label[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.dropdown-desc[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #6c757d;\n margin-top: 2px;\n}"] });
|
|
451
812
|
};
|
|
452
813
|
DataArtifactViewerComponent = __decorate([
|
|
453
814
|
RegisterClass(BaseArtifactViewerPluginComponent, 'DataArtifactViewerPlugin')
|
|
@@ -455,136 +816,11 @@ DataArtifactViewerComponent = __decorate([
|
|
|
455
816
|
export { DataArtifactViewerComponent };
|
|
456
817
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DataArtifactViewerComponent, [{
|
|
457
818
|
type: Component,
|
|
458
|
-
args: [{ standalone: false, selector: 'mj-data-artifact-viewer', template: `
|
|
459
|
-
<div class="data-artifact-viewer" [ngClass]="cssClass">
|
|
460
|
-
@if (spec) {
|
|
461
|
-
@if (HasData || IsLoading) {
|
|
462
|
-
<!-- Toolbar -->
|
|
463
|
-
<div class="data-toolbar">
|
|
464
|
-
<div class="data-title">
|
|
465
|
-
<i class="fas fa-table"></i>
|
|
466
|
-
<span>{{ spec.title || 'Data Results' }}</span>
|
|
467
|
-
@if (IsLive) {
|
|
468
|
-
<span class="live-badge">Live</span>
|
|
469
|
-
}
|
|
470
|
-
@if (DisplayRowCount != null) {
|
|
471
|
-
<span class="row-count">{{ DisplayRowCount }} rows</span>
|
|
472
|
-
}
|
|
473
|
-
@if (DisplayExecutionTime != null) {
|
|
474
|
-
<span class="exec-time">{{ DisplayExecutionTime }}ms</span>
|
|
475
|
-
}
|
|
476
|
-
</div>
|
|
477
|
-
<div class="data-actions">
|
|
478
|
-
@if (IsLive) {
|
|
479
|
-
<button class="btn-icon" title="Refresh data" (click)="OnRefresh()" [disabled]="IsLoading">
|
|
480
|
-
<i class="fas fa-sync-alt" [class.fa-spin]="IsLoading"></i> Refresh
|
|
481
|
-
</button>
|
|
482
|
-
}
|
|
483
|
-
@if (CanSaveQuery) {
|
|
484
|
-
<button class="btn-icon btn-save" title="Save as reusable query"
|
|
485
|
-
(click)="ShowSaveDialog = true">
|
|
486
|
-
<i class="fas fa-save"></i> Save Query
|
|
487
|
-
</button>
|
|
488
|
-
}
|
|
489
|
-
@if (spec.savedQueryId) {
|
|
490
|
-
<button class="btn-icon btn-open" title="Open saved query record"
|
|
491
|
-
(click)="OnOpenSavedQuery()">
|
|
492
|
-
<i class="fas fa-external-link-alt"></i> Open Query
|
|
493
|
-
</button>
|
|
494
|
-
}
|
|
495
|
-
</div>
|
|
496
|
-
</div>
|
|
497
|
-
|
|
498
|
-
<!-- Grid -->
|
|
499
|
-
<div class="grid-container">
|
|
500
|
-
@if (IsLoading) {
|
|
501
|
-
<mj-loading text="Loading data..."></mj-loading>
|
|
502
|
-
} @else if (HasError && HasData) {
|
|
503
|
-
<div class="error-banner">
|
|
504
|
-
<i class="fas fa-exclamation-triangle"></i>
|
|
505
|
-
<span>{{ ErrorMessage }}</span>
|
|
506
|
-
<span class="fallback-note">(Showing cached data)</span>
|
|
507
|
-
</div>
|
|
508
|
-
<mj-query-data-grid
|
|
509
|
-
[ColumnConfigs]="GridColumnConfigs"
|
|
510
|
-
[Data]="GridData"
|
|
511
|
-
[ShowToolbar]="false"
|
|
512
|
-
[ShowRefresh]="false"
|
|
513
|
-
[PersistState]="false"
|
|
514
|
-
[SelectionMode]="'none'"
|
|
515
|
-
(EntityLinkClick)="OnEntityLinkClick($event)"
|
|
516
|
-
Height="100%">
|
|
517
|
-
</mj-query-data-grid>
|
|
518
|
-
} @else if (HasError) {
|
|
519
|
-
<div class="error-state">
|
|
520
|
-
<i class="fas fa-exclamation-triangle"></i>
|
|
521
|
-
<p>{{ ErrorMessage }}</p>
|
|
522
|
-
</div>
|
|
523
|
-
} @else {
|
|
524
|
-
<mj-query-data-grid
|
|
525
|
-
[ColumnConfigs]="GridColumnConfigs"
|
|
526
|
-
[Data]="GridData"
|
|
527
|
-
[ShowToolbar]="false"
|
|
528
|
-
[ShowRefresh]="false"
|
|
529
|
-
[PersistState]="false"
|
|
530
|
-
[SelectionMode]="'none'"
|
|
531
|
-
(EntityLinkClick)="OnEntityLinkClick($event)"
|
|
532
|
-
Height="100%">
|
|
533
|
-
</mj-query-data-grid>
|
|
534
|
-
}
|
|
535
|
-
</div>
|
|
536
|
-
} @else if (spec.plan) {
|
|
537
|
-
<!-- Plan-only view (no results yet) -->
|
|
538
|
-
<div class="data-toolbar">
|
|
539
|
-
<div class="data-title">
|
|
540
|
-
<i class="fas fa-diagram-project"></i>
|
|
541
|
-
<span>{{ spec.title || 'Query Plan' }}</span>
|
|
542
|
-
</div>
|
|
543
|
-
</div>
|
|
544
|
-
<div class="plan-content">
|
|
545
|
-
<mj-markdown
|
|
546
|
-
[data]="spec.plan"
|
|
547
|
-
[enableMermaid]="true"
|
|
548
|
-
[enableHighlight]="true"
|
|
549
|
-
[enableCollapsibleHeadings]="false"
|
|
550
|
-
[enableSmartypants]="true">
|
|
551
|
-
</mj-markdown>
|
|
552
|
-
</div>
|
|
553
|
-
} @else {
|
|
554
|
-
<!-- No data and no plan -->
|
|
555
|
-
<div class="empty-state">
|
|
556
|
-
<i class="fas fa-inbox"></i>
|
|
557
|
-
<p>No data to display</p>
|
|
558
|
-
</div>
|
|
559
|
-
}
|
|
560
|
-
} @else if (HasError) {
|
|
561
|
-
<div class="error-state">
|
|
562
|
-
<i class="fas fa-exclamation-triangle"></i>
|
|
563
|
-
<p>{{ ErrorMessage }}</p>
|
|
564
|
-
</div>
|
|
565
|
-
} @else {
|
|
566
|
-
<div class="empty-state">
|
|
567
|
-
<i class="fas fa-inbox"></i>
|
|
568
|
-
<p>No data to display</p>
|
|
569
|
-
</div>
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
<!-- Save Query Panel (slide-in) -->
|
|
573
|
-
@if (ShowSaveDialog) {
|
|
574
|
-
<mj-save-query-panel
|
|
575
|
-
[QueryName]="spec?.title || 'Untitled Query'"
|
|
576
|
-
[QueryDescription]="''"
|
|
577
|
-
[SQL]="spec?.metadata?.sql || ''"
|
|
578
|
-
(Saved)="OnQuerySaved($event)"
|
|
579
|
-
(Cancelled)="ShowSaveDialog = false">
|
|
580
|
-
</mj-save-query-panel>
|
|
581
|
-
}
|
|
582
|
-
</div>
|
|
583
|
-
`, styles: ["\n .data-artifact-viewer {\n display: flex;\n flex-direction: column;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .data-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n background: #f8f9fa;\n border-bottom: 1px solid #dee2e6;\n }\n\n .data-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n font-size: 13px;\n color: #333;\n }\n\n .data-title i {\n color: #6c757d;\n }\n\n .row-count {\n padding: 2px 8px;\n background: #e9ecef;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: #495057;\n }\n\n .exec-time {\n padding: 2px 8px;\n background: #d4edda;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: #155724;\n }\n\n .live-badge {\n padding: 2px 8px;\n background: #cce5ff;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n color: #004085;\n }\n\n .data-actions {\n display: flex;\n gap: 6px;\n }\n\n .btn-icon {\n padding: 4px 10px;\n background: white;\n border: 1px solid #ced4da;\n border-radius: 4px;\n cursor: pointer;\n font-size: 11px;\n display: flex;\n align-items: center;\n gap: 4px;\n color: #495057;\n }\n\n .btn-icon:hover {\n background: #e9ecef;\n border-color: #adb5bd;\n }\n\n .btn-icon:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .btn-icon.btn-save {\n color: #16a34a;\n border-color: #86efac;\n }\n\n .btn-icon.btn-save:hover {\n background: #f0fdf4;\n border-color: #16a34a;\n }\n\n .btn-icon.btn-open {\n color: #2563eb;\n border-color: #93c5fd;\n }\n\n .btn-icon.btn-open:hover {\n background: #eff6ff;\n border-color: #2563eb;\n }\n\n .grid-container {\n flex: 1;\n overflow: hidden;\n min-height: 200px;\n height: 500px;\n }\n\n .error-banner {\n padding: 8px 12px;\n background: #fff3cd;\n border-bottom: 1px solid #ffc107;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: #856404;\n }\n\n .fallback-note {\n font-style: italic;\n color: #6c757d;\n }\n\n .empty-state, .error-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 200px;\n color: #6c757d;\n text-align: center;\n gap: 12px;\n }\n\n .empty-state i, .error-state i {\n font-size: 32px;\n }\n\n .error-state {\n color: #dc3545;\n }\n\n .plan-content {\n flex: 1;\n overflow: auto;\n padding: 16px;\n }\n "] }]
|
|
819
|
+
args: [{ standalone: false, selector: 'mj-data-artifact-viewer', template: "<div class=\"data-artifact-viewer\" [ngClass]=\"cssClass\">\n @if (spec) {\n @if (HasData || IsLoading) {\n <!-- Toolbar -->\n <div class=\"data-toolbar\">\n <div class=\"data-title\">\n <i class=\"fas fa-table\"></i>\n <span>{{ spec.title || 'Data Results' }}</span>\n @if (IsLive) {\n <span class=\"live-badge\">Live</span>\n }\n @if (DisplayRowCount != null) {\n <span class=\"row-count\">{{ DisplayRowCount }} rows</span>\n }\n @if (DisplayExecutionTime != null) {\n <span class=\"exec-time\">{{ DisplayExecutionTime }}ms</span>\n }\n </div>\n <div class=\"data-actions\">\n @if (IsLive) {\n <button class=\"btn-icon\" title=\"Refresh data\" (click)=\"OnRefresh()\" [disabled]=\"IsLoading\">\n <i class=\"fas fa-sync-alt\" [class.fa-spin]=\"IsLoading\"></i> Refresh\n </button>\n }\n\n @switch (QuerySyncState) {\n <!-- S1: No saved query, latest version \u2192 Save Query -->\n @case ('no-query-latest') {\n <button class=\"btn-icon btn-save\" title=\"Save as reusable query\"\n (click)=\"OnShowSaveDialog()\">\n <i class=\"fas fa-save\"></i> Save Query\n </button>\n }\n\n <!-- S2: SQL matches \u2192 green badge + Open -->\n @case ('synced') {\n <span class=\"query-badge query-badge-synced\">\n <i class=\"fas fa-check\"></i> Saved at v{{ SavedAtVersion }}\n </span>\n <button class=\"btn-icon btn-open\" title=\"Open saved query record\"\n (click)=\"OnOpenSavedQuery()\">\n <i class=\"fas fa-external-link-alt\"></i> Open Query\n </button>\n }\n\n <!-- S3: SQL differs, latest version \u2192 amber badge + dropdown -->\n @case ('outdated-latest') {\n <span class=\"query-badge query-badge-outdated\">\n <i class=\"fas fa-circle-exclamation\"></i> Saved at v{{ SavedAtVersion }}\n </span>\n <div class=\"dropdown-wrapper\">\n <button class=\"btn-icon btn-warning\" title=\"Update or save query\"\n (click)=\"OnToggleUpdateDropdown()\">\n <i class=\"fas fa-save\"></i> Update Query <i class=\"fas fa-caret-down\"></i>\n </button>\n @if (ShowUpdateDropdown) {\n <div class=\"query-dropdown\" (click)=\"$event.stopPropagation()\">\n <div class=\"query-dropdown-item\" (click)=\"OnUpdateExistingQuery()\">\n <i class=\"fas fa-arrow-up-from-bracket\" style=\"color:#d97706\"></i>\n <div>\n <div class=\"dropdown-label\">Update \"{{ SavedQueryDisplayName }}\"</div>\n <div class=\"dropdown-desc\">Replace saved query SQL with this version's SQL</div>\n </div>\n </div>\n <div class=\"query-dropdown-item\" (click)=\"OnSaveAsNewQuery()\">\n <i class=\"fas fa-plus\" style=\"color:#16a34a\"></i>\n <div>\n <div class=\"dropdown-label\">Save as New Query</div>\n <div class=\"dropdown-desc\">Create a new query, keep the original unchanged</div>\n </div>\n </div>\n <div class=\"query-dropdown-item\" (click)=\"OnOpenSavedQuery(); OnCloseDropdown()\">\n <i class=\"fas fa-external-link-alt\" style=\"color:#6c757d\"></i>\n <div>\n <div class=\"dropdown-label\">Open Saved Query</div>\n <div class=\"dropdown-desc\">View \"{{ SavedQueryDisplayName }}\" (saved at v{{ SavedAtVersion }})</div>\n </div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- S4: Older version, query was updated ahead -->\n @case ('query-ahead') {\n <span class=\"query-badge query-badge-muted\">\n <i class=\"fas fa-clock-rotate-left\"></i> Query updated at v{{ SavedAtVersion }}\n </span>\n <button class=\"btn-icon btn-muted\" title=\"Open saved query record\"\n (click)=\"OnOpenSavedQuery()\">\n <i class=\"fas fa-external-link-alt\"></i> Open Query\n </button>\n }\n\n <!-- S5: Middle version, saved at older, not latest -->\n @case ('query-behind') {\n <span class=\"query-badge query-badge-muted\">\n <i class=\"fas fa-clock-rotate-left\"></i> Saved at v{{ SavedAtVersion }}\n </span>\n <button class=\"btn-icon btn-muted\" title=\"Open saved query record\"\n (click)=\"OnOpenSavedQuery()\">\n <i class=\"fas fa-external-link-alt\"></i> Open Query\n </button>\n }\n\n <!-- S6: No saved query, older version \u2192 no actions -->\n @case ('no-query-older') {\n <!-- No query actions available on older versions -->\n }\n }\n </div>\n </div>\n\n <!-- Grid -->\n <div class=\"grid-container\">\n <!-- Saving overlay -->\n @if (IsSaving) {\n <div class=\"saving-overlay\">\n <mj-loading [text]=\"SavingMessage\"></mj-loading>\n </div>\n }\n @if (IsLoading) {\n <mj-loading text=\"Loading data...\"></mj-loading>\n } @else if (HasError && HasData) {\n <div class=\"error-banner\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <span>{{ ErrorMessage }}</span>\n <span class=\"fallback-note\">(Showing cached data)</span>\n </div>\n <mj-query-data-grid\n [ColumnConfigs]=\"GridColumnConfigs\"\n [Data]=\"GridData\"\n [ShowToolbar]=\"false\"\n [ShowRefresh]=\"false\"\n [PersistState]=\"false\"\n [SelectionMode]=\"'none'\"\n (EntityLinkClick)=\"OnEntityLinkClick($event)\"\n Height=\"100%\">\n </mj-query-data-grid>\n } @else if (HasError) {\n <div class=\"error-state\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <p>{{ ErrorMessage }}</p>\n </div>\n } @else {\n <mj-query-data-grid\n [ColumnConfigs]=\"GridColumnConfigs\"\n [Data]=\"GridData\"\n [ShowToolbar]=\"false\"\n [ShowRefresh]=\"false\"\n [PersistState]=\"false\"\n [SelectionMode]=\"'none'\"\n (EntityLinkClick)=\"OnEntityLinkClick($event)\"\n Height=\"100%\">\n </mj-query-data-grid>\n }\n </div>\n } @else if (spec.plan) {\n <!-- Plan-only view (no results yet) -->\n <div class=\"data-toolbar\">\n <div class=\"data-title\">\n <i class=\"fas fa-diagram-project\"></i>\n <span>{{ spec.title || 'Query Plan' }}</span>\n </div>\n </div>\n <div class=\"plan-content\">\n <mj-markdown\n [data]=\"spec.plan\"\n [enableMermaid]=\"true\"\n [enableHighlight]=\"true\"\n [enableCollapsibleHeadings]=\"false\"\n [enableSmartypants]=\"true\">\n </mj-markdown>\n </div>\n } @else {\n <!-- No data and no plan -->\n <div class=\"empty-state\">\n <i class=\"fas fa-inbox\"></i>\n <p>No data to display</p>\n </div>\n }\n } @else if (HasError) {\n <div class=\"error-state\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <p>{{ ErrorMessage }}</p>\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fas fa-inbox\"></i>\n <p>No data to display</p>\n </div>\n }\n\n <!-- Save Query Panel (slide-in) -->\n @if (ShowSaveDialog) {\n <mj-save-query-panel\n [QueryName]=\"spec?.title || 'Untitled Query'\"\n [QueryDescription]=\"''\"\n [SQL]=\"spec?.metadata?.sql || ''\"\n (Saved)=\"OnQuerySaved($event)\"\n (Cancelled)=\"ShowSaveDialog = false\">\n </mj-save-query-panel>\n }\n</div>\n", styles: [".data-artifact-viewer {\n display: flex;\n flex-direction: column;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.data-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n background: #f8f9fa;\n border-bottom: 1px solid #dee2e6;\n}\n\n.data-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n font-size: 13px;\n color: #333;\n}\n\n.data-title i {\n color: #6c757d;\n}\n\n.row-count {\n padding: 2px 8px;\n background: #e9ecef;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: #495057;\n}\n\n.exec-time {\n padding: 2px 8px;\n background: #d4edda;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: #155724;\n}\n\n.live-badge {\n padding: 2px 8px;\n background: #cce5ff;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n color: #004085;\n}\n\n.data-actions {\n display: flex;\n gap: 6px;\n}\n\n.btn-icon {\n padding: 4px 10px;\n background: white;\n border: 1px solid #ced4da;\n border-radius: 4px;\n cursor: pointer;\n font-size: 11px;\n display: flex;\n align-items: center;\n gap: 4px;\n color: #495057;\n}\n\n.btn-icon:hover {\n background: #e9ecef;\n border-color: #adb5bd;\n}\n\n.btn-icon:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.btn-icon.btn-save {\n color: #16a34a;\n border-color: #86efac;\n}\n\n.btn-icon.btn-save:hover {\n background: #f0fdf4;\n border-color: #16a34a;\n}\n\n.btn-icon.btn-open {\n color: #2563eb;\n border-color: #93c5fd;\n}\n\n.btn-icon.btn-open:hover {\n background: #eff6ff;\n border-color: #2563eb;\n}\n\n.grid-container {\n flex: 1;\n overflow: hidden;\n min-height: 200px;\n height: 500px;\n position: relative;\n}\n\n/* Saving overlay \u2014 semi-transparent panel over the grid */\n.saving-overlay {\n position: absolute;\n inset: 0;\n background: rgba(255, 255, 255, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 50;\n}\n\n.error-banner {\n padding: 8px 12px;\n background: #fff3cd;\n border-bottom: 1px solid #ffc107;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: #856404;\n}\n\n.fallback-note {\n font-style: italic;\n color: #6c757d;\n}\n\n.empty-state, .error-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 200px;\n color: #6c757d;\n text-align: center;\n gap: 12px;\n}\n\n.empty-state i, .error-state i {\n font-size: 32px;\n}\n\n.error-state {\n color: #dc3545;\n}\n\n.plan-content {\n flex: 1;\n overflow: auto;\n padding: 16px;\n}\n\n/* Query sync badges */\n.query-badge {\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n\n.query-badge-synced {\n background: #d1fae5;\n color: #065f46;\n}\n\n.query-badge-outdated {\n background: #fff3cd;\n color: #856404;\n}\n\n.query-badge-muted {\n background: #f3f4f6;\n color: #9ca3af;\n}\n\n/* Dropdown wrapper */\n.dropdown-wrapper {\n position: relative;\n}\n\n.btn-icon.btn-warning {\n color: #d97706;\n border-color: #fcd34d;\n}\n\n.btn-icon.btn-warning:hover {\n background: #fffbeb;\n border-color: #d97706;\n}\n\n.btn-icon.btn-muted {\n color: #9ca3af;\n border-color: #e5e7eb;\n}\n\n/* Query actions dropdown */\n.query-dropdown {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.12);\n overflow: hidden;\n min-width: 280px;\n z-index: 100;\n}\n\n.query-dropdown-item {\n padding: 10px 14px;\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n cursor: pointer;\n border-bottom: 1px solid #f0f0f0;\n transition: background 0.1s;\n}\n\n.query-dropdown-item:last-child {\n border-bottom: none;\n}\n\n.query-dropdown-item:hover {\n background: #f8f9fa;\n}\n\n.query-dropdown-item i {\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n}\n\n.dropdown-label {\n font-weight: 500;\n}\n\n.dropdown-desc {\n font-size: 11px;\n color: #6c757d;\n margin-top: 2px;\n}\n"] }]
|
|
584
820
|
}], () => [{ type: i0.ChangeDetectorRef }], { openEntityRecord: [{
|
|
585
821
|
type: Output
|
|
586
822
|
}], navigationRequest: [{
|
|
587
823
|
type: Output
|
|
588
824
|
}] }); })();
|
|
589
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(DataArtifactViewerComponent, { className: "DataArtifactViewerComponent", filePath: "src/lib/components/plugins/data-artifact-viewer.component.ts", lineNumber:
|
|
825
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(DataArtifactViewerComponent, { className: "DataArtifactViewerComponent", filePath: "src/lib/components/plugins/data-artifact-viewer.component.ts", lineNumber: 108 }); })();
|
|
590
826
|
//# sourceMappingURL=data-artifact-viewer.component.js.map
|