diff --git a/dashboard/public/index.html b/dashboard/public/index.html index cbc1ded..c48ab25 100644 --- a/dashboard/public/index.html +++ b/dashboard/public/index.html @@ -318,6 +318,7 @@ +
@@ -562,32 +563,95 @@ let seriesNames = [...new Set(data.map(r => r[1]))]; seriesNames.forEach((name, idx) => { const hue = (idx * 137.5) % 360; - datasets.push({ - label: name, - data: labels.map(l => { - const row = data.find(r => r[0] === l && r[1] === name); - return row ? row[2] : 0; - }), - backgroundColor: `hsla(${hue}, 75%, 50%, 0.7)`, - borderColor: `hsla(${hue}, 75%, 50%, 1)`, - borderWidth: 2, - fill: currentChartType === 'line', - tension: 0.3 - }); + + if (y === 'all') { + // Dual axis for breakdown + // Volume Dataset + datasets.push({ + label: `${name} (Vol)`, + data: labels.map(l => { + const row = data.find(r => r[0] === l && r[1] === name); + return row ? row[3] : 0; // value_volume is index 3 + }), + backgroundColor: `hsla(${hue}, 75%, 50%, 0.7)`, + borderColor: `hsla(${hue}, 75%, 50%, 1)`, + borderWidth: 2, + yAxisID: 'y', + type: 'bar' + }); + // Count Dataset + datasets.push({ + label: `${name} (Cnt)`, + data: labels.map(l => { + const row = data.find(r => r[0] === l && r[1] === name); + return row ? row[2] : 0; // value_count is index 2 + }), + backgroundColor: `hsla(${hue}, 75%, 30%, 0.7)`, + borderColor: `hsla(${hue}, 75%, 30%, 1)`, + borderWidth: 2, + yAxisID: 'y1', + type: 'line', // Always line for count to distinguish + pointStyle: 'rectRot' + }); + } else { + // Standard single metric breakdown + datasets.push({ + label: name, + data: labels.map(l => { + const row = data.find(r => r[0] === l && r[1] === name); + return row ? row[2] : 0; + }), + backgroundColor: `hsla(${hue}, 75%, 50%, 0.7)`, + borderColor: `hsla(${hue}, 75%, 50%, 1)`, + borderWidth: 2, + fill: currentChartType === 'line', + tension: 0.3, + yAxisID: 'y' + }); + } }); } else { - datasets.push({ - label: y.toUpperCase(), - data: labels.map(l => { - const row = data.find(r => r[0] === l); - return row ? row[1] : 0; - }), - backgroundColor: '#38bdf866', - borderColor: '#38bdf8', - borderWidth: 2, - fill: currentChartType === 'line', - tension: 0.3 - }); + if (y === 'all') { + // Standard dual axis without breakdown + datasets.push({ + label: 'Volume (€)', + data: labels.map(l => { + const row = data.find(r => r[0] === l); + return row ? row[2] : 0; // value_volume + }), + backgroundColor: '#38bdf866', + borderColor: '#38bdf8', + borderWidth: 2, + yAxisID: 'y', + type: 'bar' + }); + datasets.push({ + label: 'Count', + data: labels.map(l => { + const row = data.find(r => r[0] === l); + return row ? row[1] : 0; // value_count + }), + backgroundColor: '#fbbf2466', + borderColor: '#fbbf24', + borderWidth: 2, + yAxisID: 'y1', + type: 'line' + }); + } else { + datasets.push({ + label: y.toUpperCase(), + data: labels.map(l => { + const row = data.find(r => r[0] === l); + return row ? row[1] : 0; + }), + backgroundColor: '#38bdf866', + borderColor: '#38bdf8', + borderWidth: 2, + fill: currentChartType === 'line', + tension: 0.3, + yAxisID: 'y' + }); + } } if (charts.analytics) charts.analytics.destroy(); @@ -599,9 +663,22 @@ animation: { duration: 800, easing: 'easeOutQuart' }, scales: { y: { + type: 'linear', + display: true, + position: 'left', stacked: false, grid: { color: 'rgba(255,255,255,0.05)' }, - ticks: { color: '#64748b' } + ticks: { color: '#64748b' }, + title: { display: true, text: 'Volume (€)', color: '#38bdf8' } + }, + y1: { + type: 'linear', + display: y === 'all', + position: 'right', + stacked: false, + grid: { drawOnChartArea: false }, // only want the grid lines for one axis to show up + ticks: { color: '#fbbf24' }, + title: { display: true, text: 'Trade Count', color: '#fbbf24' } }, x: { stacked: false,