Files
MyClub/frontend/src/components/home/MatchModal.tsx
T
Tomas Dvorak 3d621e2187 dev day #71
2025-10-25 16:33:53 +02:00

221 lines
7.9 KiB
TypeScript

import React, { useMemo } from 'react';
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
ModalFooter,
Button,
HStack,
VStack,
Text,
Badge,
Link,
Divider,
} from '@chakra-ui/react';
import { useCountdown } from '../../hooks/useCountdown';
import { assetUrl, sanitizeClubName } from '../../utils/url';
import { TeamLogo } from '../common/TeamLogo';
export type FacrMatchLike = {
id?: string | number;
date?: string; // yyyy-mm-dd
time?: string; // HH:MM
date_time?: string; // alternative combined format (dd.MM.yyyy HH:mm)
home?: string;
away?: string;
home_logo_url?: string;
away_logo_url?: string;
home_id?: string;
away_id?: string;
competition?: string;
competitionName?: string;
venue?: string;
score?: string | null;
facr_link?: string | null;
report_url?: string | null;
};
interface MatchModalProps {
isOpen: boolean;
match: FacrMatchLike | null;
onClose: () => void;
onTeamClick?: (teamName: string, teamLogoUrl?: string) => void;
}
const formatWhen = (m: FacrMatchLike | null) => {
if (!m) return '';
try {
if (m.date && m.time) {
const d = new Date(`${m.date}T${(m.time || '00:00')}:00`);
if (!isNaN(d.getTime())) return d.toLocaleString();
}
if (m.date_time) {
// Try to parse dd.MM.yyyy HH:mm quickly by reordering
const dt = String(m.date_time);
const [dPart, tPart] = dt.split(' ');
const [dd, MM, yyyy] = (dPart || '').split('.');
if (dd && MM && yyyy) {
const iso = `${yyyy}-${MM.padStart(2, '0')}-${dd.padStart(2, '0')}T${(tPart || '00:00')}:00`;
const d = new Date(iso);
if (!isNaN(d.getTime())) return d.toLocaleString();
}
return m.date_time;
}
} catch {}
return '';
};
export const MatchModal: React.FC<MatchModalProps> = ({ isOpen, match, onClose, onTeamClick }) => {
const kickoffIso = useMemo(() => {
if (!match) return null;
if (match.date && match.time) return `${match.date}T${(match.time || '00:00')}:00`;
if (match.date_time) {
const dt = String(match.date_time);
const [dPart, tPart] = dt.split(' ');
const [dd, MM, yyyy] = (dPart || '').split('.');
if (dd && MM && yyyy) return `${yyyy}-${MM.padStart(2, '0')}-${dd.padStart(2, '0')}T${(tPart || '00:00')}:00`;
return match.date_time; // fallback
}
return null;
}, [match]);
const { countdownString, isActive, timeRemaining } = useCountdown(kickoffIso, 1000);
const facrLink = match?.facr_link || match?.report_url || null;
const when = formatWhen(match);
// Determine if match has started (countdown finished) but no score yet
const matchStarted = kickoffIso ? new Date(kickoffIso).getTime() <= Date.now() : false;
const hasScore = match?.score && match.score.trim() !== '';
return (
<Modal isOpen={isOpen} onClose={onClose} isCentered size={{ base: 'md', md: 'lg' }}>
<ModalOverlay />
<ModalContent>
<ModalHeader>
{sanitizeClubName(match?.home) || 'Domácí'} vs {sanitizeClubName(match?.away) || 'Hosté'}
</ModalHeader>
<ModalCloseButton />
<ModalBody>
{match && (
<VStack align="stretch" spacing={4}>
<HStack justify="space-between" align="center">
<VStack
align="center"
spacing={2}
flex={1}
minW={0}
cursor={onTeamClick ? 'pointer' : 'default'}
onClick={() => onTeamClick && onTeamClick(match.home || '', match.home_logo_url)}
_hover={onTeamClick ? { opacity: 0.8, transform: 'scale(1.05)' } : {}}
transition="all 0.2s"
role={onTeamClick ? 'button' : undefined}
tabIndex={onTeamClick ? 0 : undefined}
>
<TeamLogo
teamId={match.home_id}
teamName={match.home}
facrLogo={match.home_logo_url}
size="custom"
alt={match.home || 'Domácí'}
boxSize="56px"
borderRadius="full"
/>
<Text fontWeight="semibold" noOfLines={1} textAlign="center">{sanitizeClubName(match.home) || 'Domácí'}</Text>
</VStack>
<VStack spacing={1} minW="120px">
{!matchStarted ? (
// Future match - show countdown or vs
isActive && countdownString ? (
<>
<Text fontSize="lg" color="gray.600">Začátek za</Text>
<Text fontSize="2xl" fontWeight="bold">{countdownString}</Text>
</>
) : (
<>
<Text fontSize="2xl" fontWeight="bold">vs</Text>
</>
)
) : hasScore ? (
// Match finished with score
<>
<Text fontSize="2xl" fontWeight="bold">{match.score}</Text>
<Text fontSize="sm" color="gray.600">Skončeno</Text>
</>
) : (
// Match started but no score yet
<>
<Text fontSize="2xl" fontWeight="bold">:</Text>
<Text fontSize="sm" color="green.600">Probíhá</Text>
</>
)}
{(match.competition || match.competitionName) && (
<Badge colorScheme="blue" variant="subtle">{match.competition || match.competitionName}</Badge>
)}
</VStack>
<VStack
align="center"
spacing={2}
flex={1}
minW={0}
cursor={onTeamClick ? 'pointer' : 'default'}
onClick={() => onTeamClick && onTeamClick(match.away || '', match.away_logo_url)}
_hover={onTeamClick ? { opacity: 0.8, transform: 'scale(1.05)' } : {}}
transition="all 0.2s"
role={onTeamClick ? 'button' : undefined}
tabIndex={onTeamClick ? 0 : undefined}
>
<TeamLogo
teamId={match.away_id}
teamName={match.away}
facrLogo={match.away_logo_url}
size="custom"
alt={match.away || 'Hosté'}
boxSize="56px"
borderRadius="full"
/>
<Text fontWeight="semibold" noOfLines={1} textAlign="center">{sanitizeClubName(match.away) || 'Hosté'}</Text>
</VStack>
</HStack>
<Divider />
<VStack align="stretch" spacing={1} color="gray.700">
{when && <Text><strong>Kdy:</strong> {when}</Text>}
{match.venue && <Text><strong>Kde:</strong> {match.venue}</Text>}
</VStack>
</VStack>
)}
</ModalBody>
<ModalFooter>
{facrLink && (
<Button
colorScheme="blue"
mr={3}
onClick={(e) => {
e.preventDefault();
// Open in background tab without switching focus
const link = document.createElement('a');
link.href = facrLink;
link.target = '_blank';
link.rel = 'noopener noreferrer';
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}}
>
Detail na FAČR
</Button>
)}
<Button onClick={onClose}>Zavřít</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
};
export default MatchModal;