mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-05 03:02:56 +00:00
de day #74
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user