'use client'; import React, {useCallback, useEffect, useRef, useState} from "react"; import {Alert, Box, CircularProgress, Paper, Radio, RadioGroup, Stack, Typography} from "@mui/material"; import {AuthGuard} from "@/src/auth/AuthGuard"; import {Fixture, picksClient, PickSelection} from "@/lib/api/picks"; import {ApiError} from "@/lib/api/client"; import CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded'; import ScheduleRoundedIcon from '@mui/icons-material/ScheduleRounded'; import SyncRoundedIcon from '@mui/icons-material/SyncRounded'; import {useSearchParams} from "next/navigation"; const PicksPage: React.FC = () => { const searchParams = useSearchParams(); const footerRef = useRef(null); const [fixtures, setFixtures] = useState([]); const [selectedPicks, setSelectedPicks] = useState>({}); const [savedPicks, setSavedPicks] = useState>({}); const [savingPickIds, setSavingPickIds] = useState([]); const [isLoadingFixtures, setIsLoadingFixtures] = useState(true); const [isRefreshingFixtures, setIsRefreshingFixtures] = useState(false); const [error, setError] = useState(''); const [footerHeight, setFooterHeight] = useState(0); const loadFixtures = useCallback(async (signal?: AbortSignal) => { setError(''); setIsLoadingFixtures(true); try { const [fixturesResponse, picksResponse] = await Promise.all([ picksClient.getFixtures(signal), picksClient.getPicks(signal), ]); setFixtures(fixturesResponse.fixtures); const nextSavedPicks = picksResponse.picks.reduce>((accumulator, pick) => { accumulator[pick.match_id] = pick.selection; return accumulator; }, {}); setSelectedPicks(nextSavedPicks); setSavedPicks(nextSavedPicks); } catch (fetchError: unknown) { if (signal?.aborted) { return; } if (fetchError instanceof ApiError) { setError(fetchError.message); } else { setError('No fue posible cargar los fixtures.'); } } finally { if (!signal?.aborted) { setIsLoadingFixtures(false); } } }, []); useEffect(() => { const controller = new AbortController(); void loadFixtures(controller.signal); return () => { controller.abort(); }; }, [loadFixtures]); const handleRefreshFixtures = async () => { setIsRefreshingFixtures(true); try { await loadFixtures(); } finally { setIsRefreshingFixtures(false); } }; const getLocaleDate = (date: number): string => { return new Date(date * 1000).toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short', }) }; const getStatusString = (status: string): string => { switch (status) { case "scheduled": return "Agendado" case "in_progress": return "En vivo" case "finished": return "Terminado" case "cancelled": return "Cancelado" default: return "Desconocido" } } const handlePickChange = async (fixtureId: number, pick: PickSelection) => { const previousPick = selectedPicks[fixtureId]; setSelectedPicks((current) => ({ ...current, [fixtureId]: pick, })); setSavingPickIds((current) => [...current, fixtureId]); setError(''); try { await picksClient.savePick({ match_id: fixtureId, selection: pick, }); setSavedPicks((current) => ({ ...current, [fixtureId]: pick, })); } catch (saveError: unknown) { setSelectedPicks((current) => { if (!previousPick) { const remainingPicks = {...current}; delete remainingPicks[fixtureId]; return remainingPicks; } return { ...current, [fixtureId]: previousPick, }; }); if (saveError instanceof ApiError) { setError(saveError.message); } else { setError('No fue posible guardar tu selección.'); } } finally { setSavingPickIds((current) => current.filter((currentFixtureId) => currentFixtureId !== fixtureId)); } }; const getPickStatus = (fixtureId: number) => { if (savingPickIds.includes(fixtureId)) { return { label: 'Registrando...', color: 'text.primary', } as const; } if (savedPicks[fixtureId]) { return { label: 'Registrado correctamente', color: 'primary.main', } as const; } return { label: 'No se ha registrado el resultado', color: 'error.main', } as const; }; const savedCount = fixtures.filter((fixture) => Boolean(savedPicks[fixture.id])).length; const footerPreviewCountValue = Number(searchParams.get('footerPreview') ?? '0'); const footerPreviewCount = Number.isFinite(footerPreviewCountValue) && footerPreviewCountValue > 0 ? Math.floor(footerPreviewCountValue) : 0; const footerTotalCount = Math.max(fixtures.length, footerPreviewCount); const missingCount = Math.max(footerTotalCount - savedCount, 0); const footerItems = Array.from({length: footerTotalCount}, (_, index) => { const fixture = fixtures[index]; if (fixture) { return { key: `fixture-${fixture.id}`, fixtureId: fixture.id, }; } return { key: `preview-${index}`, fixtureId: null, }; }); const footerBottomPadding = footerTotalCount > 0 ? Math.max(footerHeight + 12, 190) : 0; useEffect(() => { if (footerTotalCount === 0) { setFooterHeight(0); return; } const footerElement = footerRef.current; if (!footerElement) { return; } const updateFooterHeight = () => { setFooterHeight(footerElement.getBoundingClientRect().height); }; updateFooterHeight(); const observer = new ResizeObserver(() => { updateFooterHeight(); }); observer.observe(footerElement); return () => { observer.disconnect(); }; }, [footerTotalCount, savedCount, missingCount, savingPickIds.length]); return ( Partidos {error ? {error} : null} {isLoadingFixtures ? ( ) : fixtures.length === 0 ? ( No hay partidos disponibles. ) : ( {fixtures.map((fixture) => ( {getLocaleDate(fixture.starts_at)} {fixture.venue} {fixture.stage} {getStatusString(fixture.status)} void handlePickChange(fixture.id, event.target.value as PickSelection)} > {fixture.home_team} {'Empate'} {fixture.away_team} {(() => { const pickStatus = getPickStatus(fixture.id); return ( {pickStatus.label} ); })()} ))} )} {footerTotalCount > 0 ? ( ) : null} {!isLoadingFixtures && footerTotalCount > 0 ? ( {savedCount}/{footerTotalCount} resultados registrados {missingCount} pendientes por registrar {savedCount} {missingCount} {footerPreviewCount > fixtures.length ? ( Vista previa del footer con {footerPreviewCount} partidos ) : null} {footerItems.map((item) => { if (item.fixtureId && savingPickIds.includes(item.fixtureId)) { return ( ); } if (item.fixtureId && savedPicks[item.fixtureId]) { return ( ); } return ( ); })} ) : null} ) } export default PicksPage;