Skip to content

Commit 5d67f3c

Browse files
rajbosCopilot
andcommitted
feat(dashboard): add chart period selector and log-scale toggle
Add two new controls to the Token Usage Trend chart: - Chart period selector: Last 7 days / Last 30 days / All Filters CHART_DATA client-side so recent days are visible at proper scale when a single past day had a very large session. - Log-scale toggle: Linear / Log Switches Y-axis to logarithmic scale, making bars of vastly different magnitudes all visible simultaneously. Also refactor rebuildChart() to filter via getChartData() before re-aggregating, so the period selection and view (day/week/month) work together correctly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent f9e8934 commit 5d67f3c

1 file changed

Lines changed: 72 additions & 8 deletions

File tree

sharing-server/src/routes/dashboard.ts

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,11 @@ function dashboardPage(user: UserRow, uploads: UploadRow[], isAdmin: boolean, al
440440
<div class="card-header">
441441
<h3>Token Usage Trend</h3>
442442
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
443+
<div class="tabs" id="chart-period-tabs">
444+
<button class="tab" data-chart-period="7">Last 7 days</button>
445+
<button class="tab active" data-chart-period="30">Last 30 days</button>
446+
<button class="tab" data-chart-period="0">All</button>
447+
</div>
443448
<div class="tabs" id="group-tabs">
444449
<button class="tab active" data-group="model">By Model</button>
445450
<button class="tab" data-group="editor">By Editor</button>
@@ -449,6 +454,10 @@ function dashboardPage(user: UserRow, uploads: UploadRow[], isAdmin: boolean, al
449454
<button class="tab" data-view="week">Week</button>
450455
<button class="tab" data-view="month">Month</button>
451456
</div>
457+
<div class="tabs" id="scale-tabs">
458+
<button class="tab active" data-scale="linear">Linear</button>
459+
<button class="tab" data-scale="log">Log</button>
460+
</div>
452461
</div>
453462
</div>
454463
<div class="chart-wrap"><canvas id="trend-chart"></canvas></div>
@@ -601,7 +610,37 @@ function dashboardPage(user: UserRow, uploads: UploadRow[], isAdmin: boolean, al
601610
.filter(function(ds) { return ds.data.some(function(v) { return v > 0; }); });
602611
}
603612
604-
var currentGroup = 'model', currentView = 'day';
613+
var currentGroup = 'model', currentView = 'day', currentChartDays = 30, currentScale = 'linear';
614+
615+
function getChartData() {
616+
if (currentChartDays === 0) { return CHART_DATA; }
617+
var cutoff = new Date();
618+
cutoff.setUTCDate(cutoff.getUTCDate() - (currentChartDays - 1));
619+
var cutoffStr = cutoff.toISOString().slice(0, 10);
620+
return CHART_DATA.filter(function(r) { return r.day >= cutoffStr; });
621+
}
622+
623+
function makeYAxisConfig() {
624+
var isLog = currentScale === 'log';
625+
return {
626+
stacked: !isLog,
627+
type: isLog ? 'logarithmic' : 'linear',
628+
grid: { color: '#21262d' },
629+
ticks: {
630+
color: '#8b949e', font: { size: 11 },
631+
callback: function(v) {
632+
// Show only "round" log-scale ticks
633+
if (isLog) {
634+
var log = Math.log10(v);
635+
if (Math.abs(log - Math.round(log)) > 0.01) { return null; }
636+
}
637+
return v >= 1000 ? (v/1000).toFixed(1)+'M' : v+'K';
638+
},
639+
},
640+
title: { display: true, text: 'Tokens (K)', color: '#8b949e', font: { size: 11 } },
641+
};
642+
}
643+
605644
var grouped = aggregate(function(d) { return d; }, currentGroup);
606645
var labels = [...new Set(CHART_DATA.map(function(r) { return r.day; }))].sort();
607646
@@ -614,11 +653,7 @@ function dashboardPage(user: UserRow, uploads: UploadRow[], isAdmin: boolean, al
614653
interaction: { mode: 'index', intersect: false },
615654
scales: {
616655
x: { stacked: true, grid: { color: '#21262d' }, ticks: { color: '#8b949e', maxTicksLimit: 16, font: { size: 11 } } },
617-
y: { stacked: true, grid: { color: '#21262d' },
618-
ticks: { color: '#8b949e', font: { size: 11 },
619-
callback: function(v) { return v >= 1000 ? (v/1000).toFixed(1)+'M' : v+'K'; } },
620-
title: { display: true, text: 'Tokens (K)', color: '#8b949e', font: { size: 11 } },
621-
},
656+
y: makeYAxisConfig(),
622657
},
623658
plugins: {
624659
legend: { position: 'bottom', labels: { color: '#c9d1d9', boxWidth: 11, padding: 14, font: { size: 11 } } },
@@ -640,16 +675,45 @@ function dashboardPage(user: UserRow, uploads: UploadRow[], isAdmin: boolean, al
640675
});
641676
642677
function rebuildChart() {
678+
var filteredData = getChartData();
643679
var keyFn = currentView === 'week' ? toWeekStart : currentView === 'month' ? toMonth : function(d) { return d; };
644-
grouped = aggregate(keyFn, currentGroup);
645-
labels = [...new Set(CHART_DATA.map(function(r) { return keyFn(r.day); }))].sort();
680+
// Re-aggregate using filtered data
681+
var filteredMap = {};
682+
filteredData.forEach(function(r) {
683+
var label = keyFn(r.day);
684+
var dim = currentGroup === 'editor' ? r.editor : r.model;
685+
if (!filteredMap[label]) filteredMap[label] = {};
686+
filteredMap[label][dim] = (filteredMap[label][dim] || 0) + r.inputTokens + r.outputTokens;
687+
});
688+
grouped = filteredMap;
689+
labels = [...new Set(filteredData.map(function(r) { return keyFn(r.day); }))].sort();
646690
var dims = currentGroup === 'editor' ? allEditors : allModels;
647691
var colorFn = currentGroup === 'editor' ? getEditorColor : getModelColor;
648692
chart.data.labels = labels;
649693
chart.data.datasets = buildDatasets(grouped, labels, dims, colorFn);
694+
chart.options.scales.x.stacked = currentScale !== 'log';
695+
chart.options.scales.y = makeYAxisConfig();
650696
chart.update();
651697
}
652698
699+
document.querySelectorAll('#chart-period-tabs .tab').forEach(function(btn) {
700+
btn.addEventListener('click', function() {
701+
document.querySelectorAll('#chart-period-tabs .tab').forEach(function(b) { b.classList.remove('active'); });
702+
btn.classList.add('active');
703+
currentChartDays = parseInt(btn.getAttribute('data-chart-period'), 10);
704+
rebuildChart();
705+
});
706+
});
707+
708+
document.querySelectorAll('#scale-tabs .tab').forEach(function(btn) {
709+
btn.addEventListener('click', function() {
710+
document.querySelectorAll('#scale-tabs .tab').forEach(function(b) { b.classList.remove('active'); });
711+
btn.classList.add('active');
712+
currentScale = btn.getAttribute('data-scale');
713+
rebuildChart();
714+
});
715+
});
716+
653717
document.querySelectorAll('#group-tabs .tab').forEach(function(btn) {
654718
btn.addEventListener('click', function() {
655719
document.querySelectorAll('#group-tabs .tab').forEach(function(b) { b.classList.remove('active'); });

0 commit comments

Comments
 (0)