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) <noreply@anthropic.com>
This commit is contained in:
parent
ccbe63f9e4
commit
fe6cf014dc
|
|
@ -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<string | null>(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 };
|
||||
|
|
|
|||
Loading…
Reference in New Issue