mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 10:42:57 +00:00
114 lines
3.8 KiB
TypeScript
114 lines
3.8 KiB
TypeScript
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';
|
|
import InstagramGeneratorButton from '../admin/InstagramGeneratorButton';
|
|
|
|
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)"
|
|
position="relative"
|
|
>
|
|
<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>
|
|
<Box position="absolute" top={2} right={2} zIndex={2}>
|
|
<InstagramGeneratorButton
|
|
article={article as any}
|
|
targetUrl={typeof window !== 'undefined' ? new URL(link, window.location.origin).toString() : undefined}
|
|
placement="inline"
|
|
size="sm"
|
|
/>
|
|
</Box>
|
|
</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;
|