import React from 'react'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { Camera, Loader2, Sparkles, Trash2, AlertTriangle } from 'lucide-react'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { AdminLayout } from '../components/AdminLayout'; import { deletePhoto, featurePhoto, getEventPhotos, TenantPhoto, unfeaturePhoto } from '../api'; import { isAuthError } from '../auth/tokens'; import { getApiErrorMessage, isApiError } from '../lib/apiError'; import { buildLimitWarnings, type EventLimitSummary } from '../lib/limitWarnings'; import { useTranslation } from 'react-i18next'; import { ADMIN_EVENTS_PATH, ADMIN_EVENT_VIEW_PATH } from '../constants'; export default function EventPhotosPage() { const params = useParams<{ slug?: string }>(); const [searchParams] = useSearchParams(); const slug = params.slug ?? searchParams.get('slug') ?? null; const navigate = useNavigate(); const { t } = useTranslation('management'); const { t: tCommon } = useTranslation('common'); const translateLimits = React.useCallback( (key: string, options?: Record) => tCommon(`limits.${key}`, options), [tCommon] ); const [photos, setPhotos] = React.useState([]); const [loading, setLoading] = React.useState(true); const [error, setError] = React.useState(undefined); const [busyId, setBusyId] = React.useState(null); const [limits, setLimits] = React.useState(null); const load = React.useCallback(async () => { if (!slug) { setLoading(false); return; } setLoading(true); setError(undefined); try { const result = await getEventPhotos(slug); setPhotos(result.photos); setLimits(result.limits ?? null); } catch (err) { if (!isAuthError(err)) { setError(getApiErrorMessage(err, 'Fotos konnten nicht geladen werden.')); } } finally { setLoading(false); } }, [slug]); React.useEffect(() => { load(); }, [load]); async function handleToggleFeature(photo: TenantPhoto) { if (!slug) return; setBusyId(photo.id); try { const updated = photo.is_featured ? await unfeaturePhoto(slug, photo.id) : await featurePhoto(slug, photo.id); setPhotos((prev) => prev.map((entry) => (entry.id === photo.id ? updated : entry))); } catch (err) { if (!isAuthError(err)) { setError(getApiErrorMessage(err, 'Feature-Aktion fehlgeschlagen.')); } } finally { setBusyId(null); } } async function handleDelete(photo: TenantPhoto) { if (!slug) return; setBusyId(photo.id); try { await deletePhoto(slug, photo.id); setPhotos((prev) => prev.filter((entry) => entry.id !== photo.id)); } catch (err) { if (!isAuthError(err)) { setError(getApiErrorMessage(err, 'Foto konnte nicht entfernt werden.')); } } finally { setBusyId(null); } } if (!slug) { return ( Kein Slug in der URL gefunden. Kehre zur Event-Liste zurück und wähle dort ein Event aus. ); } const actions = ( ); return ( {error && ( {t('photos.alerts.errorTitle', 'Aktion fehlgeschlagen')} {error} )} {t('photos.gallery.title', 'Galerie')} {t('photos.gallery.description', 'Klick auf ein Foto, um es hervorzuheben oder zu löschen.')} {loading ? ( ) : photos.length === 0 ? ( ) : (
{photos.map((photo) => (
{photo.original_name {photo.is_featured && ( Featured )}
Likes: {photo.likes_count} Uploader: {photo.uploader_name ?? 'Unbekannt'}
))}
)}
); } function LimitWarningsBanner({ limits, translate, }: { limits: EventLimitSummary | null; translate: (key: string, options?: Record) => string; }) { const warnings = React.useMemo(() => buildLimitWarnings(limits, translate), [limits, translate]); if (!warnings.length) { return null; } return (
{warnings.map((warning) => ( {warning.message} ))}
); } function GallerySkeleton() { return (
{Array.from({ length: 6 }).map((_, index) => (
))}
); } function EmptyGallery({ title, description }: { title: string; description: string }) { return (

{title}

{description}

); }