sqlite-hub 0.9.13 → 0.11.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/frontend/js/api.js +16 -0
- package/frontend/js/app.js +28 -6
- package/frontend/js/components/modal.js +98 -1
- package/frontend/js/components/queryEditor.js +1 -8
- package/frontend/js/store.js +260 -18
- package/frontend/js/views/data.js +128 -69
- package/frontend/js/views/editor.js +1 -3
- package/frontend/js/views/structure.js +43 -5
- package/frontend/styles/tailwind.generated.css +1 -1
- package/frontend/styles/views.css +4 -0
- package/package.json +1 -1
- package/server/routes/data.js +17 -0
- package/server/services/sqlite/dataBrowserService.js +215 -48
- package/tests/sql-identifier-safety.test.js +107 -8
package/frontend/js/store.js
CHANGED
|
@@ -30,7 +30,8 @@ const DEFAULT_SETTINGS = {
|
|
|
30
30
|
csvDelimiter: ',',
|
|
31
31
|
};
|
|
32
32
|
const DEFAULT_DATA_PAGE_SIZE = 50;
|
|
33
|
-
const DATA_PAGE_SIZES = [25, 50, 100];
|
|
33
|
+
const DATA_PAGE_SIZES = [25, 50, 100, 250];
|
|
34
|
+
const DATA_FILTER_OPERATORS = new Set(['=', '!=', '<', '>', '<=', '>=', 'equals']);
|
|
34
35
|
const DATA_ROW_SIZE_STORAGE_KEY = 'data_row_size';
|
|
35
36
|
const CHARTS_HISTORY_TAB_STORAGE_KEY = 'charts_history_tab';
|
|
36
37
|
const QUERY_HISTORY_TAB_STORAGE_KEY = 'query_history_tab';
|
|
@@ -38,6 +39,7 @@ const UI_PREFERENCE_STORAGE_KEYS = {
|
|
|
38
39
|
sqlEditorHistoryVisible: 'sqlite_hub_sql_editor_history_visible',
|
|
39
40
|
sqlEditorEditorVisible: 'sqlite_hub_sql_editor_editor_visible',
|
|
40
41
|
sqlEditorActiveTab: 'sqlite_hub_sql_editor_active_tab',
|
|
42
|
+
sqlEditorQueryDraft: 'sqlite_hub_sql_editor_query_draft',
|
|
41
43
|
dataTablesVisible: 'sqlite_hub_data_tables_visible',
|
|
42
44
|
structureTablesVisible: 'sqlite_hub_structure_tables_visible',
|
|
43
45
|
chartsHistoryVisible: 'sqlite_hub_charts_history_visible',
|
|
@@ -103,6 +105,23 @@ function storeBoolean(key, value) {
|
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
|
|
108
|
+
function readStoredString(key, fallback = '') {
|
|
109
|
+
try {
|
|
110
|
+
const value = globalThis.localStorage?.getItem(key);
|
|
111
|
+
return value === null || value === undefined ? fallback : value;
|
|
112
|
+
} catch {
|
|
113
|
+
return fallback;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function storeString(key, value) {
|
|
118
|
+
try {
|
|
119
|
+
globalThis.localStorage?.setItem(key, String(value ?? ''));
|
|
120
|
+
} catch {
|
|
121
|
+
// Ignore unavailable browser storage; the in-memory value still applies.
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
106
125
|
function readStoredEditorActiveTab(fallback = 'messages') {
|
|
107
126
|
try {
|
|
108
127
|
const value = globalThis.localStorage?.getItem(UI_PREFERENCE_STORAGE_KEYS.sqlEditorActiveTab);
|
|
@@ -193,7 +212,9 @@ const state = {
|
|
|
193
212
|
sortColumn: null,
|
|
194
213
|
sortDirection: null,
|
|
195
214
|
searchQuery: '',
|
|
215
|
+
tableSearchQuery: '',
|
|
196
216
|
searchColumn: '',
|
|
217
|
+
filterOperator: '=',
|
|
197
218
|
selectedRowIndex: null,
|
|
198
219
|
selectedRow: null,
|
|
199
220
|
pendingOpenRow: null,
|
|
@@ -202,7 +223,7 @@ const state = {
|
|
|
202
223
|
saveError: null,
|
|
203
224
|
},
|
|
204
225
|
editor: {
|
|
205
|
-
sqlText:
|
|
226
|
+
sqlText: readStoredString(UI_PREFERENCE_STORAGE_KEYS.sqlEditorQueryDraft),
|
|
206
227
|
editorPanelVisible: readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.sqlEditorEditorVisible, true),
|
|
207
228
|
history: [],
|
|
208
229
|
historyPanelVisible: readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.sqlEditorHistoryVisible, true),
|
|
@@ -271,6 +292,7 @@ const state = {
|
|
|
271
292
|
selectedName: null,
|
|
272
293
|
detail: null,
|
|
273
294
|
tablesVisible: readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.structureTablesVisible, true),
|
|
295
|
+
tableSearchQuery: '',
|
|
274
296
|
loading: false,
|
|
275
297
|
detailLoading: false,
|
|
276
298
|
error: null,
|
|
@@ -525,6 +547,27 @@ function canEditQueryResult(snapshot = state) {
|
|
|
525
547
|
function resetDataBrowserSearch() {
|
|
526
548
|
state.dataBrowser.searchQuery = '';
|
|
527
549
|
state.dataBrowser.searchColumn = '';
|
|
550
|
+
state.dataBrowser.filterOperator = '=';
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function isDataBrowserTextColumn(columnName) {
|
|
554
|
+
const column = (state.dataBrowser.table?.columnMeta ?? []).find(item => item.name === columnName);
|
|
555
|
+
|
|
556
|
+
return String(column?.affinity ?? '').toUpperCase() === 'TEXT';
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function normalizeDataFilterOperatorForColumn(operator, columnName) {
|
|
560
|
+
const normalizedOperator = DATA_FILTER_OPERATORS.has(operator) ? operator : '=';
|
|
561
|
+
|
|
562
|
+
if (normalizedOperator === 'equals' && !isDataBrowserTextColumn(columnName)) {
|
|
563
|
+
return '=';
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return normalizedOperator;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function resetDataBrowserTableSearch() {
|
|
570
|
+
state.dataBrowser.tableSearchQuery = '';
|
|
528
571
|
}
|
|
529
572
|
|
|
530
573
|
function resetDataBrowserSort() {
|
|
@@ -707,7 +750,11 @@ function clearDataBrowserRowSelectionState() {
|
|
|
707
750
|
}
|
|
708
751
|
|
|
709
752
|
function resolveDataBrowserRowSelection(rowIndex, identity = null) {
|
|
710
|
-
const
|
|
753
|
+
const hasRowIndex =
|
|
754
|
+
rowIndex !== null &&
|
|
755
|
+
rowIndex !== undefined &&
|
|
756
|
+
(typeof rowIndex !== 'string' || rowIndex.trim() !== '');
|
|
757
|
+
const numericIndex = hasRowIndex ? Number(rowIndex) : NaN;
|
|
711
758
|
|
|
712
759
|
if (Number.isInteger(numericIndex) && numericIndex >= 0) {
|
|
713
760
|
const indexedRow = state.dataBrowser.table?.rows?.[numericIndex] ?? null;
|
|
@@ -951,6 +998,7 @@ function setMissingDatabaseState() {
|
|
|
951
998
|
state.dataBrowser.selectedTable = null;
|
|
952
999
|
state.dataBrowser.table = null;
|
|
953
1000
|
state.dataBrowser.page = 1;
|
|
1001
|
+
resetDataBrowserTableSearch();
|
|
954
1002
|
resetDataBrowserSort();
|
|
955
1003
|
resetDataBrowserSearch();
|
|
956
1004
|
clearDataBrowserRowSelectionState();
|
|
@@ -964,6 +1012,7 @@ function setMissingDatabaseState() {
|
|
|
964
1012
|
state.structure.data = null;
|
|
965
1013
|
state.structure.detail = null;
|
|
966
1014
|
state.structure.tablesVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.structureTablesVisible, true);
|
|
1015
|
+
state.structure.tableSearchQuery = '';
|
|
967
1016
|
state.structure.error = error;
|
|
968
1017
|
|
|
969
1018
|
state.tableDesigner.loading = false;
|
|
@@ -1482,6 +1531,9 @@ async function loadDataTable(version) {
|
|
|
1482
1531
|
offset: (page - 1) * pageSize,
|
|
1483
1532
|
sortColumn,
|
|
1484
1533
|
sortDirection,
|
|
1534
|
+
filterColumn: state.dataBrowser.searchColumn,
|
|
1535
|
+
filterOperator: state.dataBrowser.filterOperator,
|
|
1536
|
+
filterValue: state.dataBrowser.searchQuery,
|
|
1485
1537
|
});
|
|
1486
1538
|
|
|
1487
1539
|
if (version !== routeLoadVersion) {
|
|
@@ -1493,9 +1545,22 @@ async function loadDataTable(version) {
|
|
|
1493
1545
|
state.dataBrowser.page = response.data?.page ?? page;
|
|
1494
1546
|
state.dataBrowser.sortColumn = response.data?.sort?.column ?? null;
|
|
1495
1547
|
state.dataBrowser.sortDirection = response.data?.sort?.direction ?? null;
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1548
|
+
const responseColumns = response.data?.columns ?? [];
|
|
1549
|
+
const responseFilter = response.data?.filter ?? null;
|
|
1550
|
+
|
|
1551
|
+
state.dataBrowser.searchColumn =
|
|
1552
|
+
responseFilter?.column ??
|
|
1553
|
+
(responseColumns.includes(state.dataBrowser.searchColumn)
|
|
1554
|
+
? state.dataBrowser.searchColumn
|
|
1555
|
+
: (responseColumns[0] ?? ''));
|
|
1556
|
+
const responseOperator = DATA_FILTER_OPERATORS.has(responseFilter?.operator)
|
|
1557
|
+
? responseFilter.operator
|
|
1558
|
+
: state.dataBrowser.filterOperator;
|
|
1559
|
+
|
|
1560
|
+
state.dataBrowser.filterOperator = normalizeDataFilterOperatorForColumn(
|
|
1561
|
+
responseOperator,
|
|
1562
|
+
state.dataBrowser.searchColumn,
|
|
1563
|
+
);
|
|
1499
1564
|
clearDataBrowserRowSelectionState();
|
|
1500
1565
|
await resolvePendingDataBrowserRow(version);
|
|
1501
1566
|
} catch (error) {
|
|
@@ -1919,6 +1984,7 @@ function invalidateDatabaseCaches() {
|
|
|
1919
1984
|
state.dataBrowser.selectedTable = null;
|
|
1920
1985
|
state.dataBrowser.table = null;
|
|
1921
1986
|
state.dataBrowser.page = 1;
|
|
1987
|
+
resetDataBrowserTableSearch();
|
|
1922
1988
|
resetDataBrowserSearch();
|
|
1923
1989
|
clearDataBrowserRowSelectionState();
|
|
1924
1990
|
state.dataBrowser.pendingOpenRow = null;
|
|
@@ -1939,6 +2005,7 @@ function invalidateDatabaseCaches() {
|
|
|
1939
2005
|
state.structure.data = null;
|
|
1940
2006
|
state.structure.detail = null;
|
|
1941
2007
|
state.structure.tablesVisible = readStoredBoolean(UI_PREFERENCE_STORAGE_KEYS.structureTablesVisible, true);
|
|
2008
|
+
state.structure.tableSearchQuery = '';
|
|
1942
2009
|
state.mediaTagging.loading = false;
|
|
1943
2010
|
state.mediaTagging.previewLoading = false;
|
|
1944
2011
|
state.mediaTagging.saving = false;
|
|
@@ -2356,6 +2423,92 @@ export function closeModal() {
|
|
|
2356
2423
|
closeModalInternal();
|
|
2357
2424
|
}
|
|
2358
2425
|
|
|
2426
|
+
export async function openDataRowUpdatePreview(rowIndex, values, identity = null) {
|
|
2427
|
+
const tableName = state.dataBrowser.selectedTable;
|
|
2428
|
+
const selected = resolveDataBrowserRowSelection(rowIndex, identity);
|
|
2429
|
+
|
|
2430
|
+
if (!tableName || !selected.identity) {
|
|
2431
|
+
pushToast('The selected row could not be loaded.', 'alert');
|
|
2432
|
+
return null;
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
state.dataBrowser.saving = true;
|
|
2436
|
+
state.dataBrowser.saveError = null;
|
|
2437
|
+
emitChange();
|
|
2438
|
+
|
|
2439
|
+
try {
|
|
2440
|
+
const response = await api.previewDataTableRowUpdate(tableName, {
|
|
2441
|
+
identity: selected.identity,
|
|
2442
|
+
values,
|
|
2443
|
+
});
|
|
2444
|
+
|
|
2445
|
+
state.modal = {
|
|
2446
|
+
kind: 'row-update-preview',
|
|
2447
|
+
target: 'data',
|
|
2448
|
+
tableName,
|
|
2449
|
+
rowIndex: selected.rowIndex,
|
|
2450
|
+
identity: selected.identity,
|
|
2451
|
+
values,
|
|
2452
|
+
preview: response.data,
|
|
2453
|
+
error: null,
|
|
2454
|
+
submitting: false,
|
|
2455
|
+
};
|
|
2456
|
+
emitChange();
|
|
2457
|
+
return response.data;
|
|
2458
|
+
} catch (error) {
|
|
2459
|
+
state.dataBrowser.saveError = normalizeError(error);
|
|
2460
|
+
emitChange();
|
|
2461
|
+
return null;
|
|
2462
|
+
} finally {
|
|
2463
|
+
state.dataBrowser.saving = false;
|
|
2464
|
+
emitChange();
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
export async function openEditorRowUpdatePreview(rowIndex, values) {
|
|
2469
|
+
const numericIndex = Number(rowIndex);
|
|
2470
|
+
const result = state.editor.result;
|
|
2471
|
+
const row = result?.rows?.[numericIndex];
|
|
2472
|
+
const tableName = result?.editing?.tableName ?? null;
|
|
2473
|
+
|
|
2474
|
+
if (!tableName || !row || !canEditQueryResult()) {
|
|
2475
|
+
pushToast('The selected query result row could not be loaded.', 'alert');
|
|
2476
|
+
return null;
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2479
|
+
state.editor.saving = true;
|
|
2480
|
+
state.editor.saveError = null;
|
|
2481
|
+
emitChange();
|
|
2482
|
+
|
|
2483
|
+
try {
|
|
2484
|
+
const response = await api.previewDataTableRowUpdate(tableName, {
|
|
2485
|
+
identity: row.__identity,
|
|
2486
|
+
values,
|
|
2487
|
+
});
|
|
2488
|
+
|
|
2489
|
+
state.modal = {
|
|
2490
|
+
kind: 'row-update-preview',
|
|
2491
|
+
target: 'editor',
|
|
2492
|
+
tableName,
|
|
2493
|
+
rowIndex: numericIndex,
|
|
2494
|
+
identity: row.__identity,
|
|
2495
|
+
values,
|
|
2496
|
+
preview: response.data,
|
|
2497
|
+
error: null,
|
|
2498
|
+
submitting: false,
|
|
2499
|
+
};
|
|
2500
|
+
emitChange();
|
|
2501
|
+
return response.data;
|
|
2502
|
+
} catch (error) {
|
|
2503
|
+
state.editor.saveError = normalizeError(error);
|
|
2504
|
+
emitChange();
|
|
2505
|
+
return null;
|
|
2506
|
+
} finally {
|
|
2507
|
+
state.editor.saving = false;
|
|
2508
|
+
emitChange();
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2359
2512
|
export function toggleChartsSqlPanel() {
|
|
2360
2513
|
state.charts.sqlExpanded = !state.charts.sqlExpanded;
|
|
2361
2514
|
emitChange();
|
|
@@ -2623,6 +2776,7 @@ export function setCurrentQuery(query) {
|
|
|
2623
2776
|
const nextLineCount = Math.max(1, nextQuery.split('\n').length);
|
|
2624
2777
|
|
|
2625
2778
|
state.editor.sqlText = nextQuery;
|
|
2779
|
+
storeString(UI_PREFERENCE_STORAGE_KEYS.sqlEditorQueryDraft, nextQuery);
|
|
2626
2780
|
|
|
2627
2781
|
if (previousLineCount !== nextLineCount) {
|
|
2628
2782
|
emitChange();
|
|
@@ -2631,6 +2785,7 @@ export function setCurrentQuery(query) {
|
|
|
2631
2785
|
|
|
2632
2786
|
export function clearCurrentQuery() {
|
|
2633
2787
|
state.editor.sqlText = '';
|
|
2788
|
+
storeString(UI_PREFERENCE_STORAGE_KEYS.sqlEditorQueryDraft, '');
|
|
2634
2789
|
state.editor.result = null;
|
|
2635
2790
|
state.editor.lastExecutedSql = '';
|
|
2636
2791
|
resetEditorResultSort();
|
|
@@ -2696,6 +2851,8 @@ export async function executeCurrentQuery() {
|
|
|
2696
2851
|
return true;
|
|
2697
2852
|
} catch (error) {
|
|
2698
2853
|
state.editor.error = normalizeError(error);
|
|
2854
|
+
state.editor.activeTab = 'messages';
|
|
2855
|
+
storeEditorActiveTab('messages');
|
|
2699
2856
|
await refreshQueryHistoryState();
|
|
2700
2857
|
return false;
|
|
2701
2858
|
} finally {
|
|
@@ -2819,6 +2976,7 @@ export function openQueryHistoryInEditor(historyId, options = {}) {
|
|
|
2819
2976
|
setActiveQueryHistoryItem(historyId);
|
|
2820
2977
|
clearQueryHistoryDetailState();
|
|
2821
2978
|
state.editor.sqlText = options.append ? [state.editor.sqlText.trim(), rawSql].filter(Boolean).join('\n\n') : rawSql;
|
|
2979
|
+
storeString(UI_PREFERENCE_STORAGE_KEYS.sqlEditorQueryDraft, state.editor.sqlText);
|
|
2822
2980
|
emitChange();
|
|
2823
2981
|
return true;
|
|
2824
2982
|
}
|
|
@@ -2926,6 +3084,16 @@ export function setTableDesignerSearchQuery(query) {
|
|
|
2926
3084
|
emitChange();
|
|
2927
3085
|
}
|
|
2928
3086
|
|
|
3087
|
+
export function setDataTableSearchQuery(query) {
|
|
3088
|
+
state.dataBrowser.tableSearchQuery = String(query ?? '');
|
|
3089
|
+
emitChange();
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
export function setStructureTableSearchQuery(query) {
|
|
3093
|
+
state.structure.tableSearchQuery = String(query ?? '');
|
|
3094
|
+
emitChange();
|
|
3095
|
+
}
|
|
3096
|
+
|
|
2929
3097
|
export function setTableDesignerSqlPreviewVisibility(visible) {
|
|
2930
3098
|
const nextValue = typeof visible === 'boolean' ? visible : !Boolean(state.tableDesigner.sqlPreviewVisible);
|
|
2931
3099
|
|
|
@@ -3494,18 +3662,59 @@ export function clearDataRowSelection(options = {}) {
|
|
|
3494
3662
|
}
|
|
3495
3663
|
}
|
|
3496
3664
|
|
|
3497
|
-
|
|
3498
|
-
state.
|
|
3665
|
+
async function reloadDataTableForFilterChange() {
|
|
3666
|
+
if (state.route.name === 'data' && state.dataBrowser.selectedTable) {
|
|
3667
|
+
await loadDataTable(++routeLoadVersion);
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
|
|
3671
|
+
export async function setDataSearchQuery(query) {
|
|
3672
|
+
const nextQuery = String(query ?? '');
|
|
3673
|
+
|
|
3674
|
+
if (state.dataBrowser.searchQuery === nextQuery) {
|
|
3675
|
+
return;
|
|
3676
|
+
}
|
|
3677
|
+
|
|
3678
|
+
state.dataBrowser.searchQuery = nextQuery;
|
|
3679
|
+
state.dataBrowser.page = 1;
|
|
3680
|
+
clearDataBrowserRowSelectionState();
|
|
3681
|
+
state.dataBrowser.saveError = null;
|
|
3682
|
+
emitChange();
|
|
3683
|
+
await reloadDataTableForFilterChange();
|
|
3684
|
+
}
|
|
3685
|
+
|
|
3686
|
+
export async function setDataSearchColumn(columnName) {
|
|
3687
|
+
const nextColumnName = String(columnName ?? '');
|
|
3688
|
+
|
|
3689
|
+
if (state.dataBrowser.searchColumn === nextColumnName) {
|
|
3690
|
+
return;
|
|
3691
|
+
}
|
|
3692
|
+
|
|
3693
|
+
state.dataBrowser.searchColumn = nextColumnName;
|
|
3694
|
+
state.dataBrowser.filterOperator = normalizeDataFilterOperatorForColumn(
|
|
3695
|
+
state.dataBrowser.filterOperator,
|
|
3696
|
+
nextColumnName,
|
|
3697
|
+
);
|
|
3698
|
+
state.dataBrowser.page = 1;
|
|
3499
3699
|
clearDataBrowserRowSelectionState();
|
|
3500
3700
|
state.dataBrowser.saveError = null;
|
|
3501
3701
|
emitChange();
|
|
3702
|
+
await reloadDataTableForFilterChange();
|
|
3502
3703
|
}
|
|
3503
3704
|
|
|
3504
|
-
export function
|
|
3505
|
-
|
|
3705
|
+
export async function setDataFilterOperator(operator) {
|
|
3706
|
+
const nextOperator = String(operator ?? '=').trim();
|
|
3707
|
+
|
|
3708
|
+
if (!DATA_FILTER_OPERATORS.has(nextOperator) || state.dataBrowser.filterOperator === nextOperator) {
|
|
3709
|
+
return;
|
|
3710
|
+
}
|
|
3711
|
+
|
|
3712
|
+
state.dataBrowser.filterOperator = nextOperator;
|
|
3713
|
+
state.dataBrowser.page = 1;
|
|
3506
3714
|
clearDataBrowserRowSelectionState();
|
|
3507
3715
|
state.dataBrowser.saveError = null;
|
|
3508
3716
|
emitChange();
|
|
3717
|
+
await reloadDataTableForFilterChange();
|
|
3509
3718
|
}
|
|
3510
3719
|
|
|
3511
3720
|
export function toggleDataTablesPanel() {
|
|
@@ -3577,9 +3786,10 @@ export async function setDataPageSize(pageSize) {
|
|
|
3577
3786
|
}
|
|
3578
3787
|
}
|
|
3579
3788
|
|
|
3580
|
-
export async function submitDataRowUpdate(rowIndex, values, identity = null) {
|
|
3789
|
+
export async function submitDataRowUpdate(rowIndex, values, identity = null, options = {}) {
|
|
3581
3790
|
const tableName = state.dataBrowser.selectedTable;
|
|
3582
3791
|
const selected = resolveDataBrowserRowSelection(rowIndex, identity);
|
|
3792
|
+
const reportErrorToModal = Boolean(options.reportErrorToModal);
|
|
3583
3793
|
|
|
3584
3794
|
if (!tableName || !selected.identity) {
|
|
3585
3795
|
pushToast('The selected row could not be loaded.', 'alert');
|
|
@@ -3601,8 +3811,12 @@ export async function submitDataRowUpdate(rowIndex, values, identity = null) {
|
|
|
3601
3811
|
clearDataBrowserRowSelectionState();
|
|
3602
3812
|
return response.data;
|
|
3603
3813
|
} catch (error) {
|
|
3604
|
-
|
|
3605
|
-
|
|
3814
|
+
if (reportErrorToModal) {
|
|
3815
|
+
withModalError(error);
|
|
3816
|
+
} else {
|
|
3817
|
+
state.dataBrowser.saveError = normalizeError(error);
|
|
3818
|
+
emitChange();
|
|
3819
|
+
}
|
|
3606
3820
|
return null;
|
|
3607
3821
|
} finally {
|
|
3608
3822
|
state.dataBrowser.saving = false;
|
|
@@ -3654,11 +3868,12 @@ export async function submitDataRowDelete(rowIndex, options = {}) {
|
|
|
3654
3868
|
}
|
|
3655
3869
|
}
|
|
3656
3870
|
|
|
3657
|
-
export async function submitEditorRowUpdate(rowIndex, values) {
|
|
3871
|
+
export async function submitEditorRowUpdate(rowIndex, values, options = {}) {
|
|
3658
3872
|
const numericIndex = Number(rowIndex);
|
|
3659
3873
|
const result = state.editor.result;
|
|
3660
3874
|
const row = result?.rows?.[numericIndex];
|
|
3661
3875
|
const tableName = result?.editing?.tableName ?? null;
|
|
3876
|
+
const reportErrorToModal = Boolean(options.reportErrorToModal);
|
|
3662
3877
|
|
|
3663
3878
|
if (!tableName || !row || !canEditQueryResult()) {
|
|
3664
3879
|
pushToast('The selected query result row could not be loaded.', 'alert');
|
|
@@ -3688,8 +3903,12 @@ export async function submitEditorRowUpdate(rowIndex, values) {
|
|
|
3688
3903
|
emitChange();
|
|
3689
3904
|
return response.data;
|
|
3690
3905
|
} catch (error) {
|
|
3691
|
-
|
|
3692
|
-
|
|
3906
|
+
if (reportErrorToModal) {
|
|
3907
|
+
withModalError(error);
|
|
3908
|
+
} else {
|
|
3909
|
+
state.editor.saveError = normalizeError(error);
|
|
3910
|
+
emitChange();
|
|
3911
|
+
}
|
|
3693
3912
|
return null;
|
|
3694
3913
|
} finally {
|
|
3695
3914
|
state.editor.saving = false;
|
|
@@ -3767,6 +3986,29 @@ export async function submitDeleteRowConfirmation() {
|
|
|
3767
3986
|
return result;
|
|
3768
3987
|
}
|
|
3769
3988
|
|
|
3989
|
+
export async function submitRowUpdatePreviewConfirmation() {
|
|
3990
|
+
const modal = state.modal;
|
|
3991
|
+
|
|
3992
|
+
if (modal?.kind !== 'row-update-preview') {
|
|
3993
|
+
return null;
|
|
3994
|
+
}
|
|
3995
|
+
|
|
3996
|
+
startModalSubmission();
|
|
3997
|
+
|
|
3998
|
+
const result =
|
|
3999
|
+
modal.target === 'editor'
|
|
4000
|
+
? await submitEditorRowUpdate(modal.rowIndex, modal.values, { reportErrorToModal: true })
|
|
4001
|
+
: await submitDataRowUpdate(modal.rowIndex, modal.values, modal.identity, {
|
|
4002
|
+
reportErrorToModal: true,
|
|
4003
|
+
});
|
|
4004
|
+
|
|
4005
|
+
if (result) {
|
|
4006
|
+
closeModalInternal();
|
|
4007
|
+
}
|
|
4008
|
+
|
|
4009
|
+
return result;
|
|
4010
|
+
}
|
|
4011
|
+
|
|
3770
4012
|
export async function saveCurrentQueryChartDraft() {
|
|
3771
4013
|
if (state.modal?.kind !== 'chart-editor' || !state.modal.draft) {
|
|
3772
4014
|
return null;
|
|
@@ -3988,12 +4230,12 @@ export function getQueryMessages(snapshot = state) {
|
|
|
3988
4230
|
|
|
3989
4231
|
if (snapshot.editor.error) {
|
|
3990
4232
|
return [
|
|
3991
|
-
...queryMessages,
|
|
3992
4233
|
{
|
|
3993
4234
|
tone: 'alert',
|
|
3994
4235
|
label: snapshot.editor.error.code,
|
|
3995
4236
|
value: snapshot.editor.error.message,
|
|
3996
4237
|
},
|
|
4238
|
+
...queryMessages,
|
|
3997
4239
|
];
|
|
3998
4240
|
}
|
|
3999
4241
|
|
|
@@ -4008,7 +4250,6 @@ export function getQueryMessages(snapshot = state) {
|
|
|
4008
4250
|
}
|
|
4009
4251
|
|
|
4010
4252
|
return [
|
|
4011
|
-
...queryMessages,
|
|
4012
4253
|
...snapshot.editor.result.statements.map(statement => ({
|
|
4013
4254
|
tone: statement.kind === 'resultSet' ? 'success' : inferStatusTone(statement.keyword),
|
|
4014
4255
|
label: `${statement.keyword} #${statement.index + 1}`,
|
|
@@ -4017,6 +4258,7 @@ export function getQueryMessages(snapshot = state) {
|
|
|
4017
4258
|
? `${statement.rowCount} row(s) returned.`
|
|
4018
4259
|
: `${statement.changes} row(s) affected.`,
|
|
4019
4260
|
})),
|
|
4261
|
+
...queryMessages,
|
|
4020
4262
|
];
|
|
4021
4263
|
}
|
|
4022
4264
|
|