CarboAny / src /components /pilot /WeeklyChart.tsx
Esketch's picture
deploy: [P4] Strangler Pattern API Adapter Release (Orphan Clean Build v2)
daaf9d7
Raw
History Blame Contribute Delete
2.66 kB
import Dn from '../Dn'
import type { WeeklyActivity } from '../../lib/database.types'
interface Props {
data: WeeklyActivity[]
// Which metric to visualize as the bar: 'cups' or 'co2_kg'.
metric?: 'cups' | 'co2_kg'
}
// E-Ink horizontal bar chart. No external libs, no animations.
// Each row is one week. Bar width scales to the real max in the dataset
// (falls back to 1 only when every value is zero, to avoid divide-by-zero).
export default function WeeklyChart({ data, metric = 'cups' }: Props) {
const values = data.map((d) => (metric === 'cups' ? d.cups : d.co2_kg))
const realMax = Math.max(...values, 0)
const max = realMax > 0 ? realMax : 1
return (
<div className="border border-border-default bg-paper-white divide-y divide-border-default rounded-lg overflow-hidden shadow-sm">
{data.map((row) => {
const value = metric === 'cups' ? row.cups : row.co2_kg
const pct = (value / max) * 100
const isZero = value === 0
return (
<div key={row.week_start} className="px-3 py-2 flex items-center gap-3">
{/* Week label */}
<div className="w-16 flex-shrink-0">
<p className="text-[10px] text-ink-medium font-mono">
{formatWeekLabel(row.week_start)}
</p>
</div>
{/* Bar track — borderless dash for zero, framed track for values */}
<div className="flex-1 h-5 relative flex items-center">
{isZero ? (
<div className="w-full border-t border-dashed border-border-default" />
) : (
<>
<div className="absolute inset-0 border border-border-default bg-paper-cream rounded-sm" />
<div
className="absolute inset-y-0 left-0 bg-accent-teal rounded-sm"
style={{ width: `${pct}%` }}
/>
</>
)}
</div>
{/* Value */}
<div className="w-16 flex-shrink-0 text-right">
<p className={`text-xs font-bold tabular-nums ${isZero ? 'text-ink-light' : 'text-ink-black'}`}>
{metric === 'cups' ? value : <Dn>{value.toFixed(2)}</Dn>}
</p>
{row.active_citizens > 0 && (
<p className="text-[9px] text-ink-light">
시민 {row.active_citizens}
</p>
)}
</div>
</div>
)
})}
</div>
)
}
// "04-13" style label (MM-DD of Monday).
function formatWeekLabel(iso: string): string {
const [, m, d] = iso.split('-')
return `${m}/${d}`
}