mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
194 lines
7.0 KiB
TypeScript
194 lines
7.0 KiB
TypeScript
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, sanitizeClubName } 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>
|
|
{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}
|
|
>
|
|
<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">{sanitizeClubName(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">{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;
|