From ddc5c28ed5ab3a7d09afe1a82fb9d7387d7844fb Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 28 Jan 2026 16:48:03 +0100 Subject: [PATCH 1/4] fix --- src/lib/GraphDataUtils.js | 21 +++++++-------------- src/lib/tests/GraphDataUtils.test.js | 10 +++++----- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/lib/GraphDataUtils.js b/src/lib/GraphDataUtils.js index f5e223655e..79268bdbab 100644 --- a/src/lib/GraphDataUtils.js +++ b/src/lib/GraphDataUtils.js @@ -444,9 +444,7 @@ export function processPieData(data, series, groupByColumn, calculatedValues = n // Apply aggregation to each group Object.keys(groups).forEach(groupKey => { - const labelKey = seriesArray.length > 1 || hasCalculatedValues - ? `${seriesLabel} (${groupKey})` - : groupKey; + const labelKey = `${seriesLabel} (${groupKey})`; aggregatedData[labelKey] = aggregateValues(groups[groupKey], s.aggregationType || 'count'); }); }); @@ -594,9 +592,7 @@ export function processPieData(data, series, groupByColumn, calculatedValues = n // Aggregate each group Object.keys(groups).forEach(groupKey => { - const labelKey = seriesArray.length > 0 || calculatedValues.length > 1 - ? `${calc.name} (${groupKey})` - : groupKey; + const labelKey = `${calc.name} (${groupKey})`; // For percent operator, calculate (sum of numerators / sum of denominators) * 100 if (calc.operator === 'percent' && percentComponents[groupKey]) { @@ -817,11 +813,7 @@ export function processBarLineData(data, xColumn, series, groupByColumn, calcula let groupKeyValue = seriesLabel; // Use series label as default group if (groupByColumn && (Array.isArray(groupByColumn) ? groupByColumn.length > 0 : true)) { const compositeKey = createGroupKey(item, groupByColumn); - groupKeyValue = compositeKey; - // When groupBy is specified, combine with series label for unique series - if (seriesArray.length > 1 || hasCalculatedValues) { - groupKeyValue = `${seriesLabel} (${compositeKey})`; - } + groupKeyValue = `${seriesLabel} (${compositeKey})`; } const groupKey = groupKeyValue; @@ -983,9 +975,10 @@ export function processBarLineData(data, xColumn, series, groupByColumn, calcula const seriesLabel = getSeriesLabel(s, idx); // Map all possible variations of this series' group keys groupKeys.forEach(groupKey => { - // Check if this groupKey is for this series - // It either matches exactly, or starts with "SeriesLabel (" - if (groupKey === seriesLabel || groupKey.startsWith(`${seriesLabel} (`)) { + // Check if this groupKey belongs to this series by matching label or "Label (groupValue)" pattern + const belongsToSeries = groupKey === seriesLabel || groupKey.startsWith(`${seriesLabel} (`); + + if (belongsToSeries) { seriesAggregationMap.set(groupKey, s.aggregationType || 'count'); if (s.chartType) { seriesChartTypeMap.set(groupKey, s.chartType); diff --git a/src/lib/tests/GraphDataUtils.test.js b/src/lib/tests/GraphDataUtils.test.js index 7a98f01c0c..4757853951 100644 --- a/src/lib/tests/GraphDataUtils.test.js +++ b/src/lib/tests/GraphDataUtils.test.js @@ -145,13 +145,13 @@ describe('GraphDataUtils', () => { ]; it('should process pie data with grouping', () => { - const series = [{ fields: ['value'], aggregationType: 'sum' }]; + const series = [{ fields: ['value'], aggregationType: 'sum', title: 'Value' }]; const result = processPieData(mockData, series, 'category'); expect(result).toHaveProperty('labels'); expect(result).toHaveProperty('datasets'); - expect(result.labels).toContain('A'); - expect(result.labels).toContain('B'); + expect(result.labels).toContain('Value (A)'); + expect(result.labels).toContain('Value (B)'); expect(result.datasets[0].data).toContain(30); // A: 10 + 20 expect(result.datasets[0].data).toContain(30); // B: 30 }); @@ -291,9 +291,9 @@ describe('GraphDataUtils', () => { // Labels are category values ('A', 'B') expect(result.datasets[0].data).toEqual([40, 50]); - // Verify labels match the categories from mockData + // Verify labels match the categories from mockData with calc name prefix expect(result.labels).toHaveLength(2); - expect(result.labels).toEqual(['A', 'B']); + expect(result.labels).toEqual(['Profit (A)', 'Profit (B)']); }); }); From 1df68dcae72320ee4c74b569335c47c22503fc6e Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 28 Jan 2026 17:14:51 +0100 Subject: [PATCH 2/4] fix calc value --- src/lib/GraphDataUtils.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/lib/GraphDataUtils.js b/src/lib/GraphDataUtils.js index 79268bdbab..c78c1a00bc 100644 --- a/src/lib/GraphDataUtils.js +++ b/src/lib/GraphDataUtils.js @@ -875,11 +875,7 @@ export function processBarLineData(data, xColumn, series, groupByColumn, calcula let groupKeyValue = calc.name; if (groupByColumn && (Array.isArray(groupByColumn) ? groupByColumn.length > 0 : true)) { const compositeKey = createGroupKey(item, groupByColumn); - groupKeyValue = compositeKey; - // When groupBy is specified, combine with calc name for unique series - if (calculatedValues.length > 1 || seriesArray.length > 0) { - groupKeyValue = `${calc.name} (${compositeKey})`; - } + groupKeyValue = `${calc.name} (${compositeKey})`; } const groupKey = groupKeyValue; From 7e494cd998f9bfafeb4199410a19e5c4cdca33f2 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 28 Jan 2026 17:30:10 +0100 Subject: [PATCH 3/4] fix --- src/lib/GraphDataUtils.js | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/lib/GraphDataUtils.js b/src/lib/GraphDataUtils.js index c78c1a00bc..bde4faa5c5 100644 --- a/src/lib/GraphDataUtils.js +++ b/src/lib/GraphDataUtils.js @@ -971,8 +971,20 @@ export function processBarLineData(data, xColumn, series, groupByColumn, calcula const seriesLabel = getSeriesLabel(s, idx); // Map all possible variations of this series' group keys groupKeys.forEach(groupKey => { - // Check if this groupKey belongs to this series by matching label or "Label (groupValue)" pattern - const belongsToSeries = groupKey === seriesLabel || groupKey.startsWith(`${seriesLabel} (`); + // Check if this groupKey belongs to this series + // Must handle edge case where seriesLabel contains " (" (e.g., "Foo (bar)") + let belongsToSeries = false; + if (groupKey === seriesLabel) { + // Exact match (no groupBy case) + belongsToSeries = true; + } else if (groupKey.endsWith(')')) { + // Check if it's seriesLabel with groupBy suffix by finding the LAST " (" + const lastParenIndex = groupKey.lastIndexOf(' ('); + if (lastParenIndex > 0) { + const prefix = groupKey.substring(0, lastParenIndex); + belongsToSeries = prefix === seriesLabel; + } + } if (belongsToSeries) { seriesAggregationMap.set(groupKey, s.aggregationType || 'count'); @@ -1012,9 +1024,22 @@ export function processBarLineData(data, xColumn, series, groupByColumn, calcula if (calc.name && calc.operator) { // Map all possible variations of this calculated value's group keys groupKeys.forEach(groupKey => { - // Check if this groupKey is for this calculated value - // It either matches exactly, or starts with "CalcName (" - if (groupKey === calc.name || groupKey.startsWith(`${calc.name} (`)) { + // Check if this groupKey belongs to this calculated value + // Must handle edge case where calc.name contains " (" (e.g., "Total (adj)") + let belongsToCalc = false; + if (groupKey === calc.name) { + // Exact match (no groupBy case) + belongsToCalc = true; + } else if (groupKey.endsWith(')')) { + // Check if it's calc.name with groupBy suffix by finding the LAST " (" + const lastParenIndex = groupKey.lastIndexOf(' ('); + if (lastParenIndex > 0) { + const prefix = groupKey.substring(0, lastParenIndex); + belongsToCalc = prefix === calc.name; + } + } + + if (belongsToCalc) { calcValueOperatorMap.set(groupKey, calc.operator); if (calc.chartType) { calcValueChartTypeMap.set(groupKey, calc.chartType); From 4bf809dc366739ffba2d3851b9c5d3395253cc8e Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 28 Jan 2026 17:36:29 +0100 Subject: [PATCH 4/4] fix group by --- .../Data/Browser/GraphDialog.react.js | 4 +-- src/lib/GraphDataUtils.js | 30 +++---------------- 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/src/dashboard/Data/Browser/GraphDialog.react.js b/src/dashboard/Data/Browser/GraphDialog.react.js index cf90893888..f06b4c7fde 100644 --- a/src/dashboard/Data/Browser/GraphDialog.react.js +++ b/src/dashboard/Data/Browser/GraphDialog.react.js @@ -484,7 +484,7 @@ export default class GraphDialog extends React.Component {
this.updateSeries(index, 'title', title)} + onChange={title => this.updateSeries(index, 'title', title.replace(/[()]/g, ''))} placeholder="Auto-generated from fields" />
@@ -707,7 +707,7 @@ export default class GraphDialog extends React.Component {
this.updateCalculatedValue(index, 'name', name)} + onChange={name => this.updateCalculatedValue(index, 'name', name.replace(/[()]/g, ''))} placeholder="Enter name" />
diff --git a/src/lib/GraphDataUtils.js b/src/lib/GraphDataUtils.js index bde4faa5c5..82bc6abcc0 100644 --- a/src/lib/GraphDataUtils.js +++ b/src/lib/GraphDataUtils.js @@ -972,19 +972,8 @@ export function processBarLineData(data, xColumn, series, groupByColumn, calcula // Map all possible variations of this series' group keys groupKeys.forEach(groupKey => { // Check if this groupKey belongs to this series - // Must handle edge case where seriesLabel contains " (" (e.g., "Foo (bar)") - let belongsToSeries = false; - if (groupKey === seriesLabel) { - // Exact match (no groupBy case) - belongsToSeries = true; - } else if (groupKey.endsWith(')')) { - // Check if it's seriesLabel with groupBy suffix by finding the LAST " (" - const lastParenIndex = groupKey.lastIndexOf(' ('); - if (lastParenIndex > 0) { - const prefix = groupKey.substring(0, lastParenIndex); - belongsToSeries = prefix === seriesLabel; - } - } + // Series/calc names cannot contain parentheses, so startsWith is safe + const belongsToSeries = groupKey === seriesLabel || groupKey.startsWith(`${seriesLabel} (`); if (belongsToSeries) { seriesAggregationMap.set(groupKey, s.aggregationType || 'count'); @@ -1025,19 +1014,8 @@ export function processBarLineData(data, xColumn, series, groupByColumn, calcula // Map all possible variations of this calculated value's group keys groupKeys.forEach(groupKey => { // Check if this groupKey belongs to this calculated value - // Must handle edge case where calc.name contains " (" (e.g., "Total (adj)") - let belongsToCalc = false; - if (groupKey === calc.name) { - // Exact match (no groupBy case) - belongsToCalc = true; - } else if (groupKey.endsWith(')')) { - // Check if it's calc.name with groupBy suffix by finding the LAST " (" - const lastParenIndex = groupKey.lastIndexOf(' ('); - if (lastParenIndex > 0) { - const prefix = groupKey.substring(0, lastParenIndex); - belongsToCalc = prefix === calc.name; - } - } + // Calc names cannot contain parentheses, so startsWith is safe + const belongsToCalc = groupKey === calc.name || groupKey.startsWith(`${calc.name} (`); if (belongsToCalc) { calcValueOperatorMap.set(groupKey, calc.operator);