import { i18n } from "@lingui/core"; import { useLingui } from "@lingui/react/macro"; import type { CoachRhythm } from "@rakazo/contracts"; import { Button, Input, NativeSelect, NativeSelectOption, Popover, PopoverContent, PopoverTrigger, Spinner, Switch, } from "@rakazo/ui-web"; import { CalendarClock } from "lucide-react"; import type { ReactNode } from "react"; import { useCallback, useId, useRef, useState } from "react"; import { rpc } from "../../lib/rpc"; /** Times a check-in starts at when it is switched on. */ const DEFAULTS = { morning: "08:00", evening: "20:00", weekly: { day: 0, time: "18:00" } }; const WEEKDAYS = [0, 1, 2, 3, 4, 5, 6]; function weekdayName(day: number): string { // 2026-10-04 was a Sunday, so day 0 maps to Sunday. return new Date(Date.UTC(2026, 9, 4 + day)).toLocaleDateString(i18n.locale || "en", { weekday: "long", timeZone: "UTC", }); } /** When the main bot checks in: loaded when opened, saved on every change. */ export function CoachRhythmControl() { const { t } = useLingui(); const [rhythm, setRhythm] = useState(null); const latestRequest = useRef(0); const load = useCallback(async () => { const request = ++latestRequest.current; try { const loaded = await rpc.coach.rhythm.get(); if (request === latestRequest.current) setRhythm(loaded); } catch { // Opening the control again retries. } }, []); const save = useCallback( async (next: CoachRhythm) => { setRhythm(next); const request = ++latestRequest.current; try { const saved = await rpc.coach.rhythm.set(next); if (request === latestRequest.current) setRhythm(saved); } catch { if (request === latestRequest.current) void load(); } }, [load], ); return ( { if (open) void load(); }} > } > {rhythm ? ( <> void save({ ...rhythm, morning: on ? DEFAULTS.morning : null })} > {(labelId) => rhythm.morning ? ( void save({ ...rhythm, morning })} /> ) : null } void save({ ...rhythm, evening: on ? DEFAULTS.evening : null })} > {(labelId) => rhythm.evening ? ( void save({ ...rhythm, evening })} /> ) : null } void save({ ...rhythm, weekly: on ? DEFAULTS.weekly : null })} > {(labelId) => { const weekly = rhythm.weekly; if (!weekly) return null; return ( <> void save({ ...rhythm, weekly: { ...weekly, day: Number(event.target.value) }, }) } > {WEEKDAYS.map((day) => ( {weekdayName(day)} ))} void save({ ...rhythm, weekly: { ...weekly, time } })} /> ); }} ) : (
)}
); } function RhythmRow({ label, testId, on, onToggle, children, }: { label: string; testId: string; on: boolean; onToggle: (on: boolean) => void; children: (labelId: string) => ReactNode; }) { const labelId = useId(); return (
onToggle(checked)} /> {label}
{children(labelId)}
); } function TimeInput({ labelId, value, onChange, }: { labelId: string; value: string; onChange: (value: string) => void; }) { return ( { if (event.target.value) onChange(event.target.value.slice(0, 5)); }} className="h-control-sm w-32 px-2 tabular-nums" /> ); }