Skip to content

Commit cd21064

Browse files
committed
feat: Introducing Tabs! fix: resolve multiple UI/UX issues in panel editor and dashboard management
- Fix infinite re-render loop in panel configuration (#21) - Fixed DebouncedInput component circular dependency causing infinite updates - Removed unnecessary refreshTrigger from config changes - Fixed ECharts disposal issues by separating initialization from updates - Added proper memoization to prevent unnecessary re-renders - Implement live preview updates in panel editor (#21) - Panel preview now updates instantly when changing configuration - Unit, decimals, and other settings update without re-running queries - Smooth real-time feedback for better user experience - Fix chart spacing and formatting issues (#21) - Added proper spacing between Y-axis values and units (e.g., '100.0 temperature' instead of '100.0temperature') - Fixed chart margins with consistent padding - Improved overall chart layout and readability - Preserve panel dimensions when duplicating (#23) - Duplicated panels now maintain original width and height settings - Enhanced duplicatePanel function to copy layout information - Updated addPanelAtom to accept custom dimensions - Enhance tab management (#11) - Fixed tab isolation issues - Improved dynamic schema fetching - Better error handling for tab operations Fixes #11, #21, #23
1 parent de6e523 commit cd21064

35 files changed

Lines changed: 3294 additions & 756 deletions

bun.lock

Lines changed: 61 additions & 36 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "gigapi-ui",
3-
"version": "1.0.17",
3+
"version": "1.0.18",
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": {
@@ -16,7 +16,7 @@
1616
"author": "",
1717
"license": "AGPL-3.0",
1818
"dependencies": {
19-
"@hookform/resolvers": "^5.1.1",
19+
"@hookform/resolvers": "^5.2.0",
2020
"@monaco-editor/react": "^4.7.0",
2121
"@radix-ui/react-checkbox": "^1.3.2",
2222
"@radix-ui/react-dialog": "^1.1.14",
@@ -33,44 +33,44 @@
3333
"@tanstack/react-table": "^8.21.3",
3434
"@tanstack/react-virtual": "^3.13.12",
3535
"@types/react-grid-layout": "^1.3.5",
36-
"axios": "^1.10.0",
36+
"axios": "^1.11.0",
3737
"class-variance-authority": "^0.7.1",
3838
"clsx": "^2.1.1",
3939
"cmdk": "^1.1.1",
4040
"date-fns": "^4.1.0",
4141
"date-fns-tz": "^3.2.0",
4242
"echarts": "^5.6.0",
4343
"jotai": "^2.12.5",
44-
"lucide-react": "^0.525.0",
44+
"lucide-react": "^0.533.0",
4545
"pako": "^2.1.0",
46-
"react": "^19.1.0",
47-
"react-day-picker": "^9.8.0",
48-
"react-dom": "^19.1.0",
46+
"react": "^19.1.1",
47+
"react-day-picker": "^9.8.1",
48+
"react-dom": "^19.1.1",
4949
"react-grid-layout": "^1.5.2",
50-
"react-hook-form": "^7.60.0",
50+
"react-hook-form": "^7.61.1",
5151
"react-markdown": "^10.1.0",
5252
"react-resizable": "^3.0.5",
5353
"react-resizable-panels": "^3.0.3",
54-
"react-router-dom": "^7.6.3",
54+
"react-router-dom": "^7.7.1",
5555
"rehype-highlight": "^7.0.2",
5656
"rehype-katex": "^7.0.1",
5757
"remark-gfm": "^4.0.1",
5858
"remark-math": "^6.0.0",
5959
"sonner": "^2.0.6",
6060
"tailwind-merge": "^3.3.1",
6161
"uuid": "^11.1.0",
62-
"vite": "^7.0.4",
63-
"zod": "^4.0.5"
62+
"vite": "^7.0.6",
63+
"zod": "^4.0.11"
6464
},
6565
"devDependencies": {
66-
"@types/node": "^24.0.13",
66+
"@types/node": "^24.1.0",
6767
"@types/pako": "^2.0.3",
6868
"@types/react": "^19.1.8",
6969
"@types/react-dom": "^19.1.6",
7070
"@types/recharts": "^2.0.1",
71-
"@vitejs/plugin-react": "^4.6.0",
72-
"knip": "^5.61.3",
71+
"@vitejs/plugin-react": "^4.7.0",
72+
"knip": "^5.62.0",
7373
"tailwindcss": "^4.1.11",
74-
"tw-animate-css": "^1.3.5"
74+
"tw-animate-css": "^1.3.6"
7575
}
7676
}

src/atoms/chat-atoms.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { SchemaContextBuilder } from "@/lib/schema-context-builder";
1414
import { ConversationAnalyzer } from "@/lib/conversation-analyzer";
1515
import { StorageUtils } from "@/lib/storage-utils";
1616
import { schemaCacheAtom, loadAndCacheTableSchemaAtom } from "./database-atoms";
17+
import { apiUrlAtom } from "./connection-atoms";
18+
import { getCurrentTabData } from "./tab-atoms";
1719
import type {
1820
ChatSession,
1921
AIConnection,
@@ -511,10 +513,11 @@ export const sendMessageAtom = atom(
511513
console.warn("⚠️ No schema cache found");
512514
}
513515

514-
// Get time context from query interface
515-
const timeRange = localStorage.getItem("gigapi_time_range");
516-
const selectedDb = localStorage.getItem("gigapi_selected_db");
517-
const selectedTable = localStorage.getItem("gigapi_selected_table");
516+
// Get time context from current tab
517+
const currentTab = getCurrentTabData();
518+
const timeRange = currentTab?.timeRange ? JSON.stringify(currentTab.timeRange) : null;
519+
const selectedDb = currentTab?.database || "";
520+
const selectedTable = currentTab?.table || "";
518521

519522
// Build query context for artifact intelligence
520523
// Convert schema cache to proper format
@@ -906,10 +909,11 @@ export const regenerateFromMessageAtom = atom(
906909
const globalInstructions = get(globalInstructionsAtom);
907910
let schemaCache = get(schemaCacheAtom);
908911

909-
// Get time context from query interface
910-
const timeRange = localStorage.getItem("gigapi_time_range");
911-
const selectedDb = localStorage.getItem("gigapi_selected_db");
912-
const selectedTable = localStorage.getItem("gigapi_selected_table");
912+
// Get time context from current tab
913+
const currentTab = getCurrentTabData();
914+
const timeRange = currentTab?.timeRange ? JSON.stringify(currentTab.timeRange) : null;
915+
const selectedDb = currentTab?.database || "";
916+
const selectedTable = currentTab?.table || "";
913917

914918
// Build query context for artifact intelligence
915919
const schemaContext = convertSchemaCacheToContext(schemaCache);
@@ -1313,7 +1317,7 @@ Example: When user asks "show me sales by month", respond with:
13131317
const conversationContext =
13141318
ConversationAnalyzer.analyzeConversation(messages);
13151319

1316-
const schemaContextString = SchemaContextBuilder.getSchemaContext(
1320+
const schemaContextString = await SchemaContextBuilder.getSchemaContext(
13171321
messages,
13181322
schemaContext,
13191323
{
@@ -1322,6 +1326,10 @@ Example: When user asks "show me sales by month", respond with:
13221326
summaryOnly: false,
13231327
includeRecentContext: true, // Always include recent context for better continuity
13241328
isAgentic, // Pass agentic mode flag
1329+
// Enhanced with sample data
1330+
includeSampleData: true,
1331+
sampleDataLimit: 5,
1332+
apiUrl: queryContext.apiUrl || "http://localhost:7971/query",
13251333
}
13261334
);
13271335

src/atoms/dashboard-atoms.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -322,17 +322,25 @@ export const addPanelAtom = atom(
322322

323323
// Calculate position for new panel in grid
324324
const existingPanels = dashboard.layout?.panels || [];
325+
326+
// Use provided layout dimensions or defaults
327+
const layoutFromPanelData = (panelData as any).layout;
328+
const width = layoutFromPanelData?.w || 6; // Half width default
329+
const height = layoutFromPanelData?.h || 8; // Default height (8 * 60 = 480px)
330+
const minWidth = layoutFromPanelData?.minW || 2;
331+
const minHeight = layoutFromPanelData?.minH || 2;
332+
325333
const newPanelLayout = {
326334
panelId: panelWithId.id,
327335
x: 0,
328336
y:
329337
existingPanels.length > 0
330338
? Math.max(...existingPanels.map((p) => p.y + p.h))
331339
: 0,
332-
w: 6, // Half width
333-
h: 8, // Default height (8 * 60 = 480px)
334-
minW: 2,
335-
minH: 2,
340+
w: width,
341+
h: height,
342+
minW: minWidth,
343+
minH: minHeight,
336344
};
337345

338346
updatedDashboards[dashboardIndex] = {
@@ -615,13 +623,32 @@ export function useDashboard() {
615623
const panelToDuplicate = panels.find((p) => p.id === panelId);
616624
if (!panelToDuplicate) return;
617625

626+
// Find the layout information for the panel being duplicated
627+
const originalLayout = currentDashboard.layout?.panels?.find(
628+
(layout) => layout.panelId === panelId
629+
);
630+
631+
const newPanelId = uuidv4();
618632
const newPanel = {
619633
...panelToDuplicate,
620-
id: uuidv4(),
634+
id: newPanelId,
621635
title: `${panelToDuplicate.title} (Copy)`,
622636
};
623637

624-
await addPanel({ panelData: newPanel, dashboardId: currentDashboard.id });
638+
// Include layout information if available
639+
const panelDataWithLayout = originalLayout
640+
? {
641+
...newPanel,
642+
layout: {
643+
w: originalLayout.w,
644+
h: originalLayout.h,
645+
minW: originalLayout.minW,
646+
minH: originalLayout.minH,
647+
},
648+
}
649+
: newPanel;
650+
651+
await addPanel({ panelData: panelDataWithLayout, dashboardId: currentDashboard.id });
625652
};
626653

627654
const isPanelLoading = (panelId: string) =>

0 commit comments

Comments
 (0)