Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -36,8 +36,7 @@ export function SpendEvidenceCard({
const totals = evidence.map(point => point.variableUsd + point.scheduledUsd);
const axisMax = niceCeil(Math.max(0, ...totals));
const hasSpend = axisMax > 0;
const hasIncompleteEvidence = evidence.some(point => point.coverage !== 'complete');
const shouldRenderChart = hasSpend || hasIncompleteEvidence;
const hasIncompleteEvidence = hasSpend && evidence.some(point => point.coverage !== 'complete');
Comment thread
jeanduplessis marked this conversation as resolved.
Outdated
const rangeLabel = {
'1h': 'This hour',
'24h': 'Last 24 hours',
Expand All @@ -46,7 +45,7 @@ export function SpendEvidenceCard({
'90d': 'Last 90 days',
}[range];
const highest = evidence
.filter(point => point.coverage !== 'unavailable')
.filter(point => point.variableUsd + point.scheduledUsd > 0)
.reduce<(typeof evidence)[number] | undefined>((currentHighest, point) => {
if (!currentHighest) return point;
const currentTotal = currentHighest.variableUsd + currentHighest.scheduledUsd;
Expand Down Expand Up @@ -112,7 +111,7 @@ export function SpendEvidenceCard({
Use Left and Right Arrow keys to inspect each period. Use Home and End to jump to the
first or last period.
</p>
{shouldRenderChart ? (
{hasSpend ? (
<div className="border-border bg-surface-inset rounded-lg border p-4">
<fieldset
aria-describedby={chartInstructionsId}
Expand All @@ -121,7 +120,7 @@ export function SpendEvidenceCard({
>
<legend className="sr-only">{rangeLabel} spend by period</legend>
<div
className="pointer-events-none absolute inset-x-0 top-7 h-44"
className="pointer-events-none absolute inset-x-0 top-0 h-44"
aria-hidden="true"
>
<ChartGridLine position="top-0" label={money(axisMax)} />
Expand All @@ -132,12 +131,13 @@ export function SpendEvidenceCard({
const pointTotal = point.variableUsd + point.scheduledUsd;
const totalHeight = spendBarHeightPercent(pointTotal, axisMax);
const scheduledShare = percentOf(point.scheduledUsd, pointTotal);
const isPeak = highest !== undefined && point.periodStart === highest.periodStart;
const showTick = index % tickStride === 0 || index === evidence.length - 1;
const accessibilityLabel =
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` : ''}`;
pointTotal === 0
Comment thread
jeanduplessis marked this conversation as resolved.
Outdated
? `${point.label}: no spend`
: 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 @@ -152,13 +152,6 @@ export function SpendEvidenceCard({
onFocus={() => setActiveBarIndex(index)}
onKeyDown={event => handleBarKeyDown(event, index)}
>
<span className="flex h-5 w-full items-end justify-center">
{isPeak && point.coverage !== 'unavailable' && (
<span className="type-label font-mono tabular-nums whitespace-nowrap">
{money(pointTotal)}
</span>
)}
</span>
<span className="flex h-44 w-full items-end">
<span
className={cn(
Expand Down Expand Up @@ -200,7 +193,9 @@ 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 !== 'unavailable' ? (
{pointTotal === 0 ? (
<p className="type-label text-muted-foreground mt-2">No spend.</p>
) : 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">
{point.coverage === 'partial' ? 'Known spend' : 'Total'}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,75 +108,71 @@ export function CostInsightsSettingsView({
</div>
</section>

<div className="border-border ml-6 divide-y border-l sm:ml-10">
{!data.enabled && (
<p className="type-label text-muted-foreground px-6 py-4" role="status">
Spend Alerts are off. Saved anomaly and threshold settings apply when you turn
Spend Alerts on again.
</p>
)}
<section
className="grid gap-5 px-6 py-5 md:grid-cols-[minmax(0,20rem)_minmax(0,1fr)]"
aria-labelledby="anomaly-setting-title"
>
<div>
<h3 id="anomaly-setting-title" className="type-body font-semibold">
Spend anomalies
</h3>
<p className="type-label text-muted-foreground mt-1">
Compare current-hour usage-based spend with your recent hourly pattern.
</p>
</div>
<div className="flex min-h-control-touch items-center gap-3 md:justify-end">
<span className="type-label text-muted-foreground" aria-hidden="true">
{data.anomalyAlertsEnabled ? 'On' : 'Off'}
</span>
<Label htmlFor="spend-anomalies-enabled" className="sr-only">
Spend anomalies
</Label>
<Switch
id="spend-anomalies-enabled"
className="relative before:absolute before:inset-x-0 before:-inset-y-2.5"
checked={data.anomalyAlertsEnabled}
disabled={disabled}
onCheckedChange={anomalyAlertsEnabled => onChange?.({ anomalyAlertsEnabled })}
/>
</div>
</section>
{data.enabled && (
Comment thread
jeanduplessis marked this conversation as resolved.
Outdated
<div className="border-border ml-6 divide-y border-l sm:ml-10">
<section
className="grid gap-5 px-6 py-5 md:grid-cols-[minmax(0,20rem)_minmax(0,1fr)]"
aria-labelledby="anomaly-setting-title"
>
<div>
<h3 id="anomaly-setting-title" className="type-body font-semibold">
Spend anomalies
</h3>
<p className="type-label text-muted-foreground mt-1">
Compare current-hour usage-based spend with your recent hourly pattern.
</p>
</div>
<div className="flex min-h-control-touch items-center gap-3 md:justify-end">
<span className="type-label text-muted-foreground" aria-hidden="true">
{data.anomalyAlertsEnabled ? 'On' : 'Off'}
</span>
<Label htmlFor="spend-anomalies-enabled" className="sr-only">
Spend anomalies
</Label>
<Switch
id="spend-anomalies-enabled"
className="relative before:absolute before:inset-x-0 before:-inset-y-2.5"
checked={data.anomalyAlertsEnabled}
disabled={disabled}
onCheckedChange={anomalyAlertsEnabled => onChange?.({ anomalyAlertsEnabled })}
/>
</div>
</section>

<ThresholdOption
id="spend-threshold-24h"
title="24-hour spend threshold"
description="Optional. Includes all Credit spend in a rolling 24-hour period."
value={data.thresholdUsd}
validation={thresholdValidation}
disabled={disabled}
readOnly={data.readOnly}
onChange={thresholdUsd => onChange?.({ thresholdUsd })}
/>
<ThresholdOption
id="spend-threshold-24h"
Comment thread
jeanduplessis marked this conversation as resolved.
title="24-hour spend threshold"
description="Optional. Includes all Credit spend in a rolling 24-hour period."
value={data.thresholdUsd}
validation={thresholdValidation}
disabled={disabled}
readOnly={data.readOnly}
onChange={thresholdUsd => onChange?.({ thresholdUsd })}
/>

<ThresholdOption
id="spend-threshold-7d"
title="7-day spend threshold"
description="Optional. Includes all Credit spend in a rolling 7-day period."
value={data.threshold7DayUsd ?? ''}
validation={threshold7DayValidation}
disabled={disabled}
readOnly={data.readOnly}
onChange={threshold7DayUsd => onChange?.({ threshold7DayUsd })}
/>
<ThresholdOption
id="spend-threshold-7d"
title="7-day spend threshold"
description="Optional. Includes all Credit spend in a rolling 7-day period."
value={data.threshold7DayUsd ?? ''}
validation={threshold7DayValidation}
disabled={disabled}
readOnly={data.readOnly}
onChange={threshold7DayUsd => onChange?.({ threshold7DayUsd })}
/>

<ThresholdOption
id="spend-threshold-30d"
title="30-day spend threshold"
description="Optional. Includes all Credit spend in a rolling 30-day period."
value={data.threshold30DayUsd}
validation={threshold30DayValidation}
disabled={disabled}
readOnly={data.readOnly}
onChange={threshold30DayUsd => onChange?.({ threshold30DayUsd })}
/>
</div>
<ThresholdOption
id="spend-threshold-30d"
title="30-day spend threshold"
description="Optional. Includes all Credit spend in a rolling 30-day period."
value={data.threshold30DayUsd}
validation={threshold30DayValidation}
disabled={disabled}
readOnly={data.readOnly}
onChange={threshold30DayUsd => onChange?.({ threshold30DayUsd })}
/>
</div>
)}
</div>
</CardContent>
</Card>
Expand Down