Skip to content

Commit 9f56eda

Browse files
committed
fix: align dashboard spend with Cursor billed totals (/teams/spend)
Scale usage-event series to spending.spend_cents for user/team charts and rankings. User KPIs use cycle spend and cycle-scoped agent requests. Made-with: Cursor
1 parent b96da79 commit 9f56eda

3 files changed

Lines changed: 426 additions & 332 deletions

File tree

src/app/users/[email]/user-detail-client.tsx

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -193,14 +193,23 @@ export function UserDetailClient({ email, stats }: UserDetailClientProps) {
193193
const currentSpend = stats.spending[0];
194194
const activityDays = stats.dailyActivity.length;
195195
const totalAgentRequests = stats.dailyActivity.reduce((sum, d) => sum + d.agent_requests, 0);
196-
const totalSpendCents = stats.dailySpend.reduce((s, d) => s + d.spend_cents, 0);
197-
const totalSpendDollars = totalSpendCents / 100;
196+
const cycleStart = currentSpend?.cycle_start;
197+
const cycleAgentRequests = cycleStart
198+
? stats.dailyActivity
199+
.filter((d) => d.date >= cycleStart)
200+
.reduce((sum, d) => sum + d.agent_requests, 0)
201+
: totalAgentRequests;
202+
const billedCycleCents = currentSpend?.spend_cents ?? 0;
203+
const cycleSpendDollars = billedCycleCents / 100;
198204
const dollarsPerReq =
199-
totalAgentRequests > 0 ? (totalSpendCents / totalAgentRequests / 100).toFixed(2) : null;
200-
const dailyAvgSpend =
201-
stats.dailySpend.length > 0 ? totalSpendDollars / stats.dailySpend.length : 0;
202-
const cycleSpendDollars =
203-
totalSpendDollars || (currentSpend ? currentSpend.spend_cents / 100 : 0);
205+
cycleAgentRequests > 0 ? (billedCycleCents / cycleAgentRequests / 100).toFixed(2) : null;
206+
const cycleDaysElapsed = cycleStart
207+
? Math.max(
208+
1,
209+
Math.ceil((Date.now() - new Date(`${cycleStart}T12:00:00Z`).getTime()) / 86_400_000),
210+
)
211+
: Math.max(1, stats.dailySpend.length);
212+
const dailyAvgSpend = cycleSpendDollars / cycleDaysElapsed;
204213

205214
const mergedDailyData = buildMergedDailyData(stats.dailySpend, stats.dailyActivity);
206215
const hasUsageEvents = stats.usageEventsSummary.length > 0;
@@ -297,6 +306,7 @@ export function UserDetailClient({ email, stats }: UserDetailClientProps) {
297306
<div className="grid grid-cols-1 lg:grid-cols-2 gap-3">
298307
<ExpandableCard>
299308
<SpendTrendChart
309+
avgPerDay={dailyAvgSpend}
300310
data={stats.dailySpend.map((d) => {
301311
const activity = stats.dailyActivity.find((a) => a.date === d.date);
302312
return {
@@ -318,6 +328,9 @@ export function UserDetailClient({ email, stats }: UserDetailClientProps) {
318328
<div className="bg-zinc-900 rounded-lg border border-zinc-800 overflow-hidden">
319329
<div className="px-4 py-2.5 border-b border-zinc-800">
320330
<h3 className="text-xs font-medium text-zinc-400">Cost Breakdown</h3>
331+
<p className="text-[10px] text-zinc-600 mt-0.5">
332+
Dollar totals match billed cycle spend; split by model follows usage-event mix
333+
</p>
321334
</div>
322335
<div className="overflow-x-auto max-h-[300px] overflow-y-auto">
323336
<table className="w-full text-xs">

src/components/charts/spend-trend-chart.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export function SpendTrendChart({ data, selectedDays, avgPerDay }: SpendTrendCha
104104
)}
105105
{provisionalStart && (
106106
<span className="text-[10px] text-amber-500/70 ml-auto">
107-
Last {provisionalDays}d may be partial (API lag)
107+
Last {provisionalDays}d may be partial · daily shape from events, totals match billing
108108
</span>
109109
)}
110110
</div>

0 commit comments

Comments
 (0)