From fe6cf014dc6e066f09d9c00d1e5f702bad8b1438 Mon Sep 17 00:00:00 2001 From: hailin Date: Sun, 5 Apr 2026 01:57:54 -0700 Subject: [PATCH] feat: auto-bind chart fields based on dataset columns When creating a chart, automatically assign first text column to X axis and first number column to Y axis (varies by chart type). Charts now show data immediately instead of blank. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/frameworks/hooks/useCreateChart.ts | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/frontend/src/frameworks/hooks/useCreateChart.ts b/frontend/src/frameworks/hooks/useCreateChart.ts index b134c47..ca81583 100644 --- a/frontend/src/frameworks/hooks/useCreateChart.ts +++ b/frontend/src/frameworks/hooks/useCreateChart.ts @@ -7,6 +7,7 @@ import { addChart, setActiveChart } from '@/adapters/state/redux/chartSlice'; import { addLayoutItem } from '@/adapters/state/redux/layoutSlice'; import { type ChartType } from '@/domain'; import { type ChartInstance } from '@/domain/entities/ChartInstance'; +import { type FieldBinding } from '@/domain/entities/FieldBinding'; function mapBackendChart(raw: any): ChartInstance { return { @@ -31,9 +32,58 @@ function mapBackendChart(raw: any): ChartInstance { }; } +/** + * Auto-generate default bindings based on chart type and available columns. + */ +function autoBindings( + chartType: string, + columns: { name: string; type: string }[], +): FieldBinding[] { + const textCols = columns.filter((c) => c.type === 'text' || c.type === 'geo'); + const numCols = columns.filter((c) => c.type === 'number' || c.type === 'percentage'); + const dateCols = columns.filter((c) => c.type === 'date'); + + const bindings: FieldBinding[] = []; + + switch (chartType) { + case 'pie': + case 'donut': + case 'wordcloud': + case 'radar': + if (textCols[0]) bindings.push({ axis: 'label', columnName: textCols[0].name }); + if (numCols[0]) bindings.push({ axis: 'value', columnName: numCols[0].name }); + break; + case 'map': + const geoCols = columns.filter((c) => c.type === 'geo'); + if (geoCols[0]) bindings.push({ axis: 'geo', columnName: geoCols[0].name }); + else if (textCols[0]) bindings.push({ axis: 'geo', columnName: textCols[0].name }); + if (numCols[0]) bindings.push({ axis: 'value', columnName: numCols[0].name }); + break; + case 'kpi': + if (numCols[0]) bindings.push({ axis: 'value', columnName: numCols[0].name }); + if (textCols[0]) bindings.push({ axis: 'label', columnName: textCols[0].name }); + break; + case 'scatter': + case 'boston-matrix': + if (numCols[0]) bindings.push({ axis: 'x', columnName: numCols[0].name }); + if (numCols[1]) bindings.push({ axis: 'y', columnName: numCols[1].name }); + break; + default: // bar, line, area, grouped-bar, stacked-bar, horizontal-bar, combo, heatmap, data-table + if (dateCols[0]) bindings.push({ axis: 'x', columnName: dateCols[0].name }); + else if (textCols[0]) bindings.push({ axis: 'x', columnName: textCols[0].name }); + if (numCols[0]) bindings.push({ axis: 'y', columnName: numCols[0].name }); + break; + } + + return bindings; +} + export function useCreateChart() { const dispatch = useAppDispatch(); const activeDataSetId = useAppSelector((s) => s.data.activeDataSetId); + const activeDataSet = useAppSelector((s) => + s.data.dataSets.find((ds) => ds.id === s.data.activeDataSetId), + ); const layoutCount = useAppSelector((s) => s.layout.layouts.length); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); @@ -48,12 +98,27 @@ export function useCreateChart() { setLoading(true); setError(null); try { + // Auto-generate bindings from dataset columns + const columns = (activeDataSet?.columns ?? []).map((c) => ({ + name: c.name, + type: c.type, + })); + const defaultBindings = autoBindings(chartType ?? 'bar', columns); + const rawChart = await chartServiceClient.createChart( activeDataSetId, chartType ?? 'bar', + defaultBindings.map((b) => ({ + axis: b.axis, + column_name: b.columnName, + aggregation: b.aggregation ?? null, + })), ); const chart = mapBackendChart(rawChart); + // Override bindings with our auto-generated ones (backend may not have them) + chart.bindings = defaultBindings; + dispatch(addChart(chart)); dispatch(setActiveChart(chart.id)); @@ -76,7 +141,7 @@ export function useCreateChart() { setLoading(false); } }, - [dispatch, activeDataSetId, layoutCount], + [dispatch, activeDataSetId, activeDataSet, layoutCount], ); return { handleCreate, activeDataSetId, loading, error };