mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 10:42:57 +00:00
upload
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
import { Box, Heading, SimpleGrid, Image, Text, VStack, HStack, Button, Skeleton, Badge, useColorModeValue } from '@chakra-ui/react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getArticles, Article } from '../../services/articles';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
import { assetUrl } from '../../utils/url';
|
||||
|
||||
const BlogCard: React.FC<{ article: Article }> = ({ article }) => {
|
||||
const link = article.slug ? `/news/${article.slug}` : `/articles/${article.id}`;
|
||||
const cardBg = useColorModeValue('white', 'gray.800');
|
||||
const border = useColorModeValue('gray.200', 'whiteAlpha.300');
|
||||
const categoryName = (article as any)?.category?.name || '';
|
||||
|
||||
return (
|
||||
<VStack
|
||||
as={RouterLink}
|
||||
to={link}
|
||||
align="stretch"
|
||||
spacing={0}
|
||||
borderWidth="1px"
|
||||
borderRadius="xl"
|
||||
bg={cardBg}
|
||||
overflow="hidden"
|
||||
boxShadow="lg"
|
||||
borderColor={border}
|
||||
_hover={{ boxShadow: '2xl', transform: 'translateY(-4px)' }}
|
||||
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
||||
>
|
||||
<Box position="relative" overflow="hidden">
|
||||
<Image
|
||||
src={assetUrl(article.image_url) || '/logo192.png'}
|
||||
alt={article.title}
|
||||
objectFit="cover"
|
||||
w="100%"
|
||||
h="200px"
|
||||
transition="transform 0.3s ease"
|
||||
_groupHover={{ transform: 'scale(1.05)' }}
|
||||
/>
|
||||
{categoryName && (
|
||||
<Badge
|
||||
position="absolute"
|
||||
top={3}
|
||||
left={3}
|
||||
colorScheme="blue"
|
||||
fontSize="xs"
|
||||
px={3}
|
||||
py={1}
|
||||
borderRadius="full"
|
||||
textTransform="uppercase"
|
||||
fontWeight="bold"
|
||||
>
|
||||
{categoryName}
|
||||
</Badge>
|
||||
)}
|
||||
</Box>
|
||||
<VStack align="stretch" spacing={3} p={5}>
|
||||
<Heading size="md" noOfLines={2} lineHeight="1.3">{article.title}</Heading>
|
||||
<Text noOfLines={3} color="gray.600" fontSize="sm" lineHeight="1.5">
|
||||
{article.content?.replace(/<[^>]*>/g, '').slice(0, 160)}
|
||||
</Text>
|
||||
<HStack spacing={2} pt={2} borderTopWidth="1px" borderColor={border}>
|
||||
{article.estimated_read_minutes && (
|
||||
<Text fontSize="xs" color="gray.500">
|
||||
{article.estimated_read_minutes} min čtení
|
||||
</Text>
|
||||
)}
|
||||
{article.published_at && (
|
||||
<Text fontSize="xs" color="gray.500">
|
||||
• {new Date(article.published_at).toLocaleDateString('cs-CZ')}
|
||||
</Text>
|
||||
)}
|
||||
</HStack>
|
||||
</VStack>
|
||||
</VStack>
|
||||
);
|
||||
};
|
||||
|
||||
const BlogGrid: React.FC = () => {
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['articles', { page: 1, page_size: 10, published: true }],
|
||||
queryFn: () => getArticles({ page: 1, page_size: 10, published: true }),
|
||||
});
|
||||
|
||||
const articles = data?.data || [];
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<HStack justify="space-between" mb={4}>
|
||||
<Heading size="lg">Aktuality</Heading>
|
||||
<Button as={RouterLink} to="/blog" size="sm" variant="link">Zobrazit všechny</Button>
|
||||
</HStack>
|
||||
<SimpleGrid columns={{ base: 1, sm: 2, md: 3 }} spacing={6}>
|
||||
{isLoading && Array.from({ length: 6 }).map((_, i) => (
|
||||
<Skeleton key={i} height="240px" />
|
||||
))}
|
||||
{!isLoading && articles.map((a) => (
|
||||
<BlogCard key={a.id} article={a} />
|
||||
))}
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default BlogGrid;
|
||||
Reference in New Issue
Block a user