This commit is contained in:
Tomáš Dvořák
2025-10-16 13:32:05 +02:00
commit 12cba639b9
663 changed files with 168914 additions and 0 deletions
+193
View File
@@ -0,0 +1,193 @@
import React, { useMemo } from 'react';
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
ModalFooter,
Button,
HStack,
VStack,
Image,
Text,
Badge,
Link,
Divider,
} from '@chakra-ui/react';
import { useCountdown } from '../../hooks/useCountdown';
import { assetUrl } from '../../utils/url';
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;
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>
{match?.home || 'Domácí'} vs {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}
>
<Image src={assetUrl(match.home_logo_url) || '/logo192.png'} alt={match.home || 'Domácí'} boxSize="56px" objectFit="contain" />
<Text fontWeight="semibold" noOfLines={1} textAlign="center">{match.home || 'Domácí'}</Text>
</VStack>
<VStack spacing={1} minW="120px">
{hasScore ? (
<>
<Text fontSize="2xl" fontWeight="bold">{match.score}</Text>
<Text fontSize="sm" color="gray.600">Skončeno</Text>
</>
) : matchStarted ? (
<>
<Text fontSize="2xl" fontWeight="bold">:</Text>
<Text fontSize="sm" color="green.600">Probíhá</Text>
</>
) : (
<>
<Text fontSize="lg" color="gray.600">Začátek za</Text>
<Text fontSize="2xl" fontWeight="bold">{countdownString || '—'}</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}
>
<Image src={assetUrl(match.away_logo_url) || '/logo192.png'} alt={match.away || 'Hosté'} boxSize="56px" objectFit="contain" />
<Text fontWeight="semibold" noOfLines={1} textAlign="center">{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;