sqlite-hub 0.8.7 → 0.9.1
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/changelog.md +16 -0
- package/frontend/index.html +1 -1
- package/frontend/js/api.js +67 -0
- package/frontend/js/app.js +1554 -999
- package/frontend/js/components/modal.js +208 -3
- package/frontend/js/components/queryEditor.js +9 -4
- package/frontend/js/components/queryHistoryDetail.js +34 -12
- package/frontend/js/components/queryHistoryPanel.js +8 -11
- package/frontend/js/components/rowEditorPanel.js +26 -5
- package/frontend/js/components/sidebar.js +49 -5
- package/frontend/js/components/structureGraph.js +614 -632
- package/frontend/js/components/tableDesignerSqlPreview.js +24 -25
- package/frontend/js/lib/mediaTaggingDefaults.js +27 -0
- package/frontend/js/router.js +81 -71
- package/frontend/js/store.js +3140 -2179
- package/frontend/js/views/charts.js +2 -2
- package/frontend/js/views/data.js +28 -14
- package/frontend/js/views/editor.js +172 -177
- package/frontend/js/views/mediaTagging.js +931 -0
- package/frontend/styles/base.css +11 -1
- package/frontend/styles/components.css +54 -6
- package/frontend/styles/views.css +910 -0
- package/package.json +1 -1
- package/server/routes/charts.js +4 -1
- package/server/routes/data.js +14 -0
- package/server/routes/mediaTagging.js +166 -0
- package/server/routes/sql.js +1 -0
- package/server/server.js +4 -0
- package/server/services/sqlite/dataBrowserService.js +25 -0
- package/server/services/sqlite/exportService.js +31 -1
- package/server/services/sqlite/mediaTaggingService.js +1689 -0
- package/server/services/storage/appStateStore.js +325 -3
- package/server/services/storage/queryHistoryUtils.js +165 -2
- package/shortkeys.md +5 -0
package/frontend/js/app.js
CHANGED
|
@@ -1,227 +1,469 @@
|
|
|
1
|
-
import { renderAppShell } from
|
|
2
|
-
import { renderModal } from
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { renderAppShell } from './components/appShell.js';
|
|
2
|
+
import { renderModal } from './components/modal.js';
|
|
3
|
+
import { renderQueryHistoryDetail } from './components/queryHistoryDetail.js';
|
|
4
|
+
import { renderQueryHistoryListItem } from './components/queryHistoryPanel.js';
|
|
5
|
+
import { renderSidebar } from './components/sidebar.js';
|
|
6
|
+
import { renderStatusBar } from './components/statusBar.js';
|
|
5
7
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from
|
|
8
|
+
mountStructureGraph,
|
|
9
|
+
teardownStructureGraph,
|
|
10
|
+
resetPersistedStructureGraphState,
|
|
11
|
+
} from './components/structureGraph.js';
|
|
10
12
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from
|
|
15
|
-
import { renderToasts } from
|
|
16
|
-
import { renderTopNav } from
|
|
17
|
-
import { createRouter } from
|
|
13
|
+
exportQueryChartAsPng,
|
|
14
|
+
mountQueryChartRenderer,
|
|
15
|
+
teardownQueryChartRenderer,
|
|
16
|
+
} from './components/queryChartRenderer.js';
|
|
17
|
+
import { renderToasts } from './components/toast.js';
|
|
18
|
+
import { renderTopNav } from './components/topNav.js';
|
|
19
|
+
import { createRouter } from './router.js';
|
|
18
20
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
21
|
+
createActiveConnectionBackup,
|
|
22
|
+
clearCurrentQuery,
|
|
23
|
+
clearDataRowSelection,
|
|
24
|
+
clearEditorRowSelection,
|
|
25
|
+
clearEditorResults,
|
|
26
|
+
clearQueryHistorySelection,
|
|
27
|
+
closeModal,
|
|
28
|
+
dismissMediaTaggingIssue,
|
|
29
|
+
dismissToast,
|
|
30
|
+
executeCurrentQuery,
|
|
31
|
+
exportCurrentDataTableCsv,
|
|
32
|
+
exportCurrentQueryCsv,
|
|
33
|
+
getState,
|
|
34
|
+
initializeApp,
|
|
35
|
+
loadMoreQueryHistory,
|
|
36
|
+
openModal,
|
|
37
|
+
openOverviewInFinder,
|
|
38
|
+
openQueryHistoryInEditor,
|
|
39
|
+
openDeleteDataRowModal,
|
|
40
|
+
openDeleteEditorRowModal,
|
|
41
|
+
openDeleteQueryHistoryModal,
|
|
42
|
+
openDeleteQueryChartModal,
|
|
43
|
+
openDataRowByIdentity,
|
|
44
|
+
openEditConnectionModal,
|
|
45
|
+
openCreateQueryChartModal,
|
|
46
|
+
openEditQueryChartModal,
|
|
47
|
+
refreshCurrentRoute,
|
|
48
|
+
refreshMediaTaggingPreview,
|
|
49
|
+
removeConnection,
|
|
50
|
+
removeCurrentMediaTag,
|
|
51
|
+
resetMediaTaggingQueriesToDefault,
|
|
52
|
+
resetSkippedMediaTaggingItems,
|
|
53
|
+
runQueryHistoryItem,
|
|
54
|
+
skipCurrentMediaTaggingItem,
|
|
55
|
+
saveCurrentQueryChartDraft,
|
|
56
|
+
saveCurrentMediaTaggingConfig,
|
|
57
|
+
selectDataRow,
|
|
58
|
+
selectEditorRow,
|
|
59
|
+
selectConnection,
|
|
60
|
+
selectQueryHistoryItem,
|
|
61
|
+
selectStructureEntry,
|
|
62
|
+
setTableDesignerSearchQuery,
|
|
63
|
+
setTableDesignerSqlPreviewVisibility,
|
|
64
|
+
toggleStructureTablesPanel,
|
|
65
|
+
setDataPage,
|
|
66
|
+
setDataPageSize,
|
|
67
|
+
setDataSearchColumn,
|
|
68
|
+
setDataSearchQuery,
|
|
69
|
+
toggleDataTablesPanel,
|
|
70
|
+
setCurrentQuery,
|
|
71
|
+
setChartsHeightPreset,
|
|
72
|
+
setEditorPanelVisibility,
|
|
73
|
+
setEditorTab,
|
|
74
|
+
submitDeleteChartConfirmation,
|
|
75
|
+
submitCreateMediaTaggingTagTable,
|
|
76
|
+
submitCreateMediaTaggingMappingTable,
|
|
77
|
+
submitDeleteQueryHistoryConfirmation,
|
|
78
|
+
setQueryHistoryPanelVisibility,
|
|
79
|
+
sortDataTableByColumn,
|
|
80
|
+
sortEditorResultsByColumn,
|
|
81
|
+
setQueryHistorySearchInput,
|
|
82
|
+
setQueryHistoryTab,
|
|
83
|
+
setRoute,
|
|
84
|
+
saveQueryHistoryNotes,
|
|
85
|
+
saveQueryHistoryTitle,
|
|
86
|
+
saveCurrentTableDesignerDraft,
|
|
87
|
+
toggleChartsResultsPanel,
|
|
88
|
+
toggleChartsSqlPanel,
|
|
89
|
+
setMediaTaggingWorkflowMediaDetailsVisible,
|
|
90
|
+
setMediaTaggingWorkflowMediaRotationDegrees,
|
|
91
|
+
queueTableDesignerCsvImport,
|
|
92
|
+
showToast,
|
|
93
|
+
submitCreateConnection,
|
|
94
|
+
createCurrentMediaTag,
|
|
95
|
+
submitDeleteRowConfirmation,
|
|
96
|
+
submitDataRowUpdate,
|
|
97
|
+
submitEditorRowUpdate,
|
|
98
|
+
submitEditConnection,
|
|
99
|
+
submitImportSql,
|
|
100
|
+
submitOpenConnection,
|
|
101
|
+
subscribe,
|
|
102
|
+
toggleCurrentMediaTagSelection,
|
|
103
|
+
toggleQueryHistorySavedState,
|
|
104
|
+
updateCurrentMediaTaggingField,
|
|
105
|
+
updateCurrentMediaTaggingTagFormField,
|
|
106
|
+
updateCurrentQueryChartDraftConfigField,
|
|
107
|
+
updateCurrentQueryChartDraftField,
|
|
108
|
+
updateCurrentTableDesignerColumnField,
|
|
109
|
+
updateCurrentTableDesignerField,
|
|
110
|
+
addCurrentTableDesignerColumn,
|
|
111
|
+
applyCurrentMediaTaggingSelection,
|
|
112
|
+
removeCurrentTableDesignerColumn,
|
|
113
|
+
} from './store.js';
|
|
114
|
+
import { renderChartsView } from './views/charts.js';
|
|
115
|
+
import { renderConnectionsView } from './views/connections.js';
|
|
116
|
+
import { renderDataRowEditorPanel, renderDataView } from './views/data.js';
|
|
117
|
+
import { renderEditorView } from './views/editor.js';
|
|
118
|
+
import { renderLandingView } from './views/landing.js';
|
|
119
|
+
import { renderMediaTaggingView } from './views/mediaTagging.js';
|
|
120
|
+
import { renderOverviewView } from './views/overview.js';
|
|
121
|
+
import { renderSettingsView } from './views/settings.js';
|
|
122
|
+
import { renderStructureView } from './views/structure.js';
|
|
123
|
+
import { renderTableDesignerView } from './views/tableDesigner.js';
|
|
124
|
+
import { highlightSql } from './utils/format.js';
|
|
125
|
+
|
|
126
|
+
const appRoot = document.querySelector('#app');
|
|
106
127
|
|
|
107
128
|
appRoot.innerHTML = renderAppShell();
|
|
108
129
|
|
|
109
130
|
const shellRefs = {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
131
|
+
shell: document.querySelector('.app-shell'),
|
|
132
|
+
topNav: document.querySelector('#top-nav'),
|
|
133
|
+
sidebar: document.querySelector('#sidebar'),
|
|
134
|
+
view: document.querySelector('#app-view'),
|
|
135
|
+
panel: document.querySelector('#app-panel'),
|
|
136
|
+
statusBar: document.querySelector('#status-bar'),
|
|
137
|
+
modal: document.querySelector('#modal-root'),
|
|
138
|
+
toast: document.querySelector('#toast-root'),
|
|
118
139
|
};
|
|
119
140
|
let lastRenderedRoutePath = null;
|
|
120
|
-
let
|
|
121
|
-
let
|
|
122
|
-
let
|
|
123
|
-
let
|
|
124
|
-
let
|
|
125
|
-
let
|
|
126
|
-
let
|
|
141
|
+
let lastRenderedRouteName = null;
|
|
142
|
+
let lastRenderedTopNavMarkup = '';
|
|
143
|
+
let lastRenderedSidebarMarkup = '';
|
|
144
|
+
let lastRenderedStatusBarMarkup = '';
|
|
145
|
+
let lastRenderedMainMarkup = '';
|
|
146
|
+
let lastRenderedPanelMarkup = '';
|
|
147
|
+
let lastRenderedModalMarkup = '';
|
|
148
|
+
let lastRenderedToastMarkup = '';
|
|
127
149
|
let lastRenderedPanelOpen = false;
|
|
128
150
|
let lastRenderedLockedRoute = false;
|
|
129
151
|
let pendingNewTableDesignerAutofocus = false;
|
|
130
152
|
let pendingQueryEditorFocus = false;
|
|
153
|
+
let pendingMediaTaggingTagSearchFocus = false;
|
|
131
154
|
|
|
132
155
|
function invalidateMainRenderCache() {
|
|
133
|
-
|
|
156
|
+
lastRenderedMainMarkup = null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function isMediaTaggingRouteName(routeName) {
|
|
160
|
+
return routeName === 'mediaTaggingSetup' || routeName === 'mediaTaggingQueue';
|
|
134
161
|
}
|
|
135
162
|
|
|
136
163
|
function resetStructureGraphForDatabaseChange() {
|
|
137
|
-
|
|
164
|
+
resetPersistedStructureGraphState();
|
|
138
165
|
}
|
|
139
166
|
|
|
140
167
|
function renderQueryHighlightMarkup(query) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
168
|
+
if (query) {
|
|
169
|
+
return highlightSql(query);
|
|
170
|
+
}
|
|
144
171
|
|
|
145
|
-
|
|
172
|
+
return '<span class="text-on-surface-variant/35">SELECT name FROM sqlite_master WHERE type = \'table\';</span>';
|
|
146
173
|
}
|
|
147
174
|
|
|
148
175
|
function syncQueryEditorHighlight(textarea) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
176
|
+
if (!(textarea instanceof HTMLTextAreaElement)) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
152
179
|
|
|
153
|
-
|
|
154
|
-
|
|
180
|
+
const layer = textarea.closest('.query-editor-layer');
|
|
181
|
+
const highlightNode = layer?.querySelector('[data-query-editor-highlight]');
|
|
155
182
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
183
|
+
if (!(highlightNode instanceof HTMLElement)) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
159
186
|
|
|
160
|
-
|
|
187
|
+
highlightNode.innerHTML = renderQueryHighlightMarkup(textarea.value);
|
|
161
188
|
}
|
|
162
189
|
|
|
163
190
|
function syncQueryEditorScroll(textarea) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
191
|
+
if (!(textarea instanceof HTMLTextAreaElement)) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
167
194
|
|
|
168
|
-
|
|
169
|
-
|
|
195
|
+
const layer = textarea.closest('.query-editor-layer');
|
|
196
|
+
const highlightNode = layer?.querySelector('[data-query-editor-highlight]');
|
|
197
|
+
const gutterNode = textarea.closest('.query-editor-shell')?.querySelector('[data-query-editor-gutter]');
|
|
170
198
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
199
|
+
if (!(highlightNode instanceof HTMLElement)) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
174
202
|
|
|
175
|
-
|
|
203
|
+
highlightNode.style.transform = `translate(${-textarea.scrollLeft}px, ${-textarea.scrollTop}px)`;
|
|
204
|
+
|
|
205
|
+
if (gutterNode instanceof HTMLElement) {
|
|
206
|
+
gutterNode.style.transform = `translateY(${-textarea.scrollTop}px)`;
|
|
207
|
+
}
|
|
176
208
|
}
|
|
177
209
|
|
|
178
|
-
function
|
|
179
|
-
|
|
180
|
-
|
|
210
|
+
function syncMediaTagSelectionUi(input, checked) {
|
|
211
|
+
if (!(input instanceof HTMLInputElement)) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
181
214
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
215
|
+
const tagOption = input.closest('.media-tagging-tag-option');
|
|
216
|
+
input.checked = Boolean(checked);
|
|
217
|
+
tagOption?.classList.toggle('is-selected', Boolean(checked));
|
|
218
|
+
syncMediaTaggingApplyButtonUi(input);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function syncMediaTaggingApplyButtonUi(node) {
|
|
222
|
+
if (!(node instanceof HTMLElement)) {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const tagPanel = node.closest('.media-tagging-tag-panel');
|
|
227
|
+
|
|
228
|
+
if (!(tagPanel instanceof HTMLElement)) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const applyButton = tagPanel.querySelector('[data-action="apply-media-tagging"]');
|
|
233
|
+
|
|
234
|
+
if (!(applyButton instanceof HTMLButtonElement)) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const selectedCount = tagPanel.querySelectorAll('.media-tagging-tag-option__checkbox:checked').length;
|
|
239
|
+
const canApply = applyButton.dataset.canApply === 'true';
|
|
240
|
+
applyButton.textContent = `${selectedCount} tagged & next`;
|
|
241
|
+
applyButton.disabled = !canApply || selectedCount < 1;
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function syncMediaTaggingCurrentMediaUi(button, detailsVisible) {
|
|
246
|
+
if (!(button instanceof HTMLButtonElement)) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const preview = button.closest('.media-tagging-preview');
|
|
251
|
+
|
|
252
|
+
if (!(preview instanceof HTMLElement)) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const nextVisible = Boolean(detailsVisible);
|
|
257
|
+
const expandedLabel = button.dataset.expandedLabel || 'Shrink Media Viewer';
|
|
258
|
+
const collapsedLabel = button.dataset.collapsedLabel || 'Show Media Viewer';
|
|
259
|
+
const expandedMarkup = `<span class="material-symbols-outlined">visibility_off</span> ${expandedLabel}`;
|
|
260
|
+
|
|
261
|
+
preview.classList.toggle('media-tagging-preview--meta-hidden', !nextVisible);
|
|
262
|
+
button.dataset.nextValue = nextVisible ? 'false' : 'true';
|
|
263
|
+
button.setAttribute('aria-expanded', nextVisible ? 'true' : 'false');
|
|
264
|
+
button.innerHTML = nextVisible ? expandedMarkup : collapsedLabel;
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function normalizeMediaTaggingRotationDegrees(value) {
|
|
269
|
+
const numericValue = Number(value);
|
|
270
|
+
|
|
271
|
+
if (!Number.isFinite(numericValue)) {
|
|
272
|
+
return 0;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return ((Math.round(numericValue / 90) * 90) % 360 + 360) % 360;
|
|
276
|
+
}
|
|
190
277
|
|
|
191
|
-
|
|
192
|
-
|
|
278
|
+
function syncMediaTaggingMediaRotationUi(node, rotationDegrees) {
|
|
279
|
+
if (!(node instanceof HTMLElement)) {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const preview = node.closest('.media-tagging-preview');
|
|
284
|
+
const target = preview?.querySelector('[data-media-tagging-rotation-target]');
|
|
285
|
+
|
|
286
|
+
if (!(preview instanceof HTMLElement) || !(target instanceof HTMLElement)) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const normalizedRotation = normalizeMediaTaggingRotationDegrees(rotationDegrees);
|
|
291
|
+
|
|
292
|
+
target.style.setProperty('--media-tagging-preview-rotation', `${normalizedRotation}deg`);
|
|
293
|
+
target.dataset.rotationDegrees = String(normalizedRotation);
|
|
294
|
+
target.classList.toggle('is-rotated-quarter', normalizedRotation === 90 || normalizedRotation === 270);
|
|
295
|
+
|
|
296
|
+
const resetButton = preview.querySelector(
|
|
297
|
+
'[data-action="rotate-media-tagging-current-media"][data-rotation-command="reset"]',
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
if (resetButton instanceof HTMLButtonElement) {
|
|
301
|
+
resetButton.disabled = normalizedRotation === 0;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function syncMediaTaggingTagSearchUi(input) {
|
|
308
|
+
if (!(input instanceof HTMLInputElement)) {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const tagPanel = input.closest('.media-tagging-tag-panel');
|
|
313
|
+
|
|
314
|
+
if (!(tagPanel instanceof HTMLElement)) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const normalizedQuery = input.value.trim().toLowerCase();
|
|
319
|
+
const tagOptions = tagPanel.querySelectorAll('.media-tagging-tag-option');
|
|
320
|
+
|
|
321
|
+
for (const tagOption of tagOptions) {
|
|
322
|
+
if (!(tagOption instanceof HTMLElement)) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const searchText = String(tagOption.dataset.tagSearchText ?? '').trim().toLowerCase();
|
|
327
|
+
tagOption.hidden = Boolean(normalizedQuery) && !searchText.includes(normalizedQuery);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function syncDataRowSelectionUi(selectedRowIndex = null) {
|
|
334
|
+
if (getState().route.name !== 'data') {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const rowNodes = shellRefs.view.querySelectorAll('[data-action="select-data-row"][data-row-index]');
|
|
339
|
+
|
|
340
|
+
for (const rowNode of rowNodes) {
|
|
341
|
+
if (!(rowNode instanceof HTMLElement)) {
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
rowNode.classList.toggle('is-selected', rowNode.dataset.rowIndex === String(selectedRowIndex));
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const panelMarkup = renderDataRowEditorPanel(getState());
|
|
349
|
+
const panelOpen = Boolean(panelMarkup);
|
|
350
|
+
|
|
351
|
+
shellRefs.panel.innerHTML = panelMarkup;
|
|
352
|
+
shellRefs.shell.classList.toggle('panel-open', panelOpen);
|
|
353
|
+
lastRenderedPanelMarkup = panelMarkup;
|
|
354
|
+
lastRenderedPanelOpen = panelOpen;
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function syncQueryHistoryUi(historyId) {
|
|
359
|
+
const state = getState();
|
|
360
|
+
const numericId = Number(historyId);
|
|
361
|
+
|
|
362
|
+
if (!Number.isInteger(numericId) || numericId < 1) {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const historyItem = state.editor.history.find(entry => Number(entry.id) === numericId) ?? state.editor.historyDetail ?? null;
|
|
367
|
+
const listItemNode = shellRefs.view.querySelector(
|
|
368
|
+
`[data-action="select-query-history-item"][data-history-id="${String(numericId)}"]`,
|
|
369
|
+
)?.closest('.query-history-item');
|
|
370
|
+
|
|
371
|
+
if (historyItem && listItemNode instanceof HTMLElement) {
|
|
372
|
+
listItemNode.outerHTML = renderQueryHistoryListItem(
|
|
373
|
+
historyItem,
|
|
374
|
+
state.editor.historyActiveId,
|
|
375
|
+
state.editor.historySelectedId,
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (state.editor.historySelectedId === numericId) {
|
|
380
|
+
const panelMarkup = renderQueryHistoryDetail({
|
|
381
|
+
item: state.editor.historyDetail,
|
|
382
|
+
runs: state.editor.historyRuns,
|
|
383
|
+
loading: state.editor.historyDetailLoading,
|
|
384
|
+
error: state.editor.historyDetailError,
|
|
385
|
+
});
|
|
386
|
+
const panelOpen = Boolean(panelMarkup);
|
|
387
|
+
|
|
388
|
+
shellRefs.panel.innerHTML = panelMarkup;
|
|
389
|
+
shellRefs.shell.classList.toggle('panel-open', panelOpen);
|
|
390
|
+
lastRenderedPanelMarkup = panelMarkup;
|
|
391
|
+
lastRenderedPanelOpen = panelOpen;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function syncQueryHistorySelectionUi(selectedHistoryId = null) {
|
|
398
|
+
const historyButtons = shellRefs.view.querySelectorAll(
|
|
399
|
+
'[data-action="select-query-history-item"][data-history-id]',
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
for (const button of historyButtons) {
|
|
403
|
+
if (!(button instanceof HTMLElement) || !button.classList.contains('query-history-icon-button')) {
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
button.classList.toggle('is-active', button.dataset.historyId === String(selectedHistoryId));
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (selectedHistoryId === null) {
|
|
411
|
+
shellRefs.panel.innerHTML = '';
|
|
412
|
+
shellRefs.shell.classList.remove('panel-open');
|
|
413
|
+
lastRenderedPanelMarkup = '';
|
|
414
|
+
lastRenderedPanelOpen = false;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function readFileAsBase64(file) {
|
|
421
|
+
return new Promise((resolve, reject) => {
|
|
422
|
+
const reader = new FileReader();
|
|
423
|
+
|
|
424
|
+
reader.onload = () => {
|
|
425
|
+
const result = String(reader.result ?? '');
|
|
426
|
+
const markerIndex = result.indexOf(',');
|
|
427
|
+
resolve(markerIndex >= 0 ? result.slice(markerIndex + 1) : result);
|
|
428
|
+
};
|
|
429
|
+
reader.onerror = () => {
|
|
430
|
+
reject(reader.error ?? new Error('The selected logo could not be read.'));
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
reader.readAsDataURL(file);
|
|
434
|
+
});
|
|
193
435
|
}
|
|
194
436
|
|
|
195
437
|
function readFileAsText(file) {
|
|
196
|
-
|
|
197
|
-
|
|
438
|
+
return new Promise((resolve, reject) => {
|
|
439
|
+
const reader = new FileReader();
|
|
198
440
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
441
|
+
reader.onload = () => {
|
|
442
|
+
resolve(String(reader.result ?? ''));
|
|
443
|
+
};
|
|
444
|
+
reader.onerror = () => {
|
|
445
|
+
reject(reader.error ?? new Error('The selected CSV file could not be read.'));
|
|
446
|
+
};
|
|
205
447
|
|
|
206
|
-
|
|
207
|
-
|
|
448
|
+
reader.readAsText(file);
|
|
449
|
+
});
|
|
208
450
|
}
|
|
209
451
|
|
|
210
452
|
async function buildConnectionLogoUpload(file) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
453
|
+
if (!(file instanceof File) || !file.size) {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return {
|
|
458
|
+
fileName: file.name,
|
|
459
|
+
mimeType: file.type,
|
|
460
|
+
base64: await readFileAsBase64(file),
|
|
461
|
+
};
|
|
220
462
|
}
|
|
221
463
|
|
|
222
464
|
function renderNotFoundView() {
|
|
223
|
-
|
|
224
|
-
|
|
465
|
+
return {
|
|
466
|
+
main: `
|
|
225
467
|
<section class="landing-view machined-grid px-6">
|
|
226
468
|
<div class="text-center z-10">
|
|
227
469
|
<p class="font-mono text-[10px] uppercase tracking-[0.3em] text-primary-container/40">
|
|
@@ -241,933 +483,1246 @@ function renderNotFoundView() {
|
|
|
241
483
|
</div>
|
|
242
484
|
</section>
|
|
243
485
|
`,
|
|
244
|
-
|
|
245
|
-
|
|
486
|
+
panel: '',
|
|
487
|
+
};
|
|
246
488
|
}
|
|
247
489
|
|
|
248
490
|
function resolveView(state) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
491
|
+
switch (state.route.name) {
|
|
492
|
+
case 'landing':
|
|
493
|
+
return renderLandingView(state);
|
|
494
|
+
case 'connections':
|
|
495
|
+
return renderConnectionsView(state);
|
|
496
|
+
case 'overview':
|
|
497
|
+
return renderOverviewView(state);
|
|
498
|
+
case 'charts':
|
|
499
|
+
return renderChartsView(state);
|
|
500
|
+
case 'data':
|
|
501
|
+
return renderDataView(state);
|
|
502
|
+
case 'editor':
|
|
503
|
+
return renderEditorView(state, { isResultsRoute: false });
|
|
504
|
+
case 'editorResults':
|
|
505
|
+
return renderEditorView(state, { isResultsRoute: true });
|
|
506
|
+
case 'structure':
|
|
507
|
+
return renderStructureView(state);
|
|
508
|
+
case 'tableDesigner':
|
|
509
|
+
return renderTableDesignerView(state);
|
|
510
|
+
case 'mediaTaggingSetup':
|
|
511
|
+
return renderMediaTaggingView(state, { subView: 'setup' });
|
|
512
|
+
case 'mediaTaggingQueue':
|
|
513
|
+
return renderMediaTaggingView(state, { subView: 'queue' });
|
|
514
|
+
case 'settings':
|
|
515
|
+
return renderSettingsView(state);
|
|
516
|
+
default:
|
|
517
|
+
return renderNotFoundView();
|
|
518
|
+
}
|
|
273
519
|
}
|
|
274
520
|
|
|
275
521
|
function captureFocusedInputState() {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
522
|
+
const activeElement = document.activeElement;
|
|
523
|
+
|
|
524
|
+
if (
|
|
525
|
+
!activeElement ||
|
|
526
|
+
!(
|
|
527
|
+
activeElement instanceof HTMLInputElement ||
|
|
528
|
+
activeElement instanceof HTMLTextAreaElement ||
|
|
529
|
+
activeElement instanceof HTMLSelectElement
|
|
530
|
+
)
|
|
531
|
+
) {
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const { bind } = activeElement.dataset;
|
|
536
|
+
if (!bind) {
|
|
537
|
+
return null;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return {
|
|
541
|
+
bind,
|
|
542
|
+
field: activeElement.dataset.field ?? null,
|
|
543
|
+
columnId: activeElement.dataset.columnId ?? null,
|
|
544
|
+
selectionStart: activeElement.selectionStart,
|
|
545
|
+
selectionEnd: activeElement.selectionEnd,
|
|
546
|
+
selectionDirection: activeElement.selectionDirection,
|
|
547
|
+
scrollTop: activeElement.scrollTop,
|
|
548
|
+
scrollLeft: activeElement.scrollLeft,
|
|
549
|
+
};
|
|
304
550
|
}
|
|
305
551
|
|
|
306
552
|
function buildFocusedInputSelectors(snapshot) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
553
|
+
if (!snapshot?.bind) {
|
|
554
|
+
return [];
|
|
555
|
+
}
|
|
310
556
|
|
|
311
|
-
|
|
312
|
-
|
|
557
|
+
const selectors = [];
|
|
558
|
+
const bindSelector = `[data-bind="${CSS.escape(snapshot.bind)}"]`;
|
|
313
559
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
560
|
+
if (snapshot.columnId && snapshot.field) {
|
|
561
|
+
selectors.push(
|
|
562
|
+
`${bindSelector}[data-column-id="${CSS.escape(snapshot.columnId)}"][data-field="${CSS.escape(snapshot.field)}"]`,
|
|
563
|
+
);
|
|
564
|
+
}
|
|
319
565
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
566
|
+
if (snapshot.field) {
|
|
567
|
+
selectors.push(`${bindSelector}[data-field="${CSS.escape(snapshot.field)}"]`);
|
|
568
|
+
}
|
|
323
569
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
570
|
+
if (snapshot.columnId) {
|
|
571
|
+
selectors.push(`${bindSelector}[data-column-id="${CSS.escape(snapshot.columnId)}"]`);
|
|
572
|
+
}
|
|
327
573
|
|
|
328
|
-
|
|
329
|
-
|
|
574
|
+
selectors.push(bindSelector);
|
|
575
|
+
return selectors;
|
|
330
576
|
}
|
|
331
577
|
|
|
332
578
|
function restoreFocusedInputState(snapshot) {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
const nextElement = buildFocusedInputSelectors(snapshot)
|
|
338
|
-
.map((selector) => document.querySelector(selector))
|
|
339
|
-
.find(
|
|
340
|
-
(candidate) =>
|
|
341
|
-
candidate instanceof HTMLInputElement ||
|
|
342
|
-
candidate instanceof HTMLTextAreaElement ||
|
|
343
|
-
candidate instanceof HTMLSelectElement
|
|
344
|
-
);
|
|
579
|
+
if (!snapshot) {
|
|
580
|
+
return false;
|
|
581
|
+
}
|
|
345
582
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
return false;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
nextElement.focus({ preventScroll: true });
|
|
358
|
-
|
|
359
|
-
if (
|
|
360
|
-
(nextElement instanceof HTMLInputElement || nextElement instanceof HTMLTextAreaElement) &&
|
|
361
|
-
typeof snapshot.selectionStart === "number" &&
|
|
362
|
-
typeof snapshot.selectionEnd === "number"
|
|
363
|
-
) {
|
|
364
|
-
nextElement.setSelectionRange(
|
|
365
|
-
snapshot.selectionStart,
|
|
366
|
-
snapshot.selectionEnd,
|
|
367
|
-
snapshot.selectionDirection || "none"
|
|
368
|
-
);
|
|
369
|
-
}
|
|
583
|
+
const nextElement = buildFocusedInputSelectors(snapshot)
|
|
584
|
+
.map(selector => document.querySelector(selector))
|
|
585
|
+
.find(
|
|
586
|
+
candidate =>
|
|
587
|
+
candidate instanceof HTMLInputElement ||
|
|
588
|
+
candidate instanceof HTMLTextAreaElement ||
|
|
589
|
+
candidate instanceof HTMLSelectElement,
|
|
590
|
+
);
|
|
370
591
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
592
|
+
if (
|
|
593
|
+
!nextElement ||
|
|
594
|
+
!(
|
|
595
|
+
nextElement instanceof HTMLInputElement ||
|
|
596
|
+
nextElement instanceof HTMLTextAreaElement ||
|
|
597
|
+
nextElement instanceof HTMLSelectElement
|
|
598
|
+
)
|
|
599
|
+
) {
|
|
600
|
+
return false;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
nextElement.focus({ preventScroll: true });
|
|
604
|
+
|
|
605
|
+
if (
|
|
606
|
+
(nextElement instanceof HTMLInputElement || nextElement instanceof HTMLTextAreaElement) &&
|
|
607
|
+
typeof snapshot.selectionStart === 'number' &&
|
|
608
|
+
typeof snapshot.selectionEnd === 'number'
|
|
609
|
+
) {
|
|
610
|
+
nextElement.setSelectionRange(
|
|
611
|
+
snapshot.selectionStart,
|
|
612
|
+
snapshot.selectionEnd,
|
|
613
|
+
snapshot.selectionDirection || 'none',
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
nextElement.scrollTop = snapshot.scrollTop;
|
|
618
|
+
nextElement.scrollLeft = snapshot.scrollLeft;
|
|
619
|
+
return true;
|
|
374
620
|
}
|
|
375
621
|
|
|
376
622
|
function focusNewTableDesignerNameField() {
|
|
377
|
-
|
|
378
|
-
'[data-bind="table-designer-field"][data-field="tableName"]'
|
|
379
|
-
);
|
|
623
|
+
const input = document.querySelector('[data-bind="table-designer-field"][data-field="tableName"]');
|
|
380
624
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
625
|
+
if (!(input instanceof HTMLInputElement)) {
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
384
628
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
629
|
+
input.focus({ preventScroll: true });
|
|
630
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
631
|
+
return true;
|
|
388
632
|
}
|
|
389
633
|
|
|
390
634
|
function focusTableDesignerColumnNameField(columnId) {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
635
|
+
if (!columnId) {
|
|
636
|
+
return false;
|
|
637
|
+
}
|
|
394
638
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
639
|
+
const input = document.querySelector(
|
|
640
|
+
`[data-bind="table-designer-column-field"][data-column-id="${CSS.escape(columnId)}"][data-field="name"]`,
|
|
641
|
+
);
|
|
398
642
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
643
|
+
if (!(input instanceof HTMLInputElement)) {
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
402
646
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
647
|
+
input.focus({ preventScroll: true });
|
|
648
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
649
|
+
return true;
|
|
406
650
|
}
|
|
407
651
|
|
|
408
652
|
function focusQueryEditorInput() {
|
|
409
|
-
|
|
653
|
+
const input = document.querySelector('[data-bind="current-query"]');
|
|
410
654
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
655
|
+
if (!(input instanceof HTMLTextAreaElement)) {
|
|
656
|
+
return false;
|
|
657
|
+
}
|
|
414
658
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
659
|
+
input.focus({ preventScroll: true });
|
|
660
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
661
|
+
return true;
|
|
418
662
|
}
|
|
419
663
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
664
|
+
function focusMediaTaggingTagSearchInput() {
|
|
665
|
+
const input = document.querySelector('[data-bind="media-tagging-tag-search"]');
|
|
424
666
|
|
|
425
|
-
|
|
667
|
+
if (!(input instanceof HTMLInputElement)) {
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
426
670
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
671
|
+
input.focus({ preventScroll: true });
|
|
672
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
673
|
+
return true;
|
|
674
|
+
}
|
|
430
675
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
}
|
|
676
|
+
function syncSidebarActiveRoute(routeName) {
|
|
677
|
+
if (!isMediaTaggingRouteName(routeName)) {
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
436
680
|
|
|
437
|
-
|
|
438
|
-
const
|
|
439
|
-
const
|
|
681
|
+
const mediaTaggingLink = shellRefs.sidebar.querySelector('a.sidebar-link[data-group="mediaTagging"]');
|
|
682
|
+
const setupLink = shellRefs.sidebar.querySelector('a.sidebar-sublink[href="#/media-tagging"]');
|
|
683
|
+
const queueLink = shellRefs.sidebar.querySelector('a.sidebar-sublink[href="#/media-tagging/queue"]');
|
|
440
684
|
|
|
441
|
-
if (
|
|
442
|
-
|
|
685
|
+
if (
|
|
686
|
+
!(mediaTaggingLink instanceof HTMLAnchorElement) ||
|
|
687
|
+
!(setupLink instanceof HTMLAnchorElement) ||
|
|
688
|
+
!(queueLink instanceof HTMLAnchorElement)
|
|
689
|
+
) {
|
|
690
|
+
return false;
|
|
443
691
|
}
|
|
444
692
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
693
|
+
mediaTaggingLink.classList.add('is-active');
|
|
694
|
+
setupLink.classList.toggle('is-active', routeName === 'mediaTaggingSetup');
|
|
695
|
+
queueLink.classList.toggle('is-active', routeName === 'mediaTaggingQueue');
|
|
696
|
+
return true;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
async function applyMediaTaggingAndFocusSearch() {
|
|
700
|
+
pendingMediaTaggingTagSearchFocus = true;
|
|
701
|
+
const result = await applyCurrentMediaTaggingSelection();
|
|
702
|
+
|
|
703
|
+
if (!result) {
|
|
704
|
+
pendingMediaTaggingTagSearchFocus = false;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
return result;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
async function handleTableDesignerCsvImport(fileInput) {
|
|
711
|
+
if (!(fileInput instanceof HTMLInputElement)) {
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const file = fileInput.files?.[0];
|
|
716
|
+
|
|
717
|
+
if (!(file instanceof File)) {
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
if (!file.size) {
|
|
722
|
+
showToast('The selected CSV file is empty.', 'alert');
|
|
723
|
+
fileInput.value = '';
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
try {
|
|
728
|
+
const csvText = await readFileAsText(file);
|
|
729
|
+
const imported = queueTableDesignerCsvImport(file.name, csvText);
|
|
730
|
+
|
|
731
|
+
if (!imported) {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
router.navigate('/table-designer/new');
|
|
736
|
+
showToast(
|
|
737
|
+
`Imported ${imported.columnCount} columns and ${imported.importedRowCount} row${
|
|
738
|
+
imported.importedRowCount === 1 ? '' : 's'
|
|
739
|
+
} from ${file.name}.`,
|
|
740
|
+
'success',
|
|
741
|
+
);
|
|
742
|
+
} catch (error) {
|
|
743
|
+
showToast(error?.message || 'CSV import failed.', 'alert');
|
|
744
|
+
} finally {
|
|
745
|
+
fileInput.value = '';
|
|
746
|
+
}
|
|
457
747
|
}
|
|
458
748
|
|
|
459
749
|
function renderApp(state) {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
750
|
+
const previousRoutePath = lastRenderedRoutePath;
|
|
751
|
+
const previousRouteName = lastRenderedRouteName;
|
|
752
|
+
const { main, panel } = resolveView(state);
|
|
753
|
+
const topNavMarkup = renderTopNav(state);
|
|
754
|
+
const sidebarMarkup = renderSidebar(state);
|
|
755
|
+
const statusBarMarkup = renderStatusBar(state);
|
|
756
|
+
const modalMarkup = renderModal(state);
|
|
757
|
+
const toastMarkup = renderToasts(state.toasts);
|
|
758
|
+
const isLockedRoute = [
|
|
759
|
+
'editor',
|
|
760
|
+
'editorResults',
|
|
761
|
+
'data',
|
|
762
|
+
'charts',
|
|
763
|
+
'structure',
|
|
764
|
+
'tableDesigner',
|
|
765
|
+
'mediaTaggingSetup',
|
|
766
|
+
'mediaTaggingQueue',
|
|
767
|
+
].includes(state.route.name);
|
|
768
|
+
const panelOpen = Boolean(panel);
|
|
769
|
+
const topNavChanged = topNavMarkup !== lastRenderedTopNavMarkup;
|
|
770
|
+
const sidebarChanged = sidebarMarkup !== lastRenderedSidebarMarkup;
|
|
771
|
+
const statusBarChanged = statusBarMarkup !== lastRenderedStatusBarMarkup;
|
|
772
|
+
const mainChanged = main !== lastRenderedMainMarkup;
|
|
773
|
+
const panelChanged = panel !== lastRenderedPanelMarkup;
|
|
774
|
+
const modalChanged = modalMarkup !== lastRenderedModalMarkup;
|
|
775
|
+
const panelOpenChanged = panelOpen !== lastRenderedPanelOpen;
|
|
776
|
+
const lockedRouteChanged = isLockedRoute !== lastRenderedLockedRoute;
|
|
777
|
+
const shellMarkupUnchanged =
|
|
778
|
+
state.route.path === lastRenderedRoutePath &&
|
|
779
|
+
!topNavChanged &&
|
|
780
|
+
!sidebarChanged &&
|
|
781
|
+
!statusBarChanged &&
|
|
782
|
+
!mainChanged &&
|
|
783
|
+
!panelChanged &&
|
|
784
|
+
!modalChanged &&
|
|
785
|
+
!panelOpenChanged &&
|
|
786
|
+
!lockedRouteChanged;
|
|
787
|
+
|
|
788
|
+
if (shellMarkupUnchanged && toastMarkup !== lastRenderedToastMarkup) {
|
|
789
|
+
shellRefs.toast.innerHTML = toastMarkup;
|
|
790
|
+
lastRenderedToastMarkup = toastMarkup;
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (shellMarkupUnchanged && toastMarkup === lastRenderedToastMarkup) {
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const focusedInput = captureFocusedInputState();
|
|
799
|
+
const isEnteringNewTableDesignerRoute =
|
|
800
|
+
state.route.name === 'tableDesigner' && state.route.params?.isNew && previousRoutePath !== state.route.path;
|
|
801
|
+
|
|
802
|
+
if (isEnteringNewTableDesignerRoute) {
|
|
803
|
+
pendingNewTableDesignerAutofocus = true;
|
|
804
|
+
} else if (state.route.name !== 'tableDesigner' || !state.route.params?.isNew) {
|
|
805
|
+
pendingNewTableDesignerAutofocus = false;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
if (mainChanged) {
|
|
809
|
+
teardownStructureGraph();
|
|
810
|
+
teardownQueryChartRenderer();
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
if (topNavChanged) {
|
|
814
|
+
shellRefs.topNav.innerHTML = topNavMarkup;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
if (sidebarChanged) {
|
|
818
|
+
const sidebarSynced =
|
|
819
|
+
isMediaTaggingRouteName(previousRouteName) &&
|
|
820
|
+
isMediaTaggingRouteName(state.route.name) &&
|
|
821
|
+
syncSidebarActiveRoute(state.route.name);
|
|
822
|
+
|
|
823
|
+
if (!sidebarSynced) {
|
|
824
|
+
shellRefs.sidebar.innerHTML = sidebarMarkup;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
if (statusBarChanged) {
|
|
829
|
+
shellRefs.statusBar.innerHTML = statusBarMarkup;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
if (mainChanged) {
|
|
833
|
+
shellRefs.view.innerHTML = main;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if (mainChanged || lockedRouteChanged) {
|
|
837
|
+
shellRefs.view.classList.toggle('app-main-scroll--locked', isLockedRoute);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
if (panelChanged) {
|
|
841
|
+
shellRefs.panel.innerHTML = panel;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
if (modalChanged) {
|
|
845
|
+
shellRefs.modal.innerHTML = modalMarkup;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
if (toastMarkup !== lastRenderedToastMarkup) {
|
|
849
|
+
shellRefs.toast.innerHTML = toastMarkup;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
if (panelChanged || panelOpenChanged) {
|
|
853
|
+
shellRefs.shell.classList.toggle('panel-open', panelOpen);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
if (pendingQueryEditorFocus && (state.route.name === 'editor' || state.route.name === 'editorResults')) {
|
|
857
|
+
if (focusQueryEditorInput()) {
|
|
858
|
+
pendingQueryEditorFocus = false;
|
|
859
|
+
}
|
|
860
|
+
} else if (pendingMediaTaggingTagSearchFocus && state.route.name === 'mediaTaggingQueue') {
|
|
861
|
+
if (focusMediaTaggingTagSearchInput()) {
|
|
862
|
+
pendingMediaTaggingTagSearchFocus = false;
|
|
863
|
+
}
|
|
864
|
+
} else if (
|
|
865
|
+
pendingNewTableDesignerAutofocus &&
|
|
866
|
+
state.route.name === 'tableDesigner' &&
|
|
867
|
+
state.route.params?.isNew &&
|
|
868
|
+
state.tableDesigner.draft?.mode === 'create'
|
|
869
|
+
) {
|
|
870
|
+
if (focusNewTableDesignerNameField()) {
|
|
871
|
+
pendingNewTableDesignerAutofocus = false;
|
|
872
|
+
}
|
|
873
|
+
} else {
|
|
874
|
+
restoreFocusedInputState(focusedInput);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
lastRenderedRoutePath = state.route.path;
|
|
878
|
+
lastRenderedRouteName = state.route.name;
|
|
879
|
+
lastRenderedTopNavMarkup = topNavMarkup;
|
|
880
|
+
lastRenderedSidebarMarkup = sidebarMarkup;
|
|
881
|
+
lastRenderedStatusBarMarkup = statusBarMarkup;
|
|
882
|
+
lastRenderedMainMarkup = main;
|
|
883
|
+
lastRenderedPanelMarkup = panel;
|
|
884
|
+
lastRenderedModalMarkup = modalMarkup;
|
|
484
885
|
lastRenderedToastMarkup = toastMarkup;
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
if (shellMarkupUnchanged && toastMarkup === lastRenderedToastMarkup) {
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
const focusedInput = captureFocusedInputState();
|
|
493
|
-
const isEnteringNewTableDesignerRoute =
|
|
494
|
-
state.route.name === "tableDesigner" &&
|
|
495
|
-
state.route.params?.isNew &&
|
|
496
|
-
previousRoutePath !== state.route.path;
|
|
497
|
-
|
|
498
|
-
if (isEnteringNewTableDesignerRoute) {
|
|
499
|
-
pendingNewTableDesignerAutofocus = true;
|
|
500
|
-
} else if (state.route.name !== "tableDesigner" || !state.route.params?.isNew) {
|
|
501
|
-
pendingNewTableDesignerAutofocus = false;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
teardownStructureGraph();
|
|
505
|
-
teardownQueryChartRenderer();
|
|
506
|
-
shellRefs.topNav.innerHTML = topNavMarkup;
|
|
507
|
-
shellRefs.sidebar.innerHTML = sidebarMarkup;
|
|
508
|
-
shellRefs.statusBar.innerHTML = statusBarMarkup;
|
|
509
|
-
shellRefs.view.innerHTML = main;
|
|
510
|
-
shellRefs.view.classList.toggle("app-main-scroll--locked", isLockedRoute);
|
|
511
|
-
shellRefs.panel.innerHTML = panel;
|
|
512
|
-
shellRefs.modal.innerHTML = modalMarkup;
|
|
513
|
-
shellRefs.toast.innerHTML = toastMarkup;
|
|
514
|
-
shellRefs.shell.classList.toggle("panel-open", panelOpen);
|
|
515
|
-
|
|
516
|
-
if (
|
|
517
|
-
pendingQueryEditorFocus &&
|
|
518
|
-
(state.route.name === "editor" || state.route.name === "editorResults")
|
|
519
|
-
) {
|
|
520
|
-
if (focusQueryEditorInput()) {
|
|
521
|
-
pendingQueryEditorFocus = false;
|
|
522
|
-
}
|
|
523
|
-
} else if (
|
|
524
|
-
pendingNewTableDesignerAutofocus &&
|
|
525
|
-
state.route.name === "tableDesigner" &&
|
|
526
|
-
state.route.params?.isNew &&
|
|
527
|
-
state.tableDesigner.draft?.mode === "create"
|
|
528
|
-
) {
|
|
529
|
-
if (focusNewTableDesignerNameField()) {
|
|
530
|
-
pendingNewTableDesignerAutofocus = false;
|
|
531
|
-
}
|
|
532
|
-
} else {
|
|
533
|
-
restoreFocusedInputState(focusedInput);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
lastRenderedRoutePath = state.route.path;
|
|
537
|
-
lastRenderedTopNavMarkup = topNavMarkup;
|
|
538
|
-
lastRenderedSidebarMarkup = sidebarMarkup;
|
|
539
|
-
lastRenderedStatusBarMarkup = statusBarMarkup;
|
|
540
|
-
lastRenderedMainMarkup = main;
|
|
541
|
-
lastRenderedPanelMarkup = panel;
|
|
542
|
-
lastRenderedModalMarkup = modalMarkup;
|
|
543
|
-
lastRenderedToastMarkup = toastMarkup;
|
|
544
|
-
lastRenderedPanelOpen = panelOpen;
|
|
545
|
-
lastRenderedLockedRoute = isLockedRoute;
|
|
546
|
-
|
|
547
|
-
if (state.route.name === "structure") {
|
|
548
|
-
mountStructureGraph(state).catch((error) => {
|
|
549
|
-
console.error("Failed to mount structure graph.", error);
|
|
550
|
-
});
|
|
551
|
-
}
|
|
886
|
+
lastRenderedPanelOpen = panelOpen;
|
|
887
|
+
lastRenderedLockedRoute = isLockedRoute;
|
|
552
888
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
889
|
+
if (state.route.name === 'structure') {
|
|
890
|
+
mountStructureGraph(state).catch(error => {
|
|
891
|
+
console.error('Failed to mount structure graph.', error);
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if (state.route.name === 'charts') {
|
|
896
|
+
mountQueryChartRenderer(state);
|
|
897
|
+
}
|
|
556
898
|
}
|
|
557
899
|
|
|
558
|
-
const router = createRouter(
|
|
559
|
-
|
|
900
|
+
const router = createRouter(route => {
|
|
901
|
+
setRoute(route);
|
|
560
902
|
});
|
|
561
903
|
|
|
562
904
|
async function executeEditorQueryAndNavigate() {
|
|
563
|
-
|
|
564
|
-
|
|
905
|
+
const success = await executeCurrentQuery();
|
|
906
|
+
router.navigate(success ? '/editor/results' : '/editor');
|
|
565
907
|
}
|
|
566
908
|
|
|
567
909
|
async function handleAction(actionNode) {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
if (isActiveConnection) {
|
|
610
|
-
resetStructureGraphForDatabaseChange();
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
const removed = await removeConnection(actionNode.dataset.connectionId);
|
|
614
|
-
if (removed) {
|
|
615
|
-
const nextState = getState();
|
|
616
|
-
if (!nextState.connections.active && nextState.route.name !== "connections") {
|
|
617
|
-
router.navigate("/connections");
|
|
618
|
-
} else {
|
|
619
|
-
await refreshCurrentRoute();
|
|
910
|
+
const { action } = actionNode.dataset;
|
|
911
|
+
|
|
912
|
+
switch (action) {
|
|
913
|
+
case 'navigate':
|
|
914
|
+
router.navigate(actionNode.dataset.to ?? '/');
|
|
915
|
+
return;
|
|
916
|
+
case 'refresh-view':
|
|
917
|
+
await refreshCurrentRoute();
|
|
918
|
+
return;
|
|
919
|
+
case 'open-modal':
|
|
920
|
+
openModal(actionNode.dataset.modal);
|
|
921
|
+
return;
|
|
922
|
+
case 'open-create-query-chart-modal':
|
|
923
|
+
openCreateQueryChartModal();
|
|
924
|
+
return;
|
|
925
|
+
case 'open-edit-query-chart-modal':
|
|
926
|
+
openEditQueryChartModal(actionNode.dataset.chartId);
|
|
927
|
+
return;
|
|
928
|
+
case 'open-delete-query-chart-modal':
|
|
929
|
+
openDeleteQueryChartModal(actionNode.dataset.chartId);
|
|
930
|
+
return;
|
|
931
|
+
case 'open-delete-query-history-modal':
|
|
932
|
+
openDeleteQueryHistoryModal(actionNode.dataset.historyId);
|
|
933
|
+
return;
|
|
934
|
+
case 'edit-connection':
|
|
935
|
+
openEditConnectionModal(actionNode.dataset.connectionId);
|
|
936
|
+
return;
|
|
937
|
+
case 'close-modal':
|
|
938
|
+
closeModal();
|
|
939
|
+
return;
|
|
940
|
+
case 'dismiss-toast':
|
|
941
|
+
dismissToast(actionNode.dataset.toastId);
|
|
942
|
+
return;
|
|
943
|
+
case 'select-connection': {
|
|
944
|
+
resetStructureGraphForDatabaseChange();
|
|
945
|
+
const next = await selectConnection(actionNode.dataset.connectionId);
|
|
946
|
+
if (next) {
|
|
947
|
+
router.navigate('/overview');
|
|
948
|
+
}
|
|
949
|
+
return;
|
|
620
950
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
951
|
+
case 'remove-connection': {
|
|
952
|
+
const isActiveConnection = getState().connections.active?.id === actionNode.dataset.connectionId;
|
|
953
|
+
|
|
954
|
+
if (isActiveConnection) {
|
|
955
|
+
resetStructureGraphForDatabaseChange();
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
const removed = await removeConnection(actionNode.dataset.connectionId);
|
|
959
|
+
if (removed) {
|
|
960
|
+
const nextState = getState();
|
|
961
|
+
if (!nextState.connections.active && nextState.route.name !== 'connections') {
|
|
962
|
+
router.navigate('/connections');
|
|
963
|
+
} else {
|
|
964
|
+
await refreshCurrentRoute();
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
case 'create-backup':
|
|
970
|
+
await createActiveConnectionBackup();
|
|
971
|
+
return;
|
|
972
|
+
case 'copy-media-tags': {
|
|
973
|
+
const tags = getState().mediaTagging.tags ?? [];
|
|
974
|
+
const formattedTags = tags
|
|
975
|
+
.map(tag => `${String(tag.label ?? '').trim()}${tag.isParentTag ? ' (parent)' : ''}`)
|
|
976
|
+
.filter(Boolean)
|
|
977
|
+
.join(', ');
|
|
978
|
+
|
|
979
|
+
if (!formattedTags) {
|
|
980
|
+
showToast('No tags available to copy.', 'alert');
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
try {
|
|
985
|
+
await navigator.clipboard.writeText(formattedTags);
|
|
986
|
+
showToast('Tags copied.', 'success');
|
|
987
|
+
} catch (error) {
|
|
988
|
+
showToast('Tags could not be copied.', 'alert');
|
|
989
|
+
}
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
case 'open-overview-in-finder':
|
|
993
|
+
await openOverviewInFinder();
|
|
994
|
+
return;
|
|
995
|
+
case 'execute-query': {
|
|
996
|
+
await executeEditorQueryAndNavigate();
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
case 'delete-data-row':
|
|
1000
|
+
openDeleteDataRowModal(actionNode.dataset.rowIndex);
|
|
1001
|
+
return;
|
|
1002
|
+
case 'delete-editor-row':
|
|
1003
|
+
openDeleteEditorRowModal(actionNode.dataset.rowIndex);
|
|
1004
|
+
return;
|
|
1005
|
+
case 'clear-query':
|
|
1006
|
+
pendingQueryEditorFocus = true;
|
|
1007
|
+
clearCurrentQuery();
|
|
1008
|
+
if (getState().route.name === 'editorResults') {
|
|
1009
|
+
router.navigate('/editor');
|
|
1010
|
+
}
|
|
1011
|
+
return;
|
|
1012
|
+
case 'clear-results':
|
|
1013
|
+
clearEditorResults();
|
|
1014
|
+
router.navigate('/editor');
|
|
1015
|
+
return;
|
|
1016
|
+
case 'select-query-history-item':
|
|
1017
|
+
if (actionNode.dataset.historyId) {
|
|
1018
|
+
const pendingSelection = selectQueryHistoryItem(actionNode.dataset.historyId, { notify: false });
|
|
1019
|
+
syncQueryHistorySelectionUi(actionNode.dataset.historyId);
|
|
1020
|
+
syncQueryHistoryUi(actionNode.dataset.historyId);
|
|
1021
|
+
await pendingSelection;
|
|
1022
|
+
syncQueryHistoryUi(actionNode.dataset.historyId);
|
|
1023
|
+
}
|
|
1024
|
+
return;
|
|
1025
|
+
case 'clear-query-history-selection':
|
|
1026
|
+
clearQueryHistorySelection({ notify: false });
|
|
1027
|
+
syncQueryHistorySelectionUi(null);
|
|
1028
|
+
return;
|
|
1029
|
+
case 'set-query-history-tab':
|
|
1030
|
+
if (actionNode.dataset.tab) {
|
|
1031
|
+
await setQueryHistoryTab(actionNode.dataset.tab);
|
|
1032
|
+
}
|
|
1033
|
+
return;
|
|
1034
|
+
case 'toggle-query-history-panel':
|
|
1035
|
+
setQueryHistoryPanelVisibility(
|
|
1036
|
+
actionNode.dataset.nextValue ? actionNode.dataset.nextValue === 'true' : undefined,
|
|
1037
|
+
);
|
|
1038
|
+
return;
|
|
1039
|
+
case 'toggle-editor-panel':
|
|
1040
|
+
setEditorPanelVisibility(
|
|
1041
|
+
actionNode.dataset.nextValue ? actionNode.dataset.nextValue === 'true' : undefined,
|
|
1042
|
+
);
|
|
1043
|
+
return;
|
|
1044
|
+
case 'load-more-query-history':
|
|
1045
|
+
await loadMoreQueryHistory();
|
|
1046
|
+
return;
|
|
1047
|
+
case 'open-query-history':
|
|
1048
|
+
if (actionNode.dataset.historyId && openQueryHistoryInEditor(actionNode.dataset.historyId)) {
|
|
1049
|
+
router.navigate('/editor');
|
|
1050
|
+
}
|
|
1051
|
+
return;
|
|
1052
|
+
case 'run-query-history':
|
|
1053
|
+
if (actionNode.dataset.historyId) {
|
|
1054
|
+
const success = await runQueryHistoryItem(actionNode.dataset.historyId);
|
|
1055
|
+
router.navigate(success ? '/editor/results' : '/editor');
|
|
1056
|
+
}
|
|
1057
|
+
return;
|
|
1058
|
+
case 'toggle-query-history-saved':
|
|
1059
|
+
if (actionNode.dataset.historyId) {
|
|
1060
|
+
await toggleQueryHistorySavedState(
|
|
1061
|
+
actionNode.dataset.historyId,
|
|
1062
|
+
actionNode.dataset.nextValue === 'true',
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
1065
|
+
return;
|
|
1066
|
+
case 'toggle-charts-sql-panel':
|
|
1067
|
+
toggleChartsSqlPanel();
|
|
1068
|
+
return;
|
|
1069
|
+
case 'toggle-charts-results-panel':
|
|
1070
|
+
toggleChartsResultsPanel();
|
|
1071
|
+
return;
|
|
1072
|
+
case 'set-charts-height-preset':
|
|
1073
|
+
if (actionNode.dataset.preset) {
|
|
1074
|
+
setChartsHeightPreset(actionNode.dataset.preset);
|
|
1075
|
+
}
|
|
1076
|
+
return;
|
|
1077
|
+
case 'export-query-chart-png':
|
|
1078
|
+
if (actionNode.dataset.chartId && !exportQueryChartAsPng(actionNode.dataset.chartId)) {
|
|
1079
|
+
showToast('The selected chart is not ready for PNG export.', 'alert');
|
|
1080
|
+
}
|
|
1081
|
+
return;
|
|
1082
|
+
case 'set-editor-tab': {
|
|
1083
|
+
const tab = actionNode.dataset.tab;
|
|
1084
|
+
if (!tab) {
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
setEditorTab(tab);
|
|
1088
|
+
router.navigate(tab === 'results' ? '/editor/results' : '/editor');
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
case 'export-query-csv':
|
|
1092
|
+
await exportCurrentQueryCsv();
|
|
1093
|
+
return;
|
|
1094
|
+
case 'export-data-csv':
|
|
1095
|
+
await exportCurrentDataTableCsv();
|
|
1096
|
+
return;
|
|
1097
|
+
case 'toggle-data-tables':
|
|
1098
|
+
toggleDataTablesPanel();
|
|
1099
|
+
return;
|
|
1100
|
+
case 'toggle-structure-tables':
|
|
1101
|
+
toggleStructureTablesPanel();
|
|
1102
|
+
return;
|
|
1103
|
+
case 'select-structure-entry':
|
|
1104
|
+
if (actionNode.dataset.entryName) {
|
|
1105
|
+
await selectStructureEntry(actionNode.dataset.entryName);
|
|
1106
|
+
}
|
|
1107
|
+
return;
|
|
1108
|
+
case 'add-table-designer-column':
|
|
1109
|
+
{
|
|
1110
|
+
const columnId = addCurrentTableDesignerColumn();
|
|
1111
|
+
if (columnId) {
|
|
1112
|
+
window.requestAnimationFrame(() => {
|
|
1113
|
+
focusTableDesignerColumnNameField(columnId);
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
return;
|
|
1118
|
+
case 'remove-table-designer-column':
|
|
1119
|
+
if (actionNode.dataset.columnId) {
|
|
1120
|
+
removeCurrentTableDesignerColumn(actionNode.dataset.columnId);
|
|
1121
|
+
}
|
|
1122
|
+
return;
|
|
1123
|
+
case 'save-table-designer': {
|
|
1124
|
+
const savedTableName = await saveCurrentTableDesignerDraft();
|
|
1125
|
+
if (savedTableName) {
|
|
1126
|
+
router.navigate(`/table-designer/${encodeURIComponent(savedTableName)}`);
|
|
1127
|
+
}
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
case 'copy-table-designer-sql': {
|
|
1131
|
+
const sqlPreview = getState().tableDesigner.draft?.sqlPreview ?? '';
|
|
1132
|
+
if (!sqlPreview.trim()) {
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
try {
|
|
1137
|
+
await navigator.clipboard.writeText(sqlPreview);
|
|
1138
|
+
showToast('SQL preview copied.', 'success');
|
|
1139
|
+
} catch (error) {
|
|
1140
|
+
showToast('Clipboard access failed.', 'alert');
|
|
1141
|
+
}
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
case 'toggle-table-designer-sql-preview':
|
|
1145
|
+
setTableDesignerSqlPreviewVisibility(
|
|
1146
|
+
actionNode.dataset.nextValue ? actionNode.dataset.nextValue === 'true' : undefined,
|
|
1147
|
+
);
|
|
1148
|
+
return;
|
|
1149
|
+
case 'refresh-media-tagging-preview':
|
|
1150
|
+
await refreshMediaTaggingPreview();
|
|
1151
|
+
return;
|
|
1152
|
+
case 'dismiss-media-tagging-issue':
|
|
1153
|
+
dismissMediaTaggingIssue(actionNode.dataset.issueKey);
|
|
1154
|
+
return;
|
|
1155
|
+
case 'reset-media-tagging-queries':
|
|
1156
|
+
await resetMediaTaggingQueriesToDefault();
|
|
1157
|
+
return;
|
|
1158
|
+
case 'save-media-tagging':
|
|
1159
|
+
await saveCurrentMediaTaggingConfig();
|
|
1160
|
+
return;
|
|
1161
|
+
case 'create-media-tag':
|
|
1162
|
+
await createCurrentMediaTag();
|
|
1163
|
+
// Refocus the name field after creating a tag
|
|
1164
|
+
const nameInput = document.querySelector('[data-bind="media-tagging-tag-form-field"][data-field="name"]');
|
|
1165
|
+
if (nameInput instanceof HTMLInputElement) {
|
|
1166
|
+
nameInput.focus();
|
|
1167
|
+
}
|
|
1168
|
+
return;
|
|
1169
|
+
case 'remove-media-tag':
|
|
1170
|
+
await removeCurrentMediaTag(actionNode.dataset.tagKey);
|
|
1171
|
+
return;
|
|
1172
|
+
case 'skip-media-tagging-item':
|
|
1173
|
+
await skipCurrentMediaTaggingItem();
|
|
1174
|
+
return;
|
|
1175
|
+
case 'reset-skipped-media-tagging':
|
|
1176
|
+
await resetSkippedMediaTaggingItems();
|
|
1177
|
+
return;
|
|
1178
|
+
case 'apply-media-tagging':
|
|
1179
|
+
await applyMediaTaggingAndFocusSearch();
|
|
1180
|
+
return;
|
|
1181
|
+
case 'toggle-media-tagging-current-media':
|
|
1182
|
+
if (actionNode instanceof HTMLButtonElement) {
|
|
1183
|
+
const nextValue = actionNode.dataset.nextValue !== 'false';
|
|
1184
|
+
syncMediaTaggingCurrentMediaUi(actionNode, nextValue);
|
|
1185
|
+
setMediaTaggingWorkflowMediaDetailsVisible(nextValue, { notify: false });
|
|
1186
|
+
}
|
|
1187
|
+
return;
|
|
1188
|
+
case 'rotate-media-tagging-current-media': {
|
|
1189
|
+
const currentRotation = getState().mediaTagging.workflowMediaRotationDegrees ?? 0;
|
|
1190
|
+
const command = actionNode.dataset.rotationCommand;
|
|
1191
|
+
const nextRotation =
|
|
1192
|
+
command === 'left'
|
|
1193
|
+
? currentRotation - 90
|
|
1194
|
+
: command === 'right'
|
|
1195
|
+
? currentRotation + 90
|
|
1196
|
+
: 0;
|
|
1197
|
+
|
|
1198
|
+
syncMediaTaggingMediaRotationUi(actionNode, nextRotation);
|
|
1199
|
+
setMediaTaggingWorkflowMediaRotationDegrees(nextRotation, { notify: false });
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
case 'open-media-tagging-current-in-data': {
|
|
1203
|
+
const currentState = getState();
|
|
1204
|
+
const mediaTableName = currentState.mediaTagging.draft?.mediaTable ?? '';
|
|
1205
|
+
const identity = currentState.mediaTagging.workflow?.currentItem?.identity ?? null;
|
|
1206
|
+
|
|
1207
|
+
if (!openDataRowByIdentity(mediaTableName, identity)) {
|
|
1208
|
+
showToast('The current media row could not be opened in Data.', 'alert');
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
router.navigate(`/data/${encodeURIComponent(mediaTableName)}`);
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
case 'open-media-tagging-current-in-structure': {
|
|
1216
|
+
const mediaTableName = String(getState().mediaTagging.draft?.mediaTable ?? '').trim();
|
|
1217
|
+
|
|
1218
|
+
if (!mediaTableName) {
|
|
1219
|
+
showToast('The current media table could not be opened in Structure.', 'alert');
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
router.navigate(`/structure/${encodeURIComponent(mediaTableName)}`);
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
case 'import-table-designer-csv': {
|
|
1227
|
+
const fileInput = document.querySelector('[data-bind="table-designer-import-file"]');
|
|
1228
|
+
|
|
1229
|
+
if (!(fileInput instanceof HTMLInputElement)) {
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
fileInput.value = '';
|
|
1234
|
+
fileInput.click();
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
case 'select-data-row':
|
|
1238
|
+
if (actionNode.dataset.rowIndex) {
|
|
1239
|
+
selectDataRow(actionNode.dataset.rowIndex, { notify: false });
|
|
1240
|
+
syncDataRowSelectionUi(actionNode.dataset.rowIndex);
|
|
1241
|
+
}
|
|
1242
|
+
return;
|
|
1243
|
+
case 'select-editor-row':
|
|
1244
|
+
if (actionNode.dataset.rowIndex) {
|
|
1245
|
+
selectEditorRow(actionNode.dataset.rowIndex);
|
|
1246
|
+
}
|
|
1247
|
+
return;
|
|
1248
|
+
case 'clear-data-row-selection':
|
|
1249
|
+
clearDataRowSelection({ notify: false });
|
|
1250
|
+
syncDataRowSelectionUi(null);
|
|
1251
|
+
return;
|
|
1252
|
+
case 'clear-editor-row-selection':
|
|
1253
|
+
clearEditorRowSelection();
|
|
1254
|
+
return;
|
|
1255
|
+
case 'set-data-page':
|
|
1256
|
+
if (actionNode.dataset.page) {
|
|
1257
|
+
await setDataPage(actionNode.dataset.page);
|
|
1258
|
+
}
|
|
1259
|
+
return;
|
|
1260
|
+
case 'sort-data-column':
|
|
1261
|
+
if (actionNode.dataset.columnName) {
|
|
1262
|
+
await sortDataTableByColumn(actionNode.dataset.columnName);
|
|
1263
|
+
}
|
|
1264
|
+
return;
|
|
1265
|
+
case 'sort-editor-results-column':
|
|
1266
|
+
if (actionNode.dataset.columnName) {
|
|
1267
|
+
sortEditorResultsByColumn(actionNode.dataset.columnName);
|
|
1268
|
+
}
|
|
1269
|
+
return;
|
|
1270
|
+
case 'set-data-page-size':
|
|
1271
|
+
if (actionNode.dataset.pageSize) {
|
|
1272
|
+
await setDataPageSize(actionNode.dataset.pageSize);
|
|
1273
|
+
}
|
|
1274
|
+
return;
|
|
1275
|
+
case 'reload-data-route':
|
|
1276
|
+
await refreshCurrentRoute();
|
|
1277
|
+
return;
|
|
1278
|
+
default:
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
function isMediaTaggingMenuActive(routeName) {
|
|
1283
|
+
return routeName === 'mediaTaggingSetup' || routeName === 'mediaTaggingQueue';
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
function canApplyMediaTaggingShortcut(state) {
|
|
1287
|
+
return (
|
|
1288
|
+
isMediaTaggingMenuActive(state.route.name) &&
|
|
1289
|
+
Boolean(state.mediaTagging.workflow?.currentItem) &&
|
|
1290
|
+
!state.mediaTagging.applying &&
|
|
1291
|
+
(state.mediaTagging.selectedTagKeys?.length ?? 0) > 0
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
document.addEventListener('click', event => {
|
|
1296
|
+
const actionNode = event.target.closest('[data-action]');
|
|
1297
|
+
|
|
1298
|
+
if (!actionNode) {
|
|
723
1299
|
return;
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
const columnId = addCurrentTableDesignerColumn();
|
|
749
|
-
if (columnId) {
|
|
750
|
-
window.requestAnimationFrame(() => {
|
|
751
|
-
focusTableDesignerColumnNameField(columnId);
|
|
752
|
-
});
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
handleAction(actionNode);
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
document.addEventListener('keydown', event => {
|
|
1306
|
+
const target = event.target;
|
|
1307
|
+
const state = getState();
|
|
1308
|
+
|
|
1309
|
+
// Handle Enter key in tag form fields to trigger create tag
|
|
1310
|
+
if (
|
|
1311
|
+
event.key === 'Enter' &&
|
|
1312
|
+
!event.shiftKey &&
|
|
1313
|
+
!event.altKey &&
|
|
1314
|
+
!event.ctrlKey &&
|
|
1315
|
+
!event.metaKey &&
|
|
1316
|
+
!event.defaultPrevented &&
|
|
1317
|
+
target instanceof HTMLElement &&
|
|
1318
|
+
target.dataset.bind === 'media-tagging-tag-form-field'
|
|
1319
|
+
) {
|
|
1320
|
+
event.preventDefault();
|
|
1321
|
+
const createButton = document.querySelector('[data-action="create-media-tag"]');
|
|
1322
|
+
if (createButton && !(createButton instanceof HTMLButtonElement && createButton.disabled)) {
|
|
1323
|
+
createButton?.click();
|
|
753
1324
|
}
|
|
754
|
-
}
|
|
755
|
-
return;
|
|
756
|
-
case "remove-table-designer-column":
|
|
757
|
-
if (actionNode.dataset.columnId) {
|
|
758
|
-
removeCurrentTableDesignerColumn(actionNode.dataset.columnId);
|
|
759
|
-
}
|
|
760
|
-
return;
|
|
761
|
-
case "save-table-designer": {
|
|
762
|
-
const savedTableName = await saveCurrentTableDesignerDraft();
|
|
763
|
-
if (savedTableName) {
|
|
764
|
-
router.navigate(`/table-designer/${encodeURIComponent(savedTableName)}`);
|
|
765
|
-
}
|
|
766
|
-
return;
|
|
767
|
-
}
|
|
768
|
-
case "copy-table-designer-sql": {
|
|
769
|
-
const sqlPreview = getState().tableDesigner.draft?.sqlPreview ?? "";
|
|
770
|
-
if (!sqlPreview.trim()) {
|
|
771
1325
|
return;
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
);
|
|
786
|
-
return;
|
|
787
|
-
case "import-table-designer-csv": {
|
|
788
|
-
const fileInput = document.querySelector('[data-bind="table-designer-import-file"]');
|
|
789
|
-
|
|
790
|
-
if (!(fileInput instanceof HTMLInputElement)) {
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
if (
|
|
1329
|
+
(event.key === 'Enter' || event.code === 'Enter' || event.code === 'NumpadEnter') &&
|
|
1330
|
+
event.shiftKey &&
|
|
1331
|
+
!event.altKey &&
|
|
1332
|
+
!event.ctrlKey &&
|
|
1333
|
+
!event.metaKey &&
|
|
1334
|
+
!event.defaultPrevented &&
|
|
1335
|
+
canApplyMediaTaggingShortcut(state)
|
|
1336
|
+
) {
|
|
1337
|
+
event.preventDefault();
|
|
1338
|
+
void applyMediaTaggingAndFocusSearch();
|
|
791
1339
|
return;
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
await setDataPage(actionNode.dataset.page);
|
|
817
|
-
}
|
|
818
|
-
return;
|
|
819
|
-
case "sort-data-column":
|
|
820
|
-
if (actionNode.dataset.columnName) {
|
|
821
|
-
await sortDataTableByColumn(actionNode.dataset.columnName);
|
|
822
|
-
}
|
|
823
|
-
return;
|
|
824
|
-
case "sort-editor-results-column":
|
|
825
|
-
if (actionNode.dataset.columnName) {
|
|
826
|
-
sortEditorResultsByColumn(actionNode.dataset.columnName);
|
|
827
|
-
}
|
|
828
|
-
return;
|
|
829
|
-
case "set-data-page-size":
|
|
830
|
-
if (actionNode.dataset.pageSize) {
|
|
831
|
-
await setDataPageSize(actionNode.dataset.pageSize);
|
|
832
|
-
}
|
|
833
|
-
return;
|
|
834
|
-
case "reload-data-route":
|
|
835
|
-
await refreshCurrentRoute();
|
|
836
|
-
return;
|
|
837
|
-
default:
|
|
838
|
-
}
|
|
839
|
-
}
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
if (
|
|
1343
|
+
event.key === 'Enter' &&
|
|
1344
|
+
event.shiftKey &&
|
|
1345
|
+
!event.altKey &&
|
|
1346
|
+
!event.ctrlKey &&
|
|
1347
|
+
!event.metaKey &&
|
|
1348
|
+
!event.defaultPrevented &&
|
|
1349
|
+
target instanceof HTMLTextAreaElement &&
|
|
1350
|
+
target.dataset.bind === 'current-query'
|
|
1351
|
+
) {
|
|
1352
|
+
event.preventDefault();
|
|
1353
|
+
|
|
1354
|
+
if (!getState().editor.executing) {
|
|
1355
|
+
void executeEditorQueryAndNavigate();
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
if (event.key !== 'Escape' || event.defaultPrevented) {
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
840
1364
|
|
|
841
|
-
|
|
842
|
-
|
|
1365
|
+
if (state.modal) {
|
|
1366
|
+
event.preventDefault();
|
|
1367
|
+
closeModal();
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
843
1370
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
1371
|
+
if (
|
|
1372
|
+
state.route.name === 'data' &&
|
|
1373
|
+
(typeof state.dataBrowser.selectedRowIndex === 'number' || Boolean(state.dataBrowser.selectedRow))
|
|
1374
|
+
) {
|
|
1375
|
+
event.preventDefault();
|
|
1376
|
+
clearDataRowSelection();
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
847
1379
|
|
|
848
|
-
|
|
1380
|
+
if (state.route.name === 'editorResults' && typeof state.editor.selectedRowIndex === 'number') {
|
|
1381
|
+
event.preventDefault();
|
|
1382
|
+
clearEditorRowSelection();
|
|
1383
|
+
}
|
|
849
1384
|
});
|
|
850
1385
|
|
|
851
|
-
document.addEventListener(
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
!event.altKey &&
|
|
858
|
-
!event.ctrlKey &&
|
|
859
|
-
!event.metaKey &&
|
|
860
|
-
!event.defaultPrevented &&
|
|
861
|
-
target instanceof HTMLTextAreaElement &&
|
|
862
|
-
target.dataset.bind === "current-query"
|
|
863
|
-
) {
|
|
864
|
-
event.preventDefault();
|
|
1386
|
+
document.addEventListener('input', event => {
|
|
1387
|
+
const bindNode = event.target.closest('[data-bind]');
|
|
1388
|
+
|
|
1389
|
+
if (!bindNode) {
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
865
1392
|
|
|
866
|
-
if (
|
|
867
|
-
|
|
1393
|
+
if (bindNode.dataset.bind === 'current-query') {
|
|
1394
|
+
invalidateMainRenderCache();
|
|
1395
|
+
syncQueryEditorHighlight(bindNode);
|
|
1396
|
+
syncQueryEditorScroll(bindNode);
|
|
1397
|
+
setCurrentQuery(bindNode.value);
|
|
1398
|
+
return;
|
|
868
1399
|
}
|
|
869
1400
|
|
|
870
|
-
|
|
871
|
-
|
|
1401
|
+
if (bindNode.dataset.bind === 'data-search-query') {
|
|
1402
|
+
setDataSearchQuery(bindNode.value);
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
872
1405
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
1406
|
+
if (bindNode.dataset.bind === 'table-designer-search') {
|
|
1407
|
+
setTableDesignerSearchQuery(bindNode.value);
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
876
1410
|
|
|
877
|
-
|
|
1411
|
+
if (bindNode.dataset.bind === 'media-tagging-tag-search') {
|
|
1412
|
+
syncMediaTaggingTagSearchUi(bindNode);
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
878
1415
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
}
|
|
1416
|
+
if (bindNode.dataset.bind === 'table-designer-field') {
|
|
1417
|
+
if (bindNode instanceof HTMLInputElement && bindNode.type === 'checkbox') {
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
884
1420
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
return;
|
|
889
|
-
}
|
|
1421
|
+
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.value);
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
890
1424
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
});
|
|
1425
|
+
if (bindNode.dataset.bind === 'table-designer-column-field') {
|
|
1426
|
+
updateCurrentTableDesignerColumnField(bindNode.dataset.columnId, bindNode.dataset.field, bindNode.value);
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
896
1429
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
syncQueryEditorHighlight(bindNode);
|
|
907
|
-
syncQueryEditorScroll(bindNode);
|
|
908
|
-
setCurrentQuery(bindNode.value);
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
if (bindNode.dataset.bind === "data-search-query") {
|
|
913
|
-
setDataSearchQuery(bindNode.value);
|
|
914
|
-
return;
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
if (bindNode.dataset.bind === "table-designer-search") {
|
|
918
|
-
setTableDesignerSearchQuery(bindNode.value);
|
|
919
|
-
return;
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
if (bindNode.dataset.bind === "table-designer-field") {
|
|
923
|
-
if (bindNode instanceof HTMLInputElement && bindNode.type === "checkbox") {
|
|
924
|
-
return;
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.value);
|
|
928
|
-
return;
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
if (bindNode.dataset.bind === "table-designer-column-field") {
|
|
932
|
-
updateCurrentTableDesignerColumnField(
|
|
933
|
-
bindNode.dataset.columnId,
|
|
934
|
-
bindNode.dataset.field,
|
|
935
|
-
bindNode.value
|
|
936
|
-
);
|
|
937
|
-
return;
|
|
938
|
-
}
|
|
1430
|
+
if (bindNode.dataset.bind === 'media-tagging-field') {
|
|
1431
|
+
if (bindNode instanceof HTMLInputElement && bindNode.type === 'checkbox') {
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
// Skip select elements - they're handled in the change event with preview refresh
|
|
1436
|
+
if (bindNode instanceof HTMLSelectElement) {
|
|
1437
|
+
return;
|
|
1438
|
+
}
|
|
939
1439
|
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1440
|
+
if (bindNode instanceof HTMLTextAreaElement && bindNode.dataset.sqlHighlight === 'true') {
|
|
1441
|
+
syncQueryEditorHighlight(bindNode);
|
|
1442
|
+
syncQueryEditorScroll(bindNode);
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
updateCurrentMediaTaggingField(bindNode.dataset.field, bindNode.value);
|
|
1446
|
+
return;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
if (bindNode.dataset.bind === 'media-tagging-tag-form-field') {
|
|
1450
|
+
if (bindNode instanceof HTMLInputElement && bindNode.type === 'checkbox') {
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
944
1453
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
1454
|
+
updateCurrentMediaTaggingTagFormField(bindNode.dataset.field, bindNode.value);
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
if (bindNode.dataset.bind === 'query-history-search') {
|
|
1459
|
+
setQueryHistorySearchInput(bindNode.value);
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
if (bindNode.dataset.bind === 'query-chart-draft:name') {
|
|
1464
|
+
updateCurrentQueryChartDraftField('name', bindNode.value);
|
|
1465
|
+
}
|
|
948
1466
|
});
|
|
949
1467
|
|
|
950
1468
|
document.addEventListener(
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
1469
|
+
'scroll',
|
|
1470
|
+
event => {
|
|
1471
|
+
const target = event.target;
|
|
954
1472
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1473
|
+
if (!(target instanceof HTMLTextAreaElement)) {
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
958
1476
|
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1477
|
+
if (target.dataset.bind !== 'current-query' && target.dataset.sqlHighlight !== 'true') {
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
syncQueryEditorScroll(target);
|
|
1482
|
+
},
|
|
1483
|
+
true,
|
|
962
1484
|
);
|
|
963
1485
|
|
|
964
|
-
document.addEventListener(
|
|
965
|
-
|
|
1486
|
+
document.addEventListener('change', event => {
|
|
1487
|
+
const bindNode = event.target.closest('[data-bind]');
|
|
966
1488
|
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1489
|
+
if (!bindNode) {
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
970
1492
|
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1493
|
+
if (bindNode.dataset.bind === 'data-search-column') {
|
|
1494
|
+
setDataSearchColumn(bindNode.value);
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
975
1497
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1498
|
+
if (bindNode.dataset.bind === 'media-tagging-field') {
|
|
1499
|
+
updateCurrentMediaTaggingField(bindNode.dataset.field, bindNode.value);
|
|
1500
|
+
void refreshMediaTaggingPreview();
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
980
1503
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1504
|
+
if (bindNode.dataset.bind === 'media-tagging-tag-form-field') {
|
|
1505
|
+
const nextValue =
|
|
1506
|
+
bindNode instanceof HTMLInputElement && bindNode.type === 'checkbox' ? bindNode.checked : bindNode.value;
|
|
1507
|
+
updateCurrentMediaTaggingTagFormField(bindNode.dataset.field, nextValue);
|
|
1508
|
+
return;
|
|
985
1509
|
}
|
|
986
1510
|
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1511
|
+
if (bindNode.dataset.bind === 'media-tagging-tag-selection') {
|
|
1512
|
+
const nextValue =
|
|
1513
|
+
bindNode instanceof HTMLInputElement && bindNode.type === 'checkbox' ? bindNode.checked : false;
|
|
1514
|
+
toggleCurrentMediaTagSelection(bindNode.dataset.tagKey, nextValue, { notify: false });
|
|
1515
|
+
syncMediaTagSelectionUi(bindNode, nextValue);
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
990
1518
|
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
bindNode.value
|
|
996
|
-
);
|
|
997
|
-
return;
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
if (bindNode.dataset.bind === "table-designer-column-flag") {
|
|
1001
|
-
updateCurrentTableDesignerColumnField(
|
|
1002
|
-
bindNode.dataset.columnId,
|
|
1003
|
-
bindNode.dataset.field,
|
|
1004
|
-
bindNode.checked
|
|
1005
|
-
);
|
|
1006
|
-
return;
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
if (bindNode.dataset.bind === "query-chart-draft:chartType") {
|
|
1010
|
-
updateCurrentQueryChartDraftField("chartType", bindNode.value);
|
|
1011
|
-
return;
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
if (bindNode.dataset.bind === "query-chart-draft:tableVisible") {
|
|
1015
|
-
updateCurrentQueryChartDraftField("tableVisible", bindNode.checked);
|
|
1016
|
-
return;
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
if (bindNode.dataset.bind?.startsWith("query-chart-draft-config:")) {
|
|
1020
|
-
const field = bindNode.dataset.bind.slice("query-chart-draft-config:".length);
|
|
1021
|
-
const value =
|
|
1022
|
-
bindNode instanceof HTMLInputElement && bindNode.type === "checkbox"
|
|
1023
|
-
? bindNode.checked
|
|
1024
|
-
: bindNode.value === ""
|
|
1025
|
-
? null
|
|
1026
|
-
: bindNode.value;
|
|
1027
|
-
updateCurrentQueryChartDraftConfigField(field, value);
|
|
1028
|
-
}
|
|
1029
|
-
});
|
|
1519
|
+
if (bindNode.dataset.bind === 'table-designer-import-file') {
|
|
1520
|
+
void handleTableDesignerCsvImport(bindNode);
|
|
1521
|
+
return;
|
|
1522
|
+
}
|
|
1030
1523
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
return;
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
event.preventDefault();
|
|
1039
|
-
const formData = new FormData(form);
|
|
1040
|
-
|
|
1041
|
-
switch (form.dataset.form) {
|
|
1042
|
-
case "open-connection": {
|
|
1043
|
-
resetStructureGraphForDatabaseChange();
|
|
1044
|
-
const connection = await submitOpenConnection({
|
|
1045
|
-
path: String(formData.get("path") ?? ""),
|
|
1046
|
-
label: String(formData.get("label") ?? ""),
|
|
1047
|
-
readOnly: formData.get("readOnly") === "on",
|
|
1048
|
-
});
|
|
1049
|
-
|
|
1050
|
-
if (connection) {
|
|
1051
|
-
router.navigate("/overview");
|
|
1052
|
-
}
|
|
1053
|
-
return;
|
|
1054
|
-
}
|
|
1055
|
-
case "create-connection": {
|
|
1056
|
-
resetStructureGraphForDatabaseChange();
|
|
1057
|
-
const connection = await submitCreateConnection({
|
|
1058
|
-
path: String(formData.get("path") ?? ""),
|
|
1059
|
-
label: String(formData.get("label") ?? ""),
|
|
1060
|
-
});
|
|
1061
|
-
|
|
1062
|
-
if (connection) {
|
|
1063
|
-
router.navigate("/overview");
|
|
1064
|
-
}
|
|
1065
|
-
return;
|
|
1066
|
-
}
|
|
1067
|
-
case "import-sql": {
|
|
1068
|
-
resetStructureGraphForDatabaseChange();
|
|
1069
|
-
const targetMode = String(formData.get("targetMode") ?? "active");
|
|
1070
|
-
const payload = {
|
|
1071
|
-
sqlFilePath: String(formData.get("sqlFilePath") ?? ""),
|
|
1072
|
-
label: String(formData.get("label") ?? ""),
|
|
1073
|
-
};
|
|
1074
|
-
|
|
1075
|
-
if (targetMode === "recent") {
|
|
1076
|
-
payload.targetConnectionId = String(formData.get("targetConnectionId") ?? "");
|
|
1077
|
-
} else if (targetMode === "create") {
|
|
1078
|
-
payload.createNew = true;
|
|
1079
|
-
payload.targetPath = String(formData.get("targetPath") ?? "");
|
|
1080
|
-
} else if (targetMode === "path") {
|
|
1081
|
-
payload.targetPath = String(formData.get("targetPath") ?? "");
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
const result = await submitImportSql(payload);
|
|
1085
|
-
if (result) {
|
|
1086
|
-
router.navigate("/overview");
|
|
1087
|
-
}
|
|
1088
|
-
return;
|
|
1089
|
-
}
|
|
1090
|
-
case "edit-connection": {
|
|
1091
|
-
const connectionId = String(formData.get("connectionId") ?? "");
|
|
1092
|
-
const isActiveConnection = getState().connections.active?.id === connectionId;
|
|
1093
|
-
const logoFile = formData.get("logoFile");
|
|
1094
|
-
const logoUpload = await buildConnectionLogoUpload(logoFile);
|
|
1095
|
-
|
|
1096
|
-
if (isActiveConnection) {
|
|
1097
|
-
resetStructureGraphForDatabaseChange();
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
await submitEditConnection(connectionId, {
|
|
1101
|
-
path: String(formData.get("path") ?? ""),
|
|
1102
|
-
label: String(formData.get("label") ?? ""),
|
|
1103
|
-
readOnly: formData.get("readOnly") === "on",
|
|
1104
|
-
clearLogo: formData.get("clearLogo") === "on" && !logoUpload,
|
|
1105
|
-
logoUpload,
|
|
1106
|
-
});
|
|
1107
|
-
|
|
1108
|
-
return;
|
|
1109
|
-
}
|
|
1110
|
-
case "delete-row-confirm":
|
|
1111
|
-
await submitDeleteRowConfirmation();
|
|
1112
|
-
return;
|
|
1113
|
-
case "save-query-chart":
|
|
1114
|
-
await saveCurrentQueryChartDraft();
|
|
1115
|
-
return;
|
|
1116
|
-
case "delete-query-chart":
|
|
1117
|
-
await submitDeleteChartConfirmation();
|
|
1118
|
-
return;
|
|
1119
|
-
case "save-data-row": {
|
|
1120
|
-
const values = {};
|
|
1121
|
-
|
|
1122
|
-
for (const [key, value] of formData.entries()) {
|
|
1123
|
-
if (!key.startsWith("field:")) {
|
|
1124
|
-
continue;
|
|
1524
|
+
if (bindNode.dataset.bind === 'table-designer-field') {
|
|
1525
|
+
if (bindNode instanceof HTMLInputElement && bindNode.type === 'checkbox') {
|
|
1526
|
+
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.checked);
|
|
1527
|
+
return;
|
|
1125
1528
|
}
|
|
1126
1529
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1530
|
+
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.value);
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
if (bindNode.dataset.bind === 'table-designer-column-field') {
|
|
1535
|
+
updateCurrentTableDesignerColumnField(bindNode.dataset.columnId, bindNode.dataset.field, bindNode.value);
|
|
1536
|
+
return;
|
|
1537
|
+
}
|
|
1129
1538
|
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
);
|
|
1134
|
-
return;
|
|
1539
|
+
if (bindNode.dataset.bind === 'table-designer-column-flag') {
|
|
1540
|
+
updateCurrentTableDesignerColumnField(bindNode.dataset.columnId, bindNode.dataset.field, bindNode.checked);
|
|
1541
|
+
return;
|
|
1135
1542
|
}
|
|
1136
|
-
case "save-editor-row": {
|
|
1137
|
-
const values = {};
|
|
1138
1543
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1544
|
+
if (bindNode.dataset.bind === 'query-chart-draft:chartType') {
|
|
1545
|
+
updateCurrentQueryChartDraftField('chartType', bindNode.value);
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
if (bindNode.dataset.bind === 'query-chart-draft:tableVisible') {
|
|
1550
|
+
updateCurrentQueryChartDraftField('tableVisible', bindNode.checked);
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
if (bindNode.dataset.bind?.startsWith('query-chart-draft-config:')) {
|
|
1555
|
+
const field = bindNode.dataset.bind.slice('query-chart-draft-config:'.length);
|
|
1556
|
+
const value =
|
|
1557
|
+
bindNode instanceof HTMLInputElement && bindNode.type === 'checkbox'
|
|
1558
|
+
? bindNode.checked
|
|
1559
|
+
: bindNode.value === ''
|
|
1560
|
+
? null
|
|
1561
|
+
: bindNode.value;
|
|
1562
|
+
updateCurrentQueryChartDraftConfigField(field, value);
|
|
1563
|
+
}
|
|
1564
|
+
});
|
|
1565
|
+
|
|
1566
|
+
document.addEventListener('submit', async event => {
|
|
1567
|
+
const form = event.target.closest('[data-form]');
|
|
1568
|
+
|
|
1569
|
+
if (!form) {
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
event.preventDefault();
|
|
1574
|
+
const formData = new FormData(form);
|
|
1575
|
+
|
|
1576
|
+
switch (form.dataset.form) {
|
|
1577
|
+
case 'open-connection': {
|
|
1578
|
+
resetStructureGraphForDatabaseChange();
|
|
1579
|
+
const connection = await submitOpenConnection({
|
|
1580
|
+
path: String(formData.get('path') ?? ''),
|
|
1581
|
+
label: String(formData.get('label') ?? ''),
|
|
1582
|
+
readOnly: formData.get('readOnly') === 'on',
|
|
1583
|
+
});
|
|
1584
|
+
|
|
1585
|
+
if (connection) {
|
|
1586
|
+
router.navigate('/overview');
|
|
1587
|
+
}
|
|
1588
|
+
return;
|
|
1142
1589
|
}
|
|
1590
|
+
case 'create-connection': {
|
|
1591
|
+
resetStructureGraphForDatabaseChange();
|
|
1592
|
+
const connection = await submitCreateConnection({
|
|
1593
|
+
path: String(formData.get('path') ?? ''),
|
|
1594
|
+
label: String(formData.get('label') ?? ''),
|
|
1595
|
+
});
|
|
1596
|
+
|
|
1597
|
+
if (connection) {
|
|
1598
|
+
router.navigate('/overview');
|
|
1599
|
+
}
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
case 'import-sql': {
|
|
1603
|
+
resetStructureGraphForDatabaseChange();
|
|
1604
|
+
const targetMode = String(formData.get('targetMode') ?? 'active');
|
|
1605
|
+
const payload = {
|
|
1606
|
+
sqlFilePath: String(formData.get('sqlFilePath') ?? ''),
|
|
1607
|
+
label: String(formData.get('label') ?? ''),
|
|
1608
|
+
};
|
|
1609
|
+
|
|
1610
|
+
if (targetMode === 'recent') {
|
|
1611
|
+
payload.targetConnectionId = String(formData.get('targetConnectionId') ?? '');
|
|
1612
|
+
} else if (targetMode === 'create') {
|
|
1613
|
+
payload.createNew = true;
|
|
1614
|
+
payload.targetPath = String(formData.get('targetPath') ?? '');
|
|
1615
|
+
} else if (targetMode === 'path') {
|
|
1616
|
+
payload.targetPath = String(formData.get('targetPath') ?? '');
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
const result = await submitImportSql(payload);
|
|
1620
|
+
if (result) {
|
|
1621
|
+
router.navigate('/overview');
|
|
1622
|
+
}
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
case 'edit-connection': {
|
|
1626
|
+
const connectionId = String(formData.get('connectionId') ?? '');
|
|
1627
|
+
const isActiveConnection = getState().connections.active?.id === connectionId;
|
|
1628
|
+
const logoFile = formData.get('logoFile');
|
|
1629
|
+
const logoUpload = await buildConnectionLogoUpload(logoFile);
|
|
1630
|
+
|
|
1631
|
+
if (isActiveConnection) {
|
|
1632
|
+
resetStructureGraphForDatabaseChange();
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
await submitEditConnection(connectionId, {
|
|
1636
|
+
path: String(formData.get('path') ?? ''),
|
|
1637
|
+
label: String(formData.get('label') ?? ''),
|
|
1638
|
+
readOnly: formData.get('readOnly') === 'on',
|
|
1639
|
+
clearLogo: formData.get('clearLogo') === 'on' && !logoUpload,
|
|
1640
|
+
logoUpload,
|
|
1641
|
+
});
|
|
1642
|
+
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
case 'delete-row-confirm':
|
|
1646
|
+
await submitDeleteRowConfirmation();
|
|
1647
|
+
return;
|
|
1648
|
+
case 'save-query-chart':
|
|
1649
|
+
await saveCurrentQueryChartDraft();
|
|
1650
|
+
return;
|
|
1651
|
+
case 'delete-query-chart':
|
|
1652
|
+
await submitDeleteChartConfirmation();
|
|
1653
|
+
return;
|
|
1654
|
+
case 'delete-query-history-confirm':
|
|
1655
|
+
await submitDeleteQueryHistoryConfirmation();
|
|
1656
|
+
return;
|
|
1657
|
+
case 'create-media-tagging-tag-table':
|
|
1658
|
+
await submitCreateMediaTaggingTagTable();
|
|
1659
|
+
return;
|
|
1660
|
+
case 'create-media-tagging-mapping-table':
|
|
1661
|
+
await submitCreateMediaTaggingMappingTable();
|
|
1662
|
+
return;
|
|
1663
|
+
case 'save-data-row': {
|
|
1664
|
+
const values = {};
|
|
1665
|
+
|
|
1666
|
+
for (const [key, value] of formData.entries()) {
|
|
1667
|
+
if (!key.startsWith('field:')) {
|
|
1668
|
+
continue;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
values[key.slice('field:'.length)] = String(value ?? '');
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
let rowIdentity = null;
|
|
1675
|
+
const rawRowIdentity = String(formData.get('rowIdentity') ?? '').trim();
|
|
1676
|
+
|
|
1677
|
+
if (rawRowIdentity) {
|
|
1678
|
+
try {
|
|
1679
|
+
rowIdentity = JSON.parse(rawRowIdentity);
|
|
1680
|
+
} catch (error) {
|
|
1681
|
+
rowIdentity = null;
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
await submitDataRowUpdate(String(formData.get('rowIndex') ?? ''), values, rowIdentity);
|
|
1686
|
+
return;
|
|
1687
|
+
}
|
|
1688
|
+
case 'save-editor-row': {
|
|
1689
|
+
const values = {};
|
|
1690
|
+
|
|
1691
|
+
for (const [key, value] of formData.entries()) {
|
|
1692
|
+
if (!key.startsWith('field:')) {
|
|
1693
|
+
continue;
|
|
1694
|
+
}
|
|
1143
1695
|
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1696
|
+
values[key.slice('field:'.length)] = String(value ?? '');
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
await submitEditorRowUpdate(String(formData.get('rowIndex') ?? ''), values);
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
case 'save-query-history-title': {
|
|
1703
|
+
const historyId = String(formData.get('historyId') ?? '');
|
|
1704
|
+
const updatedItem = await saveQueryHistoryTitle(historyId, String(formData.get('title') ?? ''));
|
|
1705
|
+
|
|
1706
|
+
if (updatedItem) {
|
|
1707
|
+
syncQueryHistoryUi(historyId);
|
|
1708
|
+
}
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1711
|
+
case 'save-query-history-notes': {
|
|
1712
|
+
const historyId = String(formData.get('historyId') ?? '');
|
|
1713
|
+
const updatedItem = await saveQueryHistoryNotes(historyId, String(formData.get('notes') ?? ''));
|
|
1714
|
+
|
|
1715
|
+
if (updatedItem) {
|
|
1716
|
+
syncQueryHistoryUi(historyId);
|
|
1717
|
+
}
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
default:
|
|
1721
|
+
}
|
|
1167
1722
|
});
|
|
1168
1723
|
|
|
1169
1724
|
subscribe(renderApp);
|
|
1170
1725
|
renderApp(getState());
|
|
1171
1726
|
initializeApp().then(() => {
|
|
1172
|
-
|
|
1727
|
+
router.start();
|
|
1173
1728
|
});
|