Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export function SpendEvidenceCard({
'90d': 'Last 90 days',
}[range];
const highest = evidence
.filter(point => point.coverage === 'complete')
.filter(point => point.coverage !== 'unavailable')
.reduce<(typeof evidence)[number] | undefined>((currentHighest, point) => {
if (!currentHighest) return point;
const currentTotal = currentHighest.variableUsd + currentHighest.scheduledUsd;
Expand Down Expand Up @@ -105,7 +105,7 @@ export function SpendEvidenceCard({
? `${rangeLabel}: spend total unavailable because one or more periods have incomplete coverage.`
: `${rangeLabel}: ${money(completeTotal)} total.`}{' '}
{highest
? `Highest complete period was ${highest.label} at ${money(highest.variableUsd + highest.scheduledUsd)}.`
? `Highest period with spend evidence was ${highest.label} at ${money(highest.variableUsd + highest.scheduledUsd)}.`
: ''}
</p>
<p id={chartInstructionsId} className="sr-only">
Expand Down Expand Up @@ -135,9 +135,9 @@ export function SpendEvidenceCard({
const isPeak = highest !== undefined && point.periodStart === highest.periodStart;
const showTick = index % tickStride === 0 || index === evidence.length - 1;
const accessibilityLabel =
point.coverage === 'complete'
? `${point.label}: ${money(pointTotal)} total, ${money(point.variableUsd)} usage-based, ${money(point.scheduledUsd)} scheduled`
: `${point.label}: spend data unavailable, ${point.coveredHours} of ${point.totalHours} hours covered`;
point.coverage === 'unavailable'
? `${point.label}: spend data unavailable, ${point.coveredHours} of ${point.totalHours} hours covered`
: `${point.label}: ${point.coverage === 'partial' ? 'at least ' : ''}${money(pointTotal)} total, ${money(point.variableUsd)} usage-based, ${money(point.scheduledUsd)} scheduled${point.coverage === 'partial' ? `, ${point.coveredHours} of ${point.totalHours} hours covered` : ''}`;
return (
<Tooltip key={point.periodStart}>
<TooltipTrigger asChild>
Expand All @@ -153,7 +153,7 @@ export function SpendEvidenceCard({
onKeyDown={event => handleBarKeyDown(event, index)}
>
<span className="flex h-5 w-full items-end justify-center">
{isPeak && point.coverage === 'complete' && (
{isPeak && point.coverage !== 'unavailable' && (
<span className="type-label font-mono tabular-nums whitespace-nowrap">
{money(pointTotal)}
</span>
Expand All @@ -163,16 +163,18 @@ export function SpendEvidenceCard({
<span
className={cn(
'group-hover:ring-foreground/50 mx-auto flex w-full max-w-10 flex-col-reverse overflow-hidden rounded-t-sm transition-[filter,box-shadow] duration-150 group-hover:brightness-110 group-focus-visible:brightness-110',
point.coverage !== 'complete' &&
point.coverage === 'partial' &&
'border-border-strong border border-dashed',
point.coverage === 'unavailable' &&
'border-border-strong bg-surface-overlay h-2 border border-dashed'
)}
style={
point.coverage === 'complete'
point.coverage !== 'unavailable'
? { height: `${totalHeight}%` }
: undefined
}
>
{point.coverage === 'complete' && (
{point.coverage !== 'unavailable' && (
<>
<span
className="bg-chart-1"
Expand All @@ -198,9 +200,11 @@ export function SpendEvidenceCard({
</TooltipTrigger>
<TooltipContent side="top" sideOffset={8} className="min-w-44 p-3">
<div className="type-label font-medium">{point.label}</div>
{point.coverage === 'complete' ? (
{point.coverage !== 'unavailable' ? (
<dl className="mt-2 grid grid-cols-[1fr_auto] gap-x-4 gap-y-1 type-label">
<dt className="text-muted-foreground">Total</dt>
<dt className="text-muted-foreground">
{point.coverage === 'partial' ? 'Known spend' : 'Total'}
</dt>
<dd className="text-right font-mono font-semibold tabular-nums">
{money(pointTotal)}
</dd>
Expand All @@ -218,6 +222,11 @@ export function SpendEvidenceCard({
<dd className="text-right font-mono tabular-nums">
{money(point.scheduledUsd)}
</dd>
{point.coverage === 'partial' && (
<dt className="text-muted-foreground col-span-2 mt-1">
Partial coverage: {point.coveredHours} of {point.totalHours} hours.
</dt>
)}
</dl>
) : (
<p className="type-label text-muted-foreground mt-2">
Expand Down
7 changes: 6 additions & 1 deletion apps/web/src/components/cost-insights/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ export type SpendEvidencePoint =
scheduledUsd: number;
})
| (SpendEvidencePointBase & {
coverage: 'partial' | 'unavailable';
coverage: 'partial';
variableUsd: number;
scheduledUsd: number;
})
| (SpendEvidencePointBase & {
coverage: 'unavailable';
variableUsd: null;
scheduledUsd: null;
});
Expand Down
6 changes: 3 additions & 3 deletions apps/web/src/lib/cost-insights/presenter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ describe('Cost Insights presenter', () => {
).toThrow('Covered Cost Insights evidence must include both spend categories.');
});

it('marks aggregate evidence partial without exposing an understated covered subtotal', () => {
it('shows known spend in partial aggregate evidence without presenting it as a complete total', () => {
const points = [
{
hourStart: '2026-06-25T22:00:00.000Z',
Expand Down Expand Up @@ -594,8 +594,8 @@ describe('Cost Insights presenter', () => {
coverage: 'partial',
coveredHours: 1,
totalHours: 2,
variableUsd: null,
scheduledUsd: null,
variableUsd: 2,
scheduledUsd: 1,
},
{
label: 'Jun 26',
Expand Down
8 changes: 2 additions & 6 deletions apps/web/src/lib/cost-insights/presenter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,6 @@ function presentEvidenceBucket(points: OwnerHourlySpend[]): SpendEvidencePoint {
if (covered.length === 0) {
return { ...common, coverage: 'unavailable', variableUsd: null, scheduledUsd: null };
}
if (covered.length !== points.length) {
return { ...common, coverage: 'partial', variableUsd: null, scheduledUsd: null };
}

let variableMicrodollars = 0;
let scheduledMicrodollars = 0;
for (const point of covered) {
Expand All @@ -238,7 +234,7 @@ function presentEvidenceBucket(points: OwnerHourlySpend[]): SpendEvidencePoint {
}
return {
...common,
coverage: 'complete',
coverage: covered.length === points.length ? 'complete' : 'partial',
variableUsd: microdollarsToUsd(variableMicrodollars),
scheduledUsd: microdollarsToUsd(scheduledMicrodollars),
};
Expand Down Expand Up @@ -472,7 +468,7 @@ function buildMetrics(params: {
params.currentHourVariableMicrodollars === null
? 'Current-hour spend evidence is unavailable'
: params.currentHourVariableMicrodollars >= params.anomalyThresholdMicrodollars
? 'Above current alert level'
? 'Unusually high for this account'
: `Typical hour: ${money(params.anomalyBaselineMicrodollars)}`,
tone:
params.currentHourVariableMicrodollars !== null &&
Expand Down