@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.
@@ -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 DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_10_Template(rf, ctx) { if (rf & 1) {
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 DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_10_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.ShowSaveDialog = true); });
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 DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_11_Template(rf, ctx) { if (rf & 1) {
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, "button", 21);
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, " Open Query ");
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 DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_13_Template(rf, ctx) { if (rf & 1) {
71
- i0.ɵɵelement(0, "mj-loading", 15);
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 DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_14_Template(rf, ctx) { if (rf & 1) {
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, "div", 23);
76
- i0.ɵɵelement(1, "i", 24);
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", 25);
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", 26);
84
- i0.ɵɵlistener("EntityLinkClick", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_14_Template_mj_query_data_grid_EntityLinkClick_6_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnEntityLinkClick($event)); });
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 DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_15_Template(rf, ctx) { if (rf & 1) {
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", 24);
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 DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_16_Template(rf, ctx) { if (rf & 1) {
105
- const _r6 = i0.ɵɵgetCurrentView();
106
- i0.ɵɵelementStart(0, "mj-query-data-grid", 26);
107
- i0.ɵɵlistener("EntityLinkClick", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_16_Template_mj_query_data_grid_EntityLinkClick_0_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnEntityLinkClick($event)); });
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, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_10_Template, 3, 0, "button", 12);
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(12, "div", 14);
129
- i0.ɵɵconditionalCreate(13, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_13_Template, 1, 0, "mj-loading", 15)(14, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_14_Template, 7, 7)(15, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_15_Template, 4, 1, "div", 1)(16, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_16_Template, 1, 6, "mj-query-data-grid", 16);
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.CanSaveQuery ? 10 : -1);
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.spec.savedQueryId ? 11 : -1);
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", 27);
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", 28);
157
- i0.ɵɵelement(6, "mj-markdown", 29);
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", 30);
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, 17, 8)(1, DataArtifactViewerComponent_Conditional_1_Conditional_1_Template, 7, 6)(2, DataArtifactViewerComponent_Conditional_1_Conditional_2_Template, 4, 0, "div", 2);
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", 24);
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", 30);
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 _r7 = i0.ɵɵgetCurrentView();
199
- i0.ɵɵelementStart(0, "mj-save-query-panel", 31);
200
- i0.ɵɵlistener("Saved", function DataArtifactViewerComponent_Conditional_4_Template_mj_save_query_panel_Saved_0_listener($event) { i0.ɵɵrestoreView(_r7); 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(_r7); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.ShowSaveDialog = false); });
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 || !!this.spec.plan);
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
- /** Whether this query can be saved (has SQL but hasn't been saved yet) */
384
- get CanSaveQuery() {
385
- return !!this.spec?.metadata?.sql && !this.spec?.savedQueryId;
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
- /** Handle successful save from the dialog */
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
- // Mutate spec to record the saved query
401
- this.spec.savedQueryId = event.queryId;
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 UpdateArtifactContent() {
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"], ["title", "Open saved query record", 1, "btn-icon", "btn-open"], [1, "grid-container"], ["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"], ["title", "Open saved query record", 1, "btn-icon", "btn-open", 3, "click"], [1, "fas", "fa-external-link-alt"], [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) {
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 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 }\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 }"] });
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: 369 }); })();
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