mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-03 18:22:57 +00:00
136 lines
5.4 KiB
TypeScript
136 lines
5.4 KiB
TypeScript
import { Box, Container, Heading, HStack, Image, SimpleGrid, Spinner, Stack, Text, VStack, useColorModeValue, Badge, Input, Select, Checkbox, InputGroup, InputLeftElement } from '@chakra-ui/react';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { getPlayers } from '../services/public';
|
|
import type { Player } from '../services/public';
|
|
import { assetUrl } from '../utils/url';
|
|
import { Link as RouterLink } from 'react-router-dom';
|
|
import MainLayout from '../components/layout/MainLayout';
|
|
import NewsletterCTA from '../components/common/NewsletterCTA';
|
|
// nationality display removed per requirements
|
|
import { useMemo, useState } from 'react';
|
|
import { SearchIcon } from '@chakra-ui/icons';
|
|
|
|
const PlayersPage: React.FC = () => {
|
|
const { data, isLoading, isError } = useQuery<Player[]>({ queryKey: ['players'], queryFn: () => getPlayers() });
|
|
const cardBg = useColorModeValue('white', 'gray.800');
|
|
const borderColor = useColorModeValue('gray.200', 'gray.700');
|
|
const textSecondary = useColorModeValue('gray.600', 'gray.400');
|
|
|
|
const [q, setQ] = useState('');
|
|
const [gender, setGender] = useState('');
|
|
const [position, setPosition] = useState('');
|
|
const [activeOnly, setActiveOnly] = useState(true);
|
|
|
|
const positions = useMemo(() => {
|
|
const all = (data || []).map(p => p.position).filter(Boolean) as string[];
|
|
return Array.from(new Set(all));
|
|
}, [data]);
|
|
|
|
const filtered = useMemo(() => {
|
|
let list = (data || []).slice();
|
|
if (activeOnly) list = list.filter(p => p.is_active !== false);
|
|
if (gender) list = list.filter(p => (p.gender || '').toLowerCase() === gender);
|
|
if (position) list = list.filter(p => (p.position || '') === position);
|
|
if (q.trim()) {
|
|
const needle = q.trim().toLowerCase();
|
|
list = list.filter(p => {
|
|
const name = `${p.first_name} ${p.last_name}`.trim().toLowerCase();
|
|
const pos = (p.position || '').toLowerCase();
|
|
const jersey = typeof p.jersey_number === 'number' ? String(p.jersey_number) : '';
|
|
return name.includes(needle) || pos.includes(needle) || jersey.includes(needle);
|
|
});
|
|
}
|
|
return list;
|
|
}, [data, q, gender, position, activeOnly]);
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<MainLayout>
|
|
<Container maxW="7xl" py={8}>
|
|
<Spinner />
|
|
</Container>
|
|
</MainLayout>
|
|
);
|
|
}
|
|
|
|
if (isError) {
|
|
return (
|
|
<MainLayout>
|
|
<Container maxW="7xl" py={8}>
|
|
<Text color="red.500">Chyba při načítání hráčů</Text>
|
|
</Container>
|
|
</MainLayout>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<MainLayout>
|
|
<Box>
|
|
<Container maxW="7xl" py={{ base: 6, md: 10 }}>
|
|
<VStack align="stretch" spacing={6}>
|
|
<Heading as="h1" size={{ base: 'xl', md: '2xl' }}>Hráči</Heading>
|
|
<SimpleGrid columns={{ base: 1, md: 4 }} spacing={4}>
|
|
<InputGroup>
|
|
<InputLeftElement pointerEvents="none">
|
|
<SearchIcon color="gray.400" />
|
|
</InputLeftElement>
|
|
<Input value={q} onChange={(e)=>setQ(e.target.value)} placeholder="Hledat jméno, číslo, pozici" />
|
|
</InputGroup>
|
|
<Select value={gender} onChange={(e)=>setGender(e.target.value)} placeholder="Pohlaví">
|
|
<option value="men">Muž</option>
|
|
<option value="women">Žena</option>
|
|
</Select>
|
|
<Select value={position} onChange={(e)=>setPosition(e.target.value)} placeholder="Pozice">
|
|
{positions.map((pos)=> (
|
|
<option key={pos} value={pos}>{pos}</option>
|
|
))}
|
|
</Select>
|
|
<HStack>
|
|
<Checkbox isChecked={activeOnly} onChange={(e)=>setActiveOnly(e.target.checked)}>Pouze aktivní</Checkbox>
|
|
</HStack>
|
|
</SimpleGrid>
|
|
<SimpleGrid columns={{ base: 1, sm: 2, md: 3, lg: 4 }} spacing={6}>
|
|
{filtered.map((p) => (
|
|
<Stack
|
|
key={p.id}
|
|
as={RouterLink}
|
|
to={`/hraci/${p.id}`}
|
|
borderWidth="1px"
|
|
borderColor={borderColor}
|
|
borderRadius="lg"
|
|
p={4}
|
|
bg={cardBg}
|
|
_hover={{ boxShadow: 'lg', transform: 'translateY(-4px)' }}
|
|
transition="all 0.2s ease"
|
|
spacing={3}
|
|
>
|
|
<Box position="relative" borderRadius="md" overflow="hidden">
|
|
<Image
|
|
src={assetUrl(p.image_url) || '/logo512.png'}
|
|
alt={`${p.first_name} ${p.last_name}`}
|
|
objectFit="cover"
|
|
w="100%"
|
|
h="240px"
|
|
/>
|
|
{typeof p.jersey_number === 'number' && (
|
|
<Badge position="absolute" top="10px" left="10px" colorScheme="blue" fontSize="0.85rem" px={3} py={1} borderRadius="md" boxShadow="sm">#{p.jersey_number}</Badge>
|
|
)}
|
|
</Box>
|
|
<Text fontWeight="bold" fontSize="lg">{p.first_name} {p.last_name}</Text>
|
|
<Text color={textSecondary}>{p.position}</Text>
|
|
{/* Národnost skryta */}
|
|
</Stack>
|
|
))}
|
|
</SimpleGrid>
|
|
</VStack>
|
|
</Container>
|
|
|
|
{/* Newsletter CTA */}
|
|
<NewsletterCTA />
|
|
</Box>
|
|
</MainLayout>
|
|
);
|
|
}
|
|
|
|
export default PlayersPage;
|