diff --git a/frontend/src/adapters/gateways/EChartsOptionBuilder/barOptionBuilder.ts b/frontend/src/adapters/gateways/EChartsOptionBuilder/barOptionBuilder.ts index 89c4821..e4ba8cf 100644 --- a/frontend/src/adapters/gateways/EChartsOptionBuilder/barOptionBuilder.ts +++ b/frontend/src/adapters/gateways/EChartsOptionBuilder/barOptionBuilder.ts @@ -149,16 +149,21 @@ export function buildBarOption( const label = buildLabelConfig(style); - if ((isGrouped || isStacked) && seriesBinding) { - const seriesField = seriesBinding.columnName; - const seriesNames = [...new Set(data.map((row) => String(row[seriesField])))]; + const colorBinding = getBinding(bindings, 'color'); + const splitField = seriesBinding?.columnName ?? colorBinding?.columnName; + const hasSplit = !!splitField; - const seriesList = seriesNames.map((name, idx) => { + if (hasSplit) { + // Multi-series: split by series or color field + const splitNames = [...new Set(data.map((row) => String(row[splitField])))]; + + const seriesList = splitNames.map((name, idx) => { const seriesData = categories.map((cat) => { - const row = data.find( - (r) => String(r[xField]) === cat && String(r[seriesField]) === name, + // Sum all matching rows (there may be multiple) + const matchingRows = data.filter( + (r) => String(r[xField]) === cat && String(r[splitField]) === name, ); - return row ? Number(row[yField]) : 0; + return matchingRows.reduce((sum, r) => sum + (Number(r[yField]) || 0), 0); }); return { @@ -183,9 +188,10 @@ export function buildBarOption( option.series = seriesList; option.tooltip = { trigger: 'axis' }; } else { + // Single series: aggregate by x category const seriesData = categories.map((cat) => { - const row = data.find((r) => String(r[xField]) === cat); - return row ? Number(row[yField]) : 0; + const matchingRows = data.filter((r) => String(r[xField]) === cat); + return matchingRows.reduce((sum, r) => sum + (Number(r[yField]) || 0), 0); }); if (isHorizontal) {