"use client"; import { useCallback, useEffect, useMemo, useState } from "react"; import Link from "next/link"; import { ChevronLeft, ChevronRight, CalendarDays, Clock, Loader2, } from "lucide-react"; import type { CalendarMatch } from "@/lib/services/calendar"; import { addMonths, buildMonthGrid, endOfMonth, formatMonthYear, matchDisplayDate, parseDateKey, sameDay, startOfMonth, toDateKey, } from "@/lib/calendar/utils"; import { apiFetch } from "@/lib/api/client"; import { cn } from "@/lib/utils"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; const WEEKDAYS = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]; function matchWhen(m: CalendarMatch) { return m.scheduled_at ?? m.proposed_scheduled_at; } function statusVariant(status: string) { if (status === "completed" || status === "played") return "default" as const; if (status === "scheduled" || status === "confirmed") return "secondary" as const; return "outline" as const; } export function MatchCalendar({ apiPath, title = "Match calendar", description = "Plan around fixtures — days with matches glow on the grid.", }: { apiPath: "/api/manager/calendar" | "/api/master/calendar"; title?: string; description?: string; }) { const [month, setMonth] = useState(() => startOfMonth(new Date())); const [selected, setSelected] = useState(() => toDateKey(new Date())); const [matches, setMatches] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const load = useCallback(async () => { setLoading(true); setError(null); try { const from = startOfMonth(month).toISOString(); const to = endOfMonth(month).toISOString(); const data = await apiFetch( `${apiPath}?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}` ); setMatches(data); } catch (e) { setError(e instanceof Error ? e.message : "Failed to load matches"); } finally { setLoading(false); } }, [apiPath, month]); useEffect(() => { void load(); }, [load]); const byDate = useMemo(() => { const map = new Map(); for (const m of matches) { const when = matchWhen(m); if (!when) continue; const key = toDateKey(new Date(when)); const list = map.get(key) ?? []; list.push(m); map.set(key, list); } return map; }, [matches]); const unscheduled = useMemo( () => matches.filter((m) => !matchWhen(m)), [matches] ); const grid = useMemo(() => buildMonthGrid(month), [month]); const selectedMatches = byDate.get(selected) ?? []; const selectedDate = parseDateKey(selected); const today = new Date(); const monthMatchCount = useMemo(() => { let n = 0; for (const [, list] of byDate) n += list.length; return n; }, [byDate]); return (

Schedule

{title}

{description}

{formatMonthYear(month)}

{loading && (
Loading fixtures…
)} {error && !loading && (

{error}

)} {!loading && !error && ( <>
{WEEKDAYS.map((d) => (
{d}
))}
{grid.map((day) => { const key = toDateKey(day); const inMonth = day.getMonth() === month.getMonth(); const isSelected = key === selected; const isToday = sameDay(day, today); const dayMatches = byDate.get(key) ?? []; const hasMatches = dayMatches.length > 0; return ( ); })}

{monthMatchCount}{" "} scheduled in {formatMonthYear(month)} {unscheduled.length > 0 && ( <> {" · "} {unscheduled.length} awaiting date )}

)}

{selectedDate.toLocaleDateString(undefined, { weekday: "long", month: "short", day: "numeric", })}

{selectedMatches.length === 0 ? "No matches" : `${selectedMatches.length} match${selectedMatches.length > 1 ? "es" : ""}`}

    {selectedMatches.length === 0 && (
  • Pick a glowing day or schedule a fixture from your league.
  • )} {selectedMatches.map((m) => ( ))}
{unscheduled.length > 0 && (

Date TBD

    {unscheduled.map((m) => ( ))}
)}
); } function MatchCard({ match, compact }: { match: CalendarMatch; compact?: boolean }) { const when = matchWhen(match); const href = `/leagues/${match.league_id}/competitions/${match.competition_id}/matches/${match.id}`; return (
  • {match.status.replace(/_/g, " ")} {!compact && ( {match.league_name} )}

    {match.home_name}{" "} vs {match.away_name}

    {match.competition_name}

    {matchDisplayDate(when)}

  • ); }