Skip to content

Commit e08de1c

Browse files
committed
fixes: #28, #27, better tab management and minor bugs
1 parent cd21064 commit e08de1c

8 files changed

Lines changed: 431 additions & 75 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "gigapi-ui",
3-
"version": "1.0.18",
3+
"version": "1.0.19",
44
"type": "module",
55
"description": "UI interface for GigAPI: The Infinite Timeseries Lakehouse. GigAPI UI provides a slick web interface to query time-series using GigAPI Catalog Metadata + DuckDB",
66
"scripts": {

src/atoms/database-atoms.ts

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -822,7 +822,7 @@ export const isCacheValidAtom = atom((get) => {
822822
export const cacheRefreshLoadingAtom = atom<boolean>(false);
823823

824824
// Refresh cache
825-
export const refreshSchemaCacheAtom = atom(null, async (get, set) => {
825+
export const refreshSchemaCacheAtom = atom(null, async (_get, set) => {
826826
set(cacheRefreshLoadingAtom, true);
827827
const startTime = Date.now();
828828

@@ -997,6 +997,7 @@ export const fetchFreshDatabasesAtom = atom(
997997
.filter(Boolean) || [];
998998

999999
set(freshDatabasesAtom, databases);
1000+
set(freshDatabasesLoadedAtom, true);
10001001
return databases;
10011002
} catch (error) {
10021003
console.error("[Fresh Databases] Failed to fetch:", error);
@@ -1008,17 +1009,16 @@ export const fetchFreshDatabasesAtom = atom(
10081009
}
10091010
);
10101011

1011-
// Fresh tables - always fetch from API
1012+
// Fresh tables - always fetch from API (keyed by database)
10121013
export const freshTablesLoadingAtom = atom<boolean>(false);
1013-
export const freshTablesAtom = atom<string[]>([]);
1014+
export const freshTablesAtom = atom<Record<string, string[]>>({});
10141015

10151016
export const fetchFreshTablesAtom = atom(
10161017
null,
10171018
async (get, set, database: string) => {
10181019
const apiUrl = get(apiUrlAtom);
10191020

10201021
if (!database) {
1021-
set(freshTablesAtom, []);
10221022
return [];
10231023
}
10241024

@@ -1034,29 +1034,42 @@ export const fetchFreshTablesAtom = atom(
10341034
?.map((item: any) => item.table_name || item.Table || item.name)
10351035
.filter(Boolean) || [];
10361036

1037-
set(freshTablesAtom, tables);
1037+
// Store tables keyed by database
1038+
set(freshTablesAtom, (prev) => ({
1039+
...prev,
1040+
[database]: tables
1041+
}));
1042+
// Mark this database as having tables loaded
1043+
set(freshTablesLoadedForAtom, (prev: Set<string>) => {
1044+
const newSet = new Set(prev);
1045+
newSet.add(database);
1046+
return newSet;
1047+
});
10381048
return tables;
10391049
} catch (error) {
10401050
console.error(`[Fresh Tables] Failed to fetch for ${database}:`, error);
1041-
set(freshTablesAtom, []);
1051+
// Don't clear all tables, just mark this database as empty
1052+
set(freshTablesAtom, (prev) => ({
1053+
...prev,
1054+
[database]: []
1055+
}));
10421056
return [];
10431057
} finally {
10441058
set(freshTablesLoadingAtom, false);
10451059
}
10461060
}
10471061
);
10481062

1049-
// Fresh schema - always fetch from API
1063+
// Fresh schema - always fetch from API (keyed by database.table)
10501064
export const freshSchemaLoadingAtom = atom<boolean>(false);
1051-
export const freshSchemaAtom = atom<ColumnSchema[]>([]);
1065+
export const freshSchemaAtom = atom<Record<string, ColumnSchema[]>>({});
10521066

10531067
export const fetchFreshSchemaAtom = atom(
10541068
null,
10551069
async (get, set, { database, table }: { database: string; table: string }) => {
10561070
const apiUrl = get(apiUrlAtom);
10571071

10581072
if (!database || !table) {
1059-
set(freshSchemaAtom, []);
10601073
return [];
10611074
}
10621075

@@ -1093,18 +1106,47 @@ export const fetchFreshSchemaAtom = atom(
10931106
};
10941107
});
10951108

1096-
set(freshSchemaAtom, schemaWithTimeUnits);
1109+
// Store schema keyed by database.table
1110+
const schemaKey = `${database}.${table}`;
1111+
set(freshSchemaAtom, (prev) => ({
1112+
...prev,
1113+
[schemaKey]: schemaWithTimeUnits
1114+
}));
1115+
// Mark this table schema as loaded
1116+
set(freshSchemasLoadedForAtom, (prev: Set<string>) => {
1117+
const newSet = new Set(prev);
1118+
newSet.add(schemaKey);
1119+
return newSet;
1120+
});
10971121
return schemaWithTimeUnits;
10981122
} catch (error) {
10991123
console.error(`[Fresh Schema] Failed to fetch for ${database}.${table}:`, error);
1100-
set(freshSchemaAtom, []);
1124+
// Don't clear all schemas, just mark this table as empty
1125+
const schemaKey = `${database}.${table}`;
1126+
set(freshSchemaAtom, (prev) => ({
1127+
...prev,
1128+
[schemaKey]: []
1129+
}));
11011130
return [];
11021131
} finally {
11031132
set(freshSchemaLoadingAtom, false);
11041133
}
11051134
}
11061135
);
11071136

1137+
// ============================================================================
1138+
// Fresh Data Tracking Atoms - Track what has been loaded
1139+
// ============================================================================
1140+
1141+
// Track if databases have been loaded in this session
1142+
export const freshDatabasesLoadedAtom = atom<boolean>(false);
1143+
1144+
// Track which database tables have been loaded
1145+
export const freshTablesLoadedForAtom = atom<Set<string>>(new Set<string>());
1146+
1147+
// Track which schemas have been loaded (key: "database.table")
1148+
export const freshSchemasLoadedForAtom = atom<Set<string>>(new Set<string>());
1149+
11081150
// ============================================================================
11091151
// Sample Data Cache Management
11101152
// ============================================================================

src/atoms/query-atoms.ts

Lines changed: 63 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { atom } from "jotai";
2-
import { atomWithStorage } from "jotai/utils";
32
import axios from "axios";
43
import { QueryProcessor } from "@/lib/query-processor";
54
import { toast } from "sonner";
@@ -13,20 +12,26 @@ export type { QueryHistoryItem };
1312
// Import tab atoms
1413
import {
1514
currentTabQueryAtom,
16-
currentTabDatabaseAtom,
17-
currentTabTableAtom,
18-
currentTabTimeFieldAtom,
19-
currentTabTimeRangeAtom,
2015
currentTabQueryHistoryAtom,
21-
addToTabQueryHistoryAtom,
2216
clearTabQueryHistoryAtom,
2317
currentTabQueryResultsAtom,
2418
currentTabQueryErrorAtom,
2519
currentTabQueryLoadingAtom,
2620
currentTabQueryExecutionTimeAtom,
2721
currentTabQueryMetricsAtom,
2822
currentTabRawQueryResponseAtom,
29-
currentTabProcessedQueryAtom
23+
currentTabProcessedQueryAtom,
24+
activeTabIdAtom,
25+
addRunningQueryAtom,
26+
removeRunningQueryAtom,
27+
updateTabQueryResultsByIdAtom,
28+
updateTabQueryErrorByIdAtom,
29+
updateTabQueryLoadingByIdAtom,
30+
updateTabQueryMetricsByIdAtom,
31+
updateTabRawResponseByIdAtom,
32+
updateTabProcessedQueryByIdAtom,
33+
addToTabQueryHistoryByIdAtom,
34+
getTabByIdAtom
3035
} from "./tab-atoms";
3136

3237
// Alias the tab-aware atoms for backward compatibility
@@ -48,11 +53,27 @@ export const setQueryAtom = atom(null, (_get, set, query: string) => {
4853
});
4954

5055
export const executeQueryAtom = atom(null, async (get, set) => {
51-
const query = get(queryAtom);
52-
const selectedDb = get(currentTabDatabaseAtom);
53-
const selectedTable = get(currentTabTableAtom);
54-
const selectedTimeField = get(currentTabTimeFieldAtom);
55-
const timeRange = get(currentTabTimeRangeAtom);
56+
// Capture the tab ID at the start of execution
57+
const executeTabId = get(activeTabIdAtom);
58+
if (!executeTabId) {
59+
toast.error("No active tab");
60+
return;
61+
}
62+
63+
// Get the tab data at the time of execution
64+
const getTabById = get(getTabByIdAtom);
65+
const executeTab = getTabById(executeTabId);
66+
if (!executeTab) {
67+
toast.error("Tab not found");
68+
return;
69+
}
70+
71+
// Use the tab's data at execution time
72+
const query = executeTab.query;
73+
const selectedDb = executeTab.database;
74+
const selectedTable = executeTab.table;
75+
const selectedTimeField = executeTab.timeField;
76+
const timeRange = executeTab.timeRange;
5677
const tableSchema = get(tableSchemaAtom);
5778
const apiUrl = get(apiUrlAtom);
5879

@@ -66,9 +87,13 @@ export const executeQueryAtom = atom(null, async (get, set) => {
6687
return;
6788
}
6889

69-
set(queryLoadingAtom, true);
70-
set(queryErrorAtom, null);
71-
set(queryResultsAtom, null); // Clear previous results
90+
// Update loading state for the specific tab
91+
set(updateTabQueryLoadingByIdAtom, { tabId: executeTabId, loading: true });
92+
set(updateTabQueryErrorByIdAtom, { tabId: executeTabId, error: null });
93+
set(updateTabQueryResultsByIdAtom, { tabId: executeTabId, results: null }); // Clear previous results
94+
95+
// Mark this tab as having a running query
96+
set(addRunningQueryAtom, executeTabId);
7297

7398
const startTime = Date.now();
7499

@@ -112,13 +137,13 @@ export const executeQueryAtom = atom(null, async (get, set) => {
112137
});
113138

114139
if (processed.errors.length > 0) {
115-
set(queryErrorAtom, processed.errors.join("; "));
140+
set(updateTabQueryErrorByIdAtom, { tabId: executeTabId, error: processed.errors.join("; ") });
116141
toast.error(`Query processing error: ${processed.errors[0]}`);
117142
return;
118143
}
119144

120145
processedQuery = processed.query;
121-
set(processedQueryAtom, processedQuery);
146+
set(updateTabProcessedQueryByIdAtom, { tabId: executeTabId, query: processedQuery });
122147
}
123148

124149
// Execute the processed query
@@ -176,23 +201,25 @@ export const executeQueryAtom = atom(null, async (get, set) => {
176201
}
177202

178203
// Store raw response for Raw tab
179-
set(rawQueryResponseAtom, rawResponse);
204+
set(updateTabRawResponseByIdAtom, { tabId: executeTabId, response: rawResponse });
180205

181206
const executionTime = Date.now() - startTime;
182-
set(queryResultsAtom, parsedResults);
183-
set(queryExecutionTimeAtom, executionTime);
207+
set(updateTabQueryResultsByIdAtom, { tabId: executeTabId, results: parsedResults });
184208

185209
// Calculate size properly based on the raw response
186210
const responseSize =
187211
typeof result === "string"
188212
? result.length
189213
: JSON.stringify(result).length;
190214

191-
set(queryMetricsAtom, {
192-
executionTime,
193-
rowCount: parsedResults.length,
194-
size: responseSize,
195-
processedRows: parsedResults.length,
215+
set(updateTabQueryMetricsByIdAtom, {
216+
tabId: executeTabId,
217+
metrics: {
218+
executionTime,
219+
rowCount: parsedResults.length,
220+
size: responseSize,
221+
processedRows: parsedResults.length,
222+
}
196223
});
197224

198225
// Add to history with full context
@@ -211,8 +238,8 @@ export const executeQueryAtom = atom(null, async (get, set) => {
211238
rowCount: parsedResults.length,
212239
};
213240

214-
// Add to current tab's history
215-
set(addToTabQueryHistoryAtom, historyItem);
241+
// Add to the specific tab's history
242+
set(addToTabQueryHistoryByIdAtom, { tabId: executeTabId, historyItem });
216243
} catch (error: any) {
217244
let errorMessage = "Query failed";
218245

@@ -245,8 +272,8 @@ export const executeQueryAtom = atom(null, async (get, set) => {
245272
errorMessage = error.message;
246273
}
247274

248-
set(queryErrorAtom, errorMessage);
249-
set(queryResultsAtom, null); // Clear results on error
275+
set(updateTabQueryErrorByIdAtom, { tabId: executeTabId, error: errorMessage });
276+
set(updateTabQueryResultsByIdAtom, { tabId: executeTabId, results: null }); // Clear results on error
250277

251278
// Add failed query to history with full context
252279
const historyItem: QueryHistoryItem = {
@@ -262,10 +289,14 @@ export const executeQueryAtom = atom(null, async (get, set) => {
262289
error: errorMessage,
263290
};
264291

265-
// Add to current tab's history
266-
set(addToTabQueryHistoryAtom, historyItem);
292+
// Add to the specific tab's history
293+
set(addToTabQueryHistoryByIdAtom, { tabId: executeTabId, historyItem });
267294
} finally {
268-
set(queryLoadingAtom, false);
295+
// Update loading state for the specific tab
296+
set(updateTabQueryLoadingByIdAtom, { tabId: executeTabId, loading: false });
297+
298+
// Remove this tab from running queries
299+
set(removeRunningQueryAtom, executeTabId);
269300
}
270301
});
271302

0 commit comments

Comments
 (0)