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:
parent
0c0592b511
commit
28ebbbfb82
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue