From 773b97313da665239600dbd8b90efe4b29ba962b Mon Sep 17 00:00:00 2001 From: Stalin <161853795+0x5t4l1n@users.noreply.github.com> Date: Mon, 3 Aug 2026 01:07:39 +0530 Subject: [PATCH] fix: show informative message when match schedule has not been released Resolves #200. The loading spinner kept spinning indefinitely when the match schedule had not been released yet, giving users no indication of what was happening. Add a scheduleLoaded state flag set after the fetch completes (whether or not data was returned) so a clear 'Schedule not released yet' message is shown instead of an infinite spinner. --- src/components/ScheduleDisplay.tsx | 46 +++++++++++++++++++++++++ src/hooks/useMatchSchedule.ts | 54 ++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 src/components/ScheduleDisplay.tsx create mode 100644 src/hooks/useMatchSchedule.ts diff --git a/src/components/ScheduleDisplay.tsx b/src/components/ScheduleDisplay.tsx new file mode 100644 index 00000000..d5adbdb8 --- /dev/null +++ b/src/components/ScheduleDisplay.tsx @@ -0,0 +1,46 @@ +import React from 'react'; + +interface ScheduleDisplayProps { + schedule: T | null; + scheduleLoaded: boolean; + renderSchedule: (schedule: T) => React.ReactNode; + /** Optional custom message shown when the schedule is not yet released. */ + notReleasedMessage?: string; +} + +/** + * ScheduleDisplay + * + * Renders the match schedule, a loading spinner while fetching, or an + * informative message when the schedule has not been released yet. + * + * Resolves issue #200: previously an infinite spinner was shown when the + * schedule simply hadn't been published yet. + */ +export function ScheduleDisplay({ + schedule, + scheduleLoaded, + renderSchedule, + notReleasedMessage = 'The match schedule has not been released yet. Check back closer to the event!', +}: ScheduleDisplayProps) { + if (!scheduleLoaded) { + return ( +
+
+
+ ); + } + + if (!schedule) { + return ( +
+

{notReleasedMessage}

+
+ ); + } + + return <>{renderSchedule(schedule)}; +} diff --git a/src/hooks/useMatchSchedule.ts b/src/hooks/useMatchSchedule.ts new file mode 100644 index 00000000..793c6b02 --- /dev/null +++ b/src/hooks/useMatchSchedule.ts @@ -0,0 +1,54 @@ +import { useState, useEffect } from 'react'; + +export interface MatchScheduleResult { + schedule: T | null; + /** True once the fetch has completed, even if no data was returned. */ + scheduleLoaded: boolean; + error: Error | null; +} + +/** + * useMatchSchedule + * + * Fetches the match schedule for a given team and returns both the data and + * a `scheduleLoaded` flag so callers can distinguish between + * "still loading" and "loaded but no schedule released yet" (issue #200). + * + * @param fetchFn - Async function that fetches the schedule data. + * @param deps - Dependency array (passed to useEffect). + */ +export function useMatchSchedule( + fetchFn: () => Promise, + deps: unknown[] = [], +): MatchScheduleResult { + const [schedule, setSchedule] = useState(null); + const [scheduleLoaded, setScheduleLoaded] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + let cancelled = false; + setScheduleLoaded(false); + setError(null); + + fetchFn() + .then((data) => { + if (!cancelled) { + setSchedule(data); + setScheduleLoaded(true); + } + }) + .catch((err: unknown) => { + if (!cancelled) { + setError(err instanceof Error ? err : new Error(String(err))); + setScheduleLoaded(true); // stop spinner even on error + } + }); + + return () => { + cancelled = true; + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, deps); + + return { schedule, scheduleLoaded, error }; +}