import { useEffect, useState } from "react"; import { api } from "../api/client"; import type { Profile as ProfileType } from "../types/api"; function NumberField({ label, value, onChange, step = 1, }: { label: string; value: number; onChange: (v: number) => void; step?: number; }) { return ( {label} onChange(Number(e.target.value))} /> ); } function NullableNumberField({ label, value, onChange, }: { label: string; value: number | null; onChange: (v: number | null) => void; }) { return ( {label} onChange(e.target.value === "" ? null : Number(e.target.value))} /> ); } export function Profile() { const [profile, setProfile] = useState(null); const [error, setError] = useState(null); const [saved, setSaved] = useState(false); useEffect(() => { api.getProfile().then(setProfile).catch((e) => setError(String(e))); }, []); function set(key: K, value: ProfileType[K]) { setProfile((p) => (p ? { ...p, [key]: value } : p)); setSaved(false); } async function save() { if (!profile) return; setError(null); try { const updated = await api.updateProfile(profile); setProfile(updated); setSaved(true); } catch (e) { setError(String(e)); setSaved(false); } } if (!profile) { return ( Profile {error ? {error} : Loading...} ); } return ( Profile {error && {error}} {saved && Saved.} Garmin account Email set("GarminEmail", e.target.value)} /> Password set("GarminPassword", e.target.value)} /> Classification set("RollingWindowDays", v)} /> Heart rate set("MaxHeartRate", v)} /> set("RestingHeartRate", v)} /> {([1, 2, 3, 4, 5] as const).map((zone) => ( set(`HRZone${zone}MinPct` as keyof ProfileType, v as never)} /> set(`HRZone${zone}MaxPct` as keyof ProfileType, v as never)} /> ))} Save ); }
{error}
Loading...
Saved.