mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
upload
This commit is contained in:
@@ -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;
|
||||
Reference in New Issue
Block a user