Files
MyClub/frontend/src/components/widgets/ArticlesWidget.tsx
T
Tomáš Dvořák 12cba639b9 upload
2025-10-16 13:32:05 +02:00

144 lines
4.7 KiB
TypeScript

import { Box, Text, VStack, HStack, Image, Skeleton, Link as ChakraLink, Icon } from '@chakra-ui/react';
import { FaNewspaper, FaUser, FaCalendarAlt } from 'react-icons/fa';
import { Link as RouterLink } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { api } from '../../services/api';
import { Widget } from './Widget';
import { format, parseISO } from 'date-fns';
import { cs } from 'date-fns/locale';
import { Article } from '../../types';
export const ArticlesWidget = () => {
const { data: articles = [], isLoading, error } = useQuery<Article[]>({
queryKey: ['recentArticles'],
queryFn: async () => {
try {
const { data } = await api.get('/articles', {
params: {
limit: 3,
include: 'author',
sort: '-createdAt',
published: true
}
});
return data.data || [];
} catch (err) {
console.error('Error fetching articles:', err);
return [];
}
},
staleTime: 5 * 60 * 1000, // 5 minutes
});
if (isLoading) {
return (
<Widget title="Poslední články" icon={FaNewspaper}>
<VStack spacing={4} align="stretch">
{[1, 2, 3].map((i) => (
<Box key={i}>
<Skeleton height="120px" mb={2} borderRadius="md" />
<Skeleton height="20px" mb={2} width="80%" />
<Skeleton height="16px" width="60%" />
</Box>
))}
</VStack>
</Widget>
);
}
if (error || !articles.length) {
return (
<Widget title="Poslední články" icon={FaNewspaper}>
<VStack p={4} spacing={4}>
<Box p={4} bg="gray.50" borderRadius="md" textAlign="center" w="full">
<Icon as={FaNewspaper} boxSize={6} color="gray.400" mb={2} />
<Text color="gray.500">Žádné články nebyly nalezeny</Text>
</Box>
</VStack>
</Widget>
);
}
return (
<Widget title="Poslední články" icon={FaNewspaper}>
<VStack spacing={3} align="stretch" divider={<Box borderBottomWidth="1px" borderColor="gray.100" />}>
{articles.map((article) => (
<ChakraLink
key={article.id}
as={RouterLink}
to={`/clanky/${article.slug}`}
_hover={{ textDecoration: 'none' }}
display="block"
>
<Box _hover={{ bg: 'gray.50' }} borderRadius="md" p={2}>
<HStack align="flex-start" spacing={3}>
<Box
flexShrink={0}
width="60px"
height="60px"
bg="gray.100"
borderRadius="md"
overflow="hidden"
position="relative"
>
{article.imageUrl ? (
<Image
src={article.imageUrl}
alt={article.title}
width="100%"
height="100%"
objectFit="cover"
/>
) : (
<Box
width="100%"
height="100%"
display="flex"
alignItems="center"
justifyContent="center"
bg="gray.200"
>
<Icon as={FaNewspaper} color="gray.400" boxSize={5} />
</Box>
)}
</Box>
<Box flex={1} minW={0}>
<Text fontWeight="medium" fontSize="sm" noOfLines={2} mb={1}>
{article.title}
</Text>
<HStack spacing={3} fontSize="xs" color="gray.500">
<HStack spacing={1}>
<Icon as={FaUser} boxSize={3} />
<Text>{article.author.name}</Text>
</HStack>
<HStack spacing={1}>
<Icon as={FaCalendarAlt} boxSize={3} />
<Text>
{format(parseISO(article.createdAt), 'd. M. yyyy', {
locale: cs,
})}
</Text>
</HStack>
</HStack>
</Box>
</HStack>
</Box>
</ChakraLink>
))}
<Box textAlign="right" mt={2}>
<ChakraLink
as={RouterLink}
to="/admin/clanky"
color="blue.500"
fontWeight="medium"
fontSize="sm"
_hover={{ textDecoration: 'underline' }}
>
Zobrazit všechny články
</ChakraLink>
</Box>
</VStack>
</Widget>
);
};