Files
MyClub/frontend/src/components/home/PhotosSection.tsx
T
2025-10-24 18:15:36 +02:00

168 lines
5.3 KiB
TypeScript

import { Box, Grid, GridItem, Heading, Image, Button, HStack, Text, VStack, Badge } from '@chakra-ui/react';
import React, { useEffect, useState } from 'react';
import { API_URL } from '../../services/api';
import { Link as RouterLink } from 'react-router-dom';
import { Calendar, Image as ImageIcon } from 'lucide-react';
interface Album {
id: string;
title: string;
url: string;
date: string;
photos_count: number;
views_count?: number;
photos: Array<{
id: string;
page_url: string;
image_1500: string;
}>;
}
// Resolve backend-relative URLs against API origin
const resolveBackendUrl = (path: string) => {
try {
if (/^https?:\/\//i.test(path)) return path;
if (path.startsWith('/cache') || path.startsWith('/uploads') || path.startsWith('/api/')) {
const origin = new URL(API_URL, typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3000').origin;
const abs = new URL(path, origin);
return abs.toString();
}
return path;
} catch { return path; }
};
const PhotosSection: React.FC<{ zoneramaUrl?: string | null }> = ({ zoneramaUrl }) => {
const [albums, setAlbums] = useState<Album[]>([]);
const [loaded, setLoaded] = useState(false);
useEffect(() => {
let active = true;
(async () => {
try {
const response = await fetch(`${API_URL}/gallery/albums`);
if (response.ok) {
const data = await response.json();
if (active) {
// Get 5 most recent albums
setAlbums((data.albums || []).slice(0, 5));
}
}
} catch {
if (active) setAlbums([]);
} finally {
if (active) setLoaded(true);
}
})();
return () => { active = false };
}, []);
const showSetupHint = loaded && albums.length === 0 && !zoneramaUrl;
return (
<Box>
<HStack justify="space-between" mb={3}>
<Heading size="lg">Fotogalerie</Heading>
<Button as={RouterLink} to="/galerie" size="sm" variant="outline">
Zobrazit vše
</Button>
</HStack>
{showSetupHint && (
<Box bg="yellow.50" borderWidth="1px" borderColor="yellow.200" color="yellow.800" p={3} borderRadius="md" mb={3}>
<Text>Žádné fotky nejsou k dispozici. Zadejte prosím odkaz na Zonerama v nastavení (Sociální sítě Fotogalerie) a my ji budeme automaticky načítat.</Text>
</Box>
)}
{/* Zonerama Attribution */}
{albums.length > 0 && (
<Box bg="blue.50" borderWidth="1px" borderColor="blue.200" color="blue.800" p={2} borderRadius="md" mb={3} fontSize="xs">
<Text>
📸 Fotografie z{' '}
<Text
as="a"
href="https://zonerama.com"
target="_blank"
rel="noopener noreferrer"
fontWeight="600"
color="blue.600"
_hover={{ textDecoration: 'underline' }}
>
Zonerama
</Text>
</Text>
</Box>
)}
<Grid templateColumns={{ base: '1fr', md: 'repeat(2, 1fr)', lg: 'repeat(3, 1fr)' }} gap={4}>
{albums.map((album) => {
const coverPhoto = album.photos && album.photos.length > 0 ? album.photos[0] : null;
return (
<GridItem key={album.id}>
<Box
as={RouterLink}
to={`/galerie/album/${album.id}`}
bg="white"
borderRadius="md"
overflow="hidden"
boxShadow="sm"
transition="all 0.2s"
_hover={{
transform: 'translateY(-2px)',
boxShadow: 'md',
}}
cursor="pointer"
display="block"
>
{/* Cover Image */}
{coverPhoto ? (
<Image
src={resolveBackendUrl(coverPhoto.image_1500)}
alt={album.title}
h="180px"
w="100%"
objectFit="cover"
/>
) : (
<Box
h="180px"
w="100%"
bg="gray.200"
display="flex"
alignItems="center"
justifyContent="center"
>
<ImageIcon size={32} color="gray" />
</Box>
)}
{/* Album Info */}
<VStack align="stretch" p={3} spacing={2}>
<Heading size="sm" noOfLines={2} color="gray.800">
{album.title}
</Heading>
<HStack spacing={3} fontSize="xs" color="gray.600">
{album.date && (
<HStack spacing={1}>
<Calendar size={14} />
<Text>{album.date}</Text>
</HStack>
)}
<HStack spacing={1}>
<ImageIcon size={14} />
<Text>{album.photos_count} foto</Text>
</HStack>
</HStack>
</VStack>
</Box>
</GridItem>
);
})}
</Grid>
</Box>
);
};
export default PhotosSection;