This commit is contained in:
Tomas Dvorak
2025-10-28 22:38:27 +01:00
parent 3d621e2187
commit 823fabee02
106 changed files with 9011 additions and 3930 deletions
+11 -2
View File
@@ -1,8 +1,9 @@
import { Box, Flex, Heading, Image, HStack, Button, Text } from '@chakra-ui/react';
import { Box, Flex, Heading, HStack, Button, Text } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import { facrApi } from '../../services/facr/facrApi';
import { FACR_CLUB_ID, FACR_CLUB_TYPE } from '../../config/facr';
import { usePublicSettings } from '../../hooks/usePublicSettings';
import { TeamLogo } from '../common/TeamLogo';
const ClubHeader: React.FC = () => {
const { data: settings } = usePublicSettings();
@@ -18,7 +19,15 @@ const ClubHeader: React.FC = () => {
return (
<Flex align="center" justify="space-between" bg="white" borderWidth="1px" borderRadius="lg" p={4}>
<HStack spacing={4}>
<Image src={data?.logo_url || '/logo192.png'} alt={data?.name || 'Club'} boxSize="64px" objectFit="contain" />
<TeamLogo
teamId={clubId}
teamName={data?.name}
facrLogo={data?.logo_url || undefined}
size="custom"
boxSize="64px"
alt={data?.name || 'Club'}
borderRadius="full"
/>
<Box>
<Heading size="lg">{data?.name || 'Club Name'}</Heading>
<Text color="gray.600" fontSize="sm">
@@ -0,0 +1,44 @@
import React from 'react';
import { usePublicSettings } from '../../hooks/usePublicSettings';
import { useClubTheme } from '../../contexts/ClubThemeContext';
import { assetUrl } from '../../utils/url';
export type ClubHeroTopbarVariant = 'brand' | 'minimal' | 'badge';
const cls = (...parts: Array<string | false | null | undefined>) => parts.filter(Boolean).join(' ');
const ClubHeroTopbar: React.FC<{ variant?: ClubHeroTopbarVariant; fullBleed?: boolean }>= ({ variant = 'brand', fullBleed = false }) => {
const { data: settings } = usePublicSettings();
const theme = useClubTheme();
const title = settings?.club_name || theme.name || 'Fotbalový klub';
const tagline = 'Oficiální web klubu';
const logo = assetUrl(settings?.club_logo_url || theme.logoUrl) || settings?.club_logo_url || theme.logoUrl || '/dist/img/logo-club-empty.svg';
const shopUrl = settings?.shop_url || undefined;
const calendarUrl = '/kalendar';
return (
<div className={cls('club-hero-topbar', fullBleed && 'full-bleed',
variant === 'brand' && 'club-hero-topbar--brand',
variant === 'minimal' && 'club-hero-topbar--minimal',
variant === 'badge' && 'club-hero-topbar--badge'
)}>
<div className="club-hero-topbar__logo">
{/* eslint-disable-next-line jsx-a11y/alt-text */}
<img src={logo} alt={title} style={{ width: 36, height: 36, objectFit: 'contain' }} />
</div>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<div className="club-hero-topbar__title">{title}</div>
<div className="club-hero-topbar__tagline">{tagline}</div>
</div>
<div className="club-hero-topbar__spacer" />
<div className="club-hero-topbar__actions">
<a href={calendarUrl} className="sparta-button-tertiary">Kalendář</a>
{shopUrl && (
<a href={shopUrl} target="_blank" rel="noreferrer" className="sparta-button-primary">Fanshop</a>
)}
</div>
</div>
);
};
export default ClubHeroTopbar;
@@ -139,11 +139,19 @@ const CompetitionMatches: React.FC = () => {
return (
<Box>
<Tabs variant="soft-rounded" colorScheme="blue" isFitted>
<TabList>
<Tabs variant="soft-rounded" colorScheme="blue" size="sm">
<TabList px={2} pt={2} overflowX="auto" overflowY="hidden" css={{
'&::-webkit-scrollbar': { height: '4px' },
'&::-webkit-scrollbar-track': { background: 'transparent' },
'&::-webkit-scrollbar-thumb': { background: 'gray.300', borderRadius: '4px' },
}}>
{sortedCompetitions.map((c) => {
const label = c.alias || c.name;
return <Tab key={c.id}>{label}</Tab>;
return (
<Tab key={c.id} flex="0 0 auto" px={3} py={2} fontSize="sm">
<Text as="span" noOfLines={1} maxW="220px" title={label}>{label}</Text>
</Tab>
);
})}
</TabList>
<TabPanels>
@@ -1,6 +1,7 @@
import React from 'react';
import { Box, Flex, HStack, Image, Text, Container, useColorModeValue } from '@chakra-ui/react';
import { Link as RouterLink } from 'react-router-dom';
import { assetUrl } from '../../utils/url';
interface HeaderVariantsProps {
variant: 'unified' | 'edge' | 'minimal' | 'modern';
@@ -15,9 +16,9 @@ const HeaderVariants: React.FC<HeaderVariantsProps> = ({
clubLogo,
clubId,
}) => {
const displayLogo = clubId
const displayLogo = (assetUrl(clubLogo) || clubLogo) || (clubId
? `http://logoapi.sportcreative.eu/logos/${clubId}?format=svg`
: clubLogo || '/images/club-logo.png';
: '/images/club-logo.png');
// Unified variant - classic header
if (variant === 'unified') {
@@ -99,10 +99,16 @@ const MatchesSection: React.FC = () => {
)}
{isLoading && <Skeleton height="200px" />}
{!isLoading && data && (
<Tabs variant="enclosed-colored" isFitted>
<TabList>
<Tabs variant="enclosed-colored" size="sm">
<TabList px={2} pt={2} overflowX="auto" overflowY="hidden" css={{
'&::-webkit-scrollbar': { height: '4px' },
'&::-webkit-scrollbar-track': { background: 'transparent' },
'&::-webkit-scrollbar-thumb': { background: 'gray.300', borderRadius: '4px' },
}}>
{data.competitions?.map((c) => (
<Tab key={c.id}>{c.name}</Tab>
<Tab key={c.id} flex="0 0 auto" px={3} py={2} fontSize="sm">
<Text as="span" noOfLines={1} maxW="220px" title={c.name}>{c.name}</Text>
</Tab>
))}
</TabList>
<TabPanels>
+30 -5
View File
@@ -44,6 +44,13 @@ const TableSection: React.FC = () => {
const rankTopText = useColorModeValue('green.800', 'white');
const pointsBg = useColorModeValue('blue.600', 'blue.400');
const pointsText = 'white';
// TabList theming constants (hoisted to avoid hooks in loops/conditions)
const tabListBg = useColorModeValue('white', 'gray.800');
const tabListBorder = useColorModeValue('gray.200', 'gray.700');
const tabSelectedBg = useColorModeValue('blue.50', 'blue.900');
const tabSelectedColor = useColorModeValue('blue.700', 'blue.200');
const tabSelectedBorderColor = useColorModeValue('blue.200', 'blue.600');
const tabColor = useColorModeValue('gray.800', 'gray.200');
const { data, isLoading, isError, error } = useQuery({
queryKey: ['facr-table', clubId, clubType],
queryFn: () => facrApi.getClubTable(clubId, clubType),
@@ -135,15 +142,33 @@ const TableSection: React.FC = () => {
</HStack>
)}
{!isLoading && !isError && data && data.competitions?.length > 0 && (
<Tabs variant="enclosed" colorScheme="blue" isFitted>
<TabList bg={useColorModeValue('white', 'gray.800')} borderRadius="md" borderWidth="1px" borderColor={useColorModeValue('gray.200', 'gray.700')}>
<Tabs variant="enclosed" colorScheme="blue" size="sm">
<TabList
bg={tabListBg}
borderRadius="md"
borderWidth="1px"
borderColor={tabListBorder}
px={2}
pt={2}
overflowX="auto"
overflowY="hidden"
css={{
'&::-webkit-scrollbar': { height: '4px' },
'&::-webkit-scrollbar-track': { background: 'transparent' },
'&::-webkit-scrollbar-thumb': { background: 'var(--chakra-colors-gray-300)', borderRadius: '4px' },
}}
>
{data.competitions?.map((c) => (
<Tab
key={c.id}
_selected={{ bg: useColorModeValue('blue.50', 'blue.900'), color: useColorModeValue('blue.700', 'blue.200'), borderColor: useColorModeValue('blue.200', 'blue.600') }}
color={useColorModeValue('gray.800', 'gray.200')}
_selected={{ bg: tabSelectedBg, color: tabSelectedColor, borderColor: tabSelectedBorderColor }}
color={tabColor}
flex="0 0 auto"
px={3}
py={2}
fontSize="sm"
>
{c.name}
<Text as="span" noOfLines={1} maxW="240px" title={c.name}>{c.name}</Text>
</Tab>
))}
</TabList>
@@ -1,6 +1,7 @@
import { Box, Heading, HStack, VStack, Image, Text, useColorModeValue } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import { getPlayers, Player } from '../../services/players';
import { assetUrl } from '../../utils/url';
const TeamScroller: React.FC = () => {
const { data } = useQuery({ queryKey: ['players'], queryFn: getPlayers });
@@ -13,7 +14,7 @@ const TeamScroller: React.FC = () => {
<HStack spacing={6} overflowX="auto" py={2} className="hide-scrollbar">
{players.map((p: Player) => (
<VStack key={p.id} minW="160px" spacing={2} bg={useColorModeValue('white', 'gray.800')} borderRadius="xl" p={4} boxShadow="sm" borderWidth="1px" borderColor={useColorModeValue('gray.200', 'gray.700')}>
<Image src={p.image_url || '/logo192.png'} alt={p.first_name + ' ' + p.last_name} w="140px" h="140px" objectFit="cover" borderRadius="lg" />
<Image src={assetUrl(p.image_url) || '/logo192.png'} alt={p.first_name + ' ' + p.last_name} w="140px" h="140px" objectFit="cover" borderRadius="lg" fallbackSrc="/dist/img/logo-club-empty.svg" />
<Text fontWeight="bold" textAlign="center">{p.first_name} {p.last_name}</Text>
<Text fontSize="sm" color={useColorModeValue('gray.600', 'gray.400')}>{p.position}</Text>
</VStack>