fix: critical data mapping bugs in useImportData and useCreateChart

- useImportData: use dataset_id instead of id from backend response,
  fetch rows after import, fix column type mapping
- useCreateChart: fix bindings mapper to use axis instead of fieldRole,
  graceful error handling instead of throwing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hailin 2026-04-05 01:43:16 -07:00
parent 0c0592b511
commit 28ebbbfb82
2 changed files with 27 additions and 28 deletions

View File

@ -8,17 +8,15 @@ import { addLayoutItem } from '@/adapters/state/redux/layoutSlice';
import { type ChartType } from '@/domain'; import { type ChartType } from '@/domain';
import { type ChartInstance } from '@/domain/entities/ChartInstance'; import { type ChartInstance } from '@/domain/entities/ChartInstance';
/**
* Map a backend chart object (snake_case) to frontend ChartInstance (camelCase).
*/
function mapBackendChart(raw: any): ChartInstance { function mapBackendChart(raw: any): ChartInstance {
return { return {
id: raw.id, id: raw.id,
type: raw.chart_type ?? raw.type, type: raw.chart_type ?? raw.type,
dataSetId: raw.dataset_id ?? raw.dataSetId, dataSetId: raw.dataset_id ?? raw.dataSetId,
bindings: (raw.bindings ?? []).map((b: any) => ({ bindings: (raw.bindings ?? []).map((b: any) => ({
fieldRole: b.field_role ?? b.fieldRole, axis: b.axis ?? b.field_role ?? b.fieldRole,
columnName: b.column_name ?? b.columnName, columnName: b.column_name ?? b.columnName,
aggregation: b.aggregation,
})), })),
style: raw.style ?? {}, style: raw.style ?? {},
filters: (raw.filters ?? []).map((f: any) => ({ filters: (raw.filters ?? []).map((f: any) => ({
@ -43,7 +41,8 @@ export function useCreateChart() {
const handleCreate = useCallback( const handleCreate = useCallback(
async (chartType?: ChartType) => { async (chartType?: ChartType) => {
if (!activeDataSetId) { if (!activeDataSetId) {
throw new Error('No active dataset. Import data first.'); setError('请先导入数据集');
return;
} }
setLoading(true); setLoading(true);
@ -58,7 +57,6 @@ export function useCreateChart() {
dispatch(addChart(chart)); dispatch(addChart(chart));
dispatch(setActiveChart(chart.id)); dispatch(setActiveChart(chart.id));
// Default position: stack items in a 2-column grid
const col = layoutCount % 2; const col = layoutCount % 2;
const row = Math.floor(layoutCount / 2); const row = Math.floor(layoutCount / 2);
dispatch( dispatch(
@ -74,7 +72,6 @@ export function useCreateChart() {
return chart; return chart;
} catch (e: any) { } catch (e: any) {
setError(e.message ?? 'Failed to create chart'); setError(e.message ?? 'Failed to create chart');
throw e;
} finally { } finally {
setLoading(false); setLoading(false);
} }

View File

@ -8,40 +8,34 @@ import { type DataSet } from '@/domain/entities/DataSet';
import { type ChartSuggestion } from '@/application/dto/ChartSuggestion'; import { type ChartSuggestion } from '@/application/dto/ChartSuggestion';
/** /**
* Map a backend dataset object (snake_case) to the frontend DataSet entity (camelCase). * Map a backend import result (snake_case) to frontend DataSet entity.
* The import endpoint returns {dataset_id, file_name, columns, row_count, ...}
* We then fetch the full dataset (with rows) from GET /datasets/{id}.
*/ */
function mapBackendDataSet(raw: any): DataSet { function mapBackendDataSet(raw: any, rows: Record<string, any>[] = []): DataSet {
return { return {
id: raw.id, id: raw.dataset_id ?? raw.id ?? '',
fileName: raw.file_name ?? raw.fileName ?? '', fileName: raw.file_name ?? raw.fileName ?? '',
sheetName: raw.sheet_name ?? raw.sheetName, sheetName: raw.sheet_name ?? raw.sheetName,
columns: (raw.columns ?? []).map((col: any) => ({ columns: (raw.columns ?? []).map((col: any) => ({
name: col.name, name: col.name,
type: col.type ?? col.field_type ?? 'string', type: col.field_type ?? col.type ?? 'text',
sampleValues: col.sample_values ?? col.sampleValues ?? [], sampleValues: col.sample_values ?? col.sampleValues ?? [],
})), })),
rows: raw.rows ?? [], rows,
rowCount: raw.row_count ?? raw.rowCount ?? (raw.rows?.length ?? 0), rowCount: raw.row_count ?? raw.rowCount ?? rows.length,
dataStructure: raw.data_structure ?? raw.dataStructure, dataStructure: raw.data_structure ?? raw.dataStructure,
createdAt: raw.created_at ?? raw.createdAt ?? new Date().toISOString(), createdAt: raw.created_at ?? raw.createdAt ?? new Date().toISOString(),
updatedAt: raw.updated_at ?? raw.updatedAt ?? new Date().toISOString(), updatedAt: raw.updated_at ?? raw.updatedAt ?? new Date().toISOString(),
}; };
} }
/**
* Map a backend chart recommendation (snake_case) to frontend ChartSuggestion.
*/
function mapBackendSuggestion(raw: any): ChartSuggestion { function mapBackendSuggestion(raw: any): ChartSuggestion {
return { return {
chartType: raw.chart_type ?? raw.chartType, chartType: raw.chart_type ?? raw.chartType,
label: raw.label ?? raw.chart_type ?? '', label: raw.label ?? raw.chart_type ?? '',
isPrimary: raw.is_primary ?? raw.isPrimary ?? false, isPrimary: raw.primary ?? raw.is_primary ?? raw.isPrimary ?? false,
defaultBindings: (raw.default_bindings ?? raw.defaultBindings ?? []).map( defaultBindings: [],
(b: any) => ({
fieldRole: b.field_role ?? b.fieldRole,
columnName: b.column_name ?? b.columnName,
}),
),
}; };
} }
@ -58,21 +52,29 @@ export function useImportData() {
try { try {
// Upload file to backend data-service // Upload file to backend data-service
const rawDatasets = await dataServiceClient.importFile(file); const rawDatasets = await dataServiceClient.importFile(file);
// The backend may return one or more datasets; take the first
const rawDataSet = Array.isArray(rawDatasets) ? rawDatasets[0] : rawDatasets; const rawDataSet = Array.isArray(rawDatasets) ? rawDatasets[0] : rawDatasets;
const dataSet = mapBackendDataSet(rawDataSet); const dataSetId = rawDataSet.dataset_id ?? rawDataSet.id;
// Fetch rows from backend
let rows: Record<string, any>[] = [];
try {
const rowsResp = await dataServiceClient.getRows(dataSetId, 10000);
rows = rowsResp.rows ?? rowsResp ?? [];
} catch {
// rows fetch failed, continue without
}
const dataSet = mapBackendDataSet(rawDataSet, rows);
dispatch(addDataSet(dataSet)); dispatch(addDataSet(dataSet));
dispatch(setActiveDataSet(dataSet.id)); dispatch(setActiveDataSet(dataSet.id));
// Request chart suggestions from backend chart-service // Request chart suggestions
try { try {
const rawSuggestions = await chartServiceClient.recommendCharts(dataSet.id); const rawSuggestions = await chartServiceClient.recommendCharts(dataSet.id);
const mapped = (rawSuggestions ?? []).map(mapBackendSuggestion); const mapped = (rawSuggestions ?? []).map(mapBackendSuggestion);
setSuggestions(mapped); setSuggestions(mapped);
} catch { } catch {
// Non-critical: recommendations are optional
setSuggestions([]); setSuggestions([]);
} }