import type { WeatherSample } from '../types' interface Props { weather: WeatherSample[] } function avg(values: number[]): number { if (values.length === 0) return 0 return values.reduce((sum, val) => sum + val, 0) / values.length } function formatNumber(value: number, digits = 1): string { return Number.isFinite(value) ? value.toFixed(digits) : '—' } function formatTime(date: string): string { if (!date) return '—' const parsed = new Date(date) if (Number.isNaN(parsed.getTime())) return date return parsed.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', }) } export function WeatherView({ weather }: Props) { if (weather.length === 0) { return (
Weather samples not ingested. Run box-box --ingest-session <key>{' '} to load this dataset.
) } const rows = [...weather].sort((a, b) => a.date.localeCompare(b.date)) const latest = rows[rows.length - 1] const rainfallSamples = rows.filter((sample) => sample.rainfall > 0).length return (
Summary Value
Latest sample {formatTime(latest.date)}
Avg air / track {formatNumber(avg(rows.map((sample) => sample.air_temperature)))}C /{' '} {formatNumber(avg(rows.map((sample) => sample.track_temperature)))}C
Rain samples {rainfallSamples}
{rows.slice(-12).map((sample) => ( ))}
Time Air Track Humidity Rain Wind
{formatTime(sample.date)} {formatNumber(sample.air_temperature)}C {formatNumber(sample.track_temperature)}C {formatNumber(sample.humidity, 0)}% {formatNumber(sample.rainfall)} {formatNumber(sample.wind_speed)} m/s
) }