127 lines
4.7 KiB
TypeScript
127 lines
4.7 KiB
TypeScript
import React from 'react';
|
||
import { Link } from 'react-router-dom';
|
||
import { Card, CardContent } from '@/components/ui/card';
|
||
import { Button } from '@/components/ui/button';
|
||
import { getDeviceId } from '../lib/device';
|
||
import { usePollGalleryDelta } from '../polling/usePollGalleryDelta';
|
||
|
||
type Props = { slug: string };
|
||
|
||
export default function GalleryPreview({ slug }: Props) {
|
||
const { photos, loading } = usePollGalleryDelta(slug);
|
||
const [mode, setMode] = React.useState<'latest' | 'popular' | 'myphotos'>('latest');
|
||
|
||
const items = React.useMemo(() => {
|
||
let arr = photos.slice();
|
||
|
||
// MyPhotos filter (requires session_id matching)
|
||
if (mode === 'myphotos') {
|
||
const deviceId = getDeviceId();
|
||
arr = arr.filter((photo: any) => photo.session_id === deviceId);
|
||
}
|
||
|
||
// Sorting
|
||
if (mode === 'popular') {
|
||
arr.sort((a: any, b: any) => (b.likes_count ?? 0) - (a.likes_count ?? 0));
|
||
} else {
|
||
arr.sort((a: any, b: any) => new Date(b.created_at ?? 0).getTime() - new Date(a.created_at ?? 0).getTime());
|
||
}
|
||
|
||
return arr.slice(0, 4); // 2x2 = 4 items
|
||
}, [photos, mode]);
|
||
|
||
// Helper function to generate photo title (must be before return)
|
||
function getPhotoTitle(photo: any): string {
|
||
if (photo.task_id) {
|
||
return `Task: ${photo.task_title || 'Unbekannte Aufgabe'}`;
|
||
}
|
||
if (photo.emotion_id) {
|
||
return `Emotion: ${photo.emotion_name || 'Gefühl'}`;
|
||
}
|
||
// Fallback based on creation time or placeholder
|
||
const now = new Date();
|
||
const created = new Date(photo.created_at || now);
|
||
const hours = created.getHours();
|
||
if (hours < 12) return 'Morgenmoment';
|
||
if (hours < 18) return 'Nachmittagslicht';
|
||
return 'Abendstimmung';
|
||
}
|
||
|
||
return (
|
||
<div>
|
||
<div className="mb-4 flex items-center justify-between">
|
||
<div className="inline-flex rounded-full bg-white/80 backdrop-blur-sm border border-pink-200 p-1 shadow-sm">
|
||
<button
|
||
onClick={() => setMode('latest')}
|
||
className={`px-4 py-2 text-sm font-medium rounded-full transition-all duration-200 ${
|
||
mode === 'latest'
|
||
? 'bg-gradient-to-r from-pink-500 to-pink-600 text-white shadow-lg scale-105'
|
||
: 'text-gray-600 hover:bg-pink-50 hover:text-pink-700'
|
||
}`}
|
||
>
|
||
Newest
|
||
</button>
|
||
<button
|
||
onClick={() => setMode('popular')}
|
||
className={`px-4 py-2 text-sm font-medium rounded-full transition-all duration-200 ${
|
||
mode === 'popular'
|
||
? 'bg-gradient-to-r from-pink-500 to-pink-600 text-white shadow-lg scale-105'
|
||
: 'text-gray-600 hover:bg-pink-50 hover:text-pink-700'
|
||
}`}
|
||
>
|
||
Popular
|
||
</button>
|
||
<button
|
||
onClick={() => setMode('myphotos')}
|
||
className={`px-4 py-2 text-sm font-medium rounded-full transition-all duration-200 ${
|
||
mode === 'myphotos'
|
||
? 'bg-gradient-to-r from-pink-500 to-pink-600 text-white shadow-lg scale-105'
|
||
: 'text-gray-600 hover:bg-pink-50 hover:text-pink-700'
|
||
}`}
|
||
>
|
||
My Photos
|
||
</button>
|
||
</div>
|
||
<Link to={`/e/${slug}/gallery?mode=${mode}`} className="text-sm text-pink-600 hover:text-pink-700 font-medium">
|
||
Alle ansehen →
|
||
</Link>
|
||
</div>
|
||
|
||
{loading && <p className="text-sm text-muted-foreground">Lädt…</p>}
|
||
{!loading && items.length === 0 && (
|
||
<Card>
|
||
<CardContent className="p-3 text-sm text-muted-foreground">
|
||
Noch keine Fotos. Starte mit deinem ersten Upload!
|
||
</CardContent>
|
||
</Card>
|
||
)}
|
||
<div className="grid grid-cols-2 gap-3">
|
||
{items.map((p: any) => (
|
||
<Link key={p.id} to={`/e/${slug}/photo/${p.id}`} state={{ photo: p }} className="block">
|
||
<div className="relative">
|
||
<img
|
||
src={p.thumbnail_path || p.file_path}
|
||
alt={p.title || 'Foto'}
|
||
className="aspect-square w-full rounded-xl object-cover shadow-lg hover:shadow-xl transition-shadow duration-200"
|
||
loading="lazy"
|
||
/>
|
||
{/* Photo Title */}
|
||
<div className="mt-2">
|
||
<div className="text-xs font-medium text-gray-900 line-clamp-2 bg-white/80 px-2 py-1 rounded-md">
|
||
{p.title || getPhotoTitle(p)}
|
||
</div>
|
||
{p.likes_count > 0 && (
|
||
<div className="mt-1 flex items-center gap-1 text-xs text-pink-600">
|
||
❤️ {p.likes_count}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</Link>
|
||
))}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|