mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 18:52:56 +00:00
upload
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
import { Box, Grid, GridItem, Heading, Image, Button, HStack, Text, VStack, Badge } from '@chakra-ui/react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
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 base = (process.env.REACT_APP_API_BASE_URL || process.env.REACT_APP_API_URL || 'http://localhost:8080/api/v1');
|
||||
const b = new URL(base);
|
||||
const abs = new URL(path, `${b.protocol}//${b.host}`);
|
||||
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 apiUrl = process.env.REACT_APP_API_URL || 'http://localhost:8080/api/v1';
|
||||
const response = await fetch(`${apiUrl}/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;
|
||||
Reference in New Issue
Block a user