fix(app): daily-spend chart fills its card, bars bottom-aligned

The StackedBars chart lived in a fixed 96px band, so when the grid
stretched its card to match the taller "By project" panel the bars
floated at the top over a large empty gap. Make the panel/body flex so
the chart fills the card height (min 120px) with bars bottom-anchored;
round stacked-segment corners only at the top/bottom of each column.
This commit is contained in:
iamtoruk 2026-07-11 17:06:00 -07:00
parent ea56ed33f0
commit 1efc0ceb44
3 changed files with 12 additions and 6 deletions

View file

@ -16,12 +16,12 @@ export function StackedBars({ daily }: { daily: DailyHistoryEntry[] }) {
const legendSeries = (['opus', 'sonnet', 'haiku', 'gpt', 'other'] as const).filter(series => presentSeries.has(series))
return (
<>
<div className="sbars-wrap">
<div className="sbars" aria-label="Daily spend by model">
{daily.map(day => (
<div className="c" key={day.date} data-date={day.date} title={`${day.date} · ${formatUsd(day.cost)}`}>
{day.topModels.map(model => {
const pct = Math.max(2, (Math.max(0, model.cost) / maxTotal) * 100)
const pct = Math.max(1, (Math.max(0, model.cost) / maxTotal) * 100)
return (
<span
key={`${day.date}-${model.name}`}
@ -42,6 +42,6 @@ export function StackedBars({ daily }: { daily: DailyHistoryEntry[] }) {
</span>
))}
</div>
</>
</div>
)
}

View file

@ -93,7 +93,7 @@ function ProjectsLens({
return (
<>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
<Panel title="Daily spend by model">
<Panel title="Daily spend by model" className="spend-chart-panel">
{daily.length ? <StackedBars daily={daily} /> : <EmptyNote>No model spend in this range yet.</EmptyNote>}
</Panel>
<Panel title="By project" right={projects.length ? `top ${projects.length}` : undefined}>

View file

@ -114,9 +114,15 @@ h2 { font-size: 20px; font-weight: 650; letter-spacing: -.015em; margin: 0 0 6px
.days span { font-size: 9.5px; color: var(--t3); font-family: var(--mono); }
/* stacked multi-series bars */
.sbars { display: flex; align-items: flex-end; gap: 3px; height: 96px; padding: 0 4px; }
.spend-chart-panel { display: flex; flex-direction: column; }
.spend-chart-panel .pbody { display: flex; flex: 1; min-height: 0; flex-direction: column; }
.sbars-wrap { display: flex; flex: 1; min-height: 0; flex-direction: column; height: 100%; }
.sbars { display: flex; flex: 1; align-items: flex-end; gap: 3px; min-height: 120px; padding: 0 4px; }
.sbars .c { flex: 1; display: flex; flex-direction: column-reverse; height: 100%; justify-content: flex-start; align-items: center; gap: 1.5px; }
.sbars .s { width: 100%; max-width: 11px; border-radius: 2px; }
.sbars .s { width: 100%; max-width: 11px; border-radius: 0; }
.sbars .s:first-child { border-radius: 0 0 2px 2px; }
.sbars .s:last-child { border-radius: 2px 2px 0 0; }
.sbars .s:only-child { border-radius: 2px; }
.s-opus { background: var(--blue); }
.s-son { background: var(--purple); }
.s-hai { background: var(--lav); }