Files
MyClub/frontend/src/pages/PlayersPage.tsx
T
Tomas Dvorak 8762bde4bf dev day #89
2025-11-11 10:29:30 +01:00

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;