import { useEffect, useState } from "react"; import { useGameMeta } from "../state/gameMeta.tsx"; import { isCustomEventId, type CustomEvent, type CustomGames, type LaneId, } from "../../shared/custom.ts"; import type { EventDraft } from "../state/useCustom.ts"; import { cadenceLabel, EventForm } from "./CustomForms.tsx"; import { formatAbsolute, formatRemaining, REGION_LABEL, } from "../../shared/time.ts"; import type { RowEvent } from "./EventRow.tsx"; import { pressure, pressureReason, type Effort } from "../../shared/effort.ts"; import type { Status } from "../state/useProgress.ts"; import { ProgressControls } from "./ProgressControls.tsx"; import { DailyChecklist } from "./DailyChecklist.tsx"; import { dailyOverride } from "../../shared/daily.ts"; import type { Region } from "../../shared/schema.ts"; import { Meter, URGENCY_COLOR } from "./Meter.tsx"; export function EventDetail({ row, completed, ignored, status, effort, note, region, now, daily, detectedDaily, dailyDays, onDaily, onToggleDay, onIgnore, onStatus, onEffort, onNote, onClose, own, }: { row: RowEvent; completed: boolean; ignored: boolean; status: Status | undefined; effort: Effort | undefined; note: string; region: Region; now: number; /** Whether to treat this as repeating, the reader's answer included. */ daily: boolean; /** What the source's wording implies, so an override can fall back to it. */ detectedDaily: boolean; /** Days already ticked off, for events that repeat. */ dailyDays: string[]; onDaily: (id: string, daily: boolean | undefined) => void; onToggleDay: (id: string, day: string) => void; onIgnore: (id: string) => void; onStatus: (id: string, s: Status | undefined) => void; onEffort: (id: string, e: Effort | undefined) => void; onNote: (id: string, n: string) => void; onClose: () => void; /** * Present only when this is an event the reader entered themselves, in which * case they can change it or take it back — this sheet is where anyone would * look for that, rather than a list in settings. */ own?: | { record: CustomEvent; lanes: LaneId[]; games: CustomGames; onSave: (id: string, draft: EventDraft) => void; onDelete: (id: string) => void; strandedBy: (draft: EventDraft) => number; } | undefined; }) { const gameMeta = useGameMeta(); const [editing, setEditing] = useState(false); const { event, clock } = row; const game = gameMeta(event.game); const heat = URGENCY_COLOR[clock.urgency]; const risk = status === "done" ? "fine" : pressure(effort, clock.msRemaining); // Read once rather than at both the guard and the render: calling it twice // was what forced the non-null assertion below it. const cadence = cadenceLabel(own?.record.repeat ?? null); useEffect(() => { const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [onClose]); return (
{game.name}
You added this. The dates are yours, not a source's.
)} {event.summary !== null && ({event.summary}
)}{cadence}
)} {risk !== "fine" && effort !== undefined && clock.msRemaining !== null && ({pressureReason(effort, clock.msRemaining)} It is a rough guide, not a verdict — you know your own schedule.
)} {/* A repeating event gets the checklist instead of nothing but a "mark done" — its work is spread over every day of the run, and one tick cannot express that. Detection reads the source's wording and is wrong in both directions, so the reader can say. The control sits where the checklist goes, which is the one place the answer visibly matters. */} {daily ? ( <>Deleting removes the event. Anything you ticked off stays.
The source gave a date but no time of day, so this counts down to that day's {REGION_LABEL[region]} server reset — where these usually land, but a reading rather than a stated time. Check in-game before the last hours.
)} {/* Says what it does and does what it says. It used to advance one step round the untouched → doing → done cycle, so a reader pressing a button labelled "Mark done" on a fresh event got "doing it" and had to press it again — and the second press from "done" silently wiped the status rather than undoing anything. Three states need three targets, which is what the control above is; this one is the commit, so it goes straight to done and back. */}