import { useEffect, useState } from 'react';
import { Link, f7 } from 'framework7-react';
import { apiClient, errorMessage, HealthDay } from '../services/api';
import Screen from '../components/Screen';
import Ring from '../components/charts/Ring';
import MetricCard from '../components/charts/MetricCard';
import MetricStrip from '../components/charts/MetricStrip';
import Skeleton from '../components/Skeleton';
import { useCountUp } from '../lib/motion';
import { RANGES } from '../lib/ranges';
import { METRICS, metricHref } from '../lib/metrics';
import { daysAgo, iso, shiftDay, today as todayIso } from '../lib/day';
import './Today.css';
/* A year is loaded up front so stepping back a day costs no request. */
const HISTORY_DAYS = 365;
interface RingSpec {
label: string;
value: number | null;
/** Reaching this counts as a full ring. */
target: number | null;
unit: string;
decimals?: number;
goalText: string;
}
/**
* Three rings: steps, sleep and HRV.
*
* Each is normalised against the point where it enters its own reference band,
* so a full ring means the same thing for all three even though the units do
* not compare. Rings sit side by side rather than concentric — Apple's nested
* form works because its three rings share one idea (move/exercise/stand);
* these three are unrelated measures and read more clearly apart.
*/
function RingRow({ today, history }: { today: HealthDay; history: HealthDay[] }) {
const stepGoal = today.stepGoal ?? RANGES.steps.goodFrom;
const specs: RingSpec[] = [
{
label: '步数',
value: today.steps,
target: stepGoal,
unit: '步',
goalText: `目标 ${stepGoal.toLocaleString()}`,
},
{
label: '睡眠',
value: today.sleepDuration,
target: RANGES.sleepDuration.goodFrom,
unit: '小时',
decimals: 1,
goalText: `目标 ${RANGES.sleepDuration.goodFrom} 小时`,
},
{
label: 'HRV',
value: today.heartRateVariability,
target: RANGES.heartRateVariability.goodFrom,
unit: 'ms',
goalText: `参考 ≥${RANGES.heartRateVariability.goodFrom} ms`,
},
];
const week = history.slice(-7).map((d) => d.steps).filter((v): v is number => v != null);
const weekAvg = week.length
? Math.round(week.reduce((a, b) => a + b, 0) / week.length)
: null;
const done = specs.filter((s) => s.value != null && s.target && s.value >= s.target).length;
return (