import React, { useState, useEffect, useRef } from 'react'; import { Link } from 'react-router-dom'; import { useAuth } from '../../context/AuthContext'; import api from '../../utils/api'; import { Card } from '../../components/ui/card'; import { Button } from '../../components/ui/button'; import { Input } from '../../components/ui/input'; import { Label } from '../../components/ui/label'; import { Textarea } from '../../components/ui/textarea'; import { Badge } from '../../components/ui/badge'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../components/ui/select'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '../../components/ui/dialog'; import { Upload, Trash2, Edit, X, ImageIcon, Calendar, MapPin, AlertCircle } from 'lucide-react'; import { toast } from 'sonner'; import moment from 'moment'; const AdminGallery = () => { const { hasPermission } = useAuth(); const [events, setEvents] = useState([]); const [selectedEvent, setSelectedEvent] = useState(null); const [galleryImages, setGalleryImages] = useState([]); const [loading, setLoading] = useState(true); const [uploading, setUploading] = useState(false); const [editingCaption, setEditingCaption] = useState(null); const [newCaption, setNewCaption] = useState(''); const [maxFileSize, setMaxFileSize] = useState(5242880); // Default 5MB const fileInputRef = useRef(null); useEffect(() => { fetchEvents(); fetchConfigLimits(); }, []); useEffect(() => { if (selectedEvent) { fetchGallery(selectedEvent); } }, [selectedEvent]); const fetchEvents = async () => { try { const response = await api.get('/admin/events'); setEvents(response.data); } catch (error) { console.error('Failed to fetch events:', error); toast.error('Failed to load events'); } finally { setLoading(false); } }; const fetchGallery = async (eventId) => { try { const response = await api.get(`/events/${eventId}/gallery`); setGalleryImages(response.data); } catch (error) { console.error('Failed to fetch gallery:', error); toast.error('Failed to load gallery'); } }; const fetchConfigLimits = async () => { try { const response = await api.get('/config/limits'); setMaxFileSize(response.data.max_file_size_bytes); } catch (error) { console.error('Failed to fetch config limits:', error); // Keep default value (5MB) if fetch fails } }; const formatFileSize = (bytes) => { if (bytes < 1024) return bytes + ' B'; else if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB'; else return (bytes / 1048576).toFixed(1) + ' MB'; }; const handleFileSelect = async (e) => { const files = Array.from(e.target.files); if (files.length === 0) return; setUploading(true); try { for (const file of files) { const formData = new FormData(); formData.append('file', file); await api.post(`/admin/events/${selectedEvent}/gallery`, formData, { headers: { 'Content-Type': 'multipart/form-data' } }); } toast.success(`${files.length} ${files.length === 1 ? 'image' : 'images'} uploaded successfully`); await fetchGallery(selectedEvent); } catch (error) { console.error('Failed to upload images:', error); toast.error(error.response?.data?.detail || 'Failed to upload images'); } finally { setUploading(false); if (fileInputRef.current) { fileInputRef.current.value = ''; } } }; const handleDeleteImage = async (imageId) => { if (!window.confirm('Are you sure you want to delete this image?')) { return; } try { await api.delete(`/admin/event-gallery/${imageId}`); toast.success('Image deleted successfully'); await fetchGallery(selectedEvent); } catch (error) { console.error('Failed to delete image:', error); toast.error('Failed to delete image'); } }; const handleUpdateCaption = async () => { if (!editingCaption) return; try { await api.put(`/admin/event-gallery/${editingCaption.id}`, null, { params: { caption: newCaption } }); toast.success('Caption updated successfully'); setEditingCaption(null); setNewCaption(''); await fetchGallery(selectedEvent); } catch (error) { console.error('Failed to update caption:', error); toast.error('Failed to update caption'); } }; const openEditCaption = (image) => { setEditingCaption(image); setNewCaption(image.caption || ''); }; return (
{/* Header */}

Event Gallery Management

Upload and manage photos for event galleries

{/* Event Selection */}
{/* Empty State Message */} {events.length === 0 && (

No Events Available

You need to create an event before uploading gallery images. Events help organize photos by occasion.

)} {selectedEvent && hasPermission('gallery.upload') && (

You can select multiple images. Max {formatFileSize(maxFileSize)} per image.

)}
{/* Gallery Grid */} {selectedEvent && (

Gallery Images

{galleryImages.length} {galleryImages.length === 1 ? 'image' : 'images'}
{galleryImages.length > 0 ? (
{galleryImages.map((image) => (
{image.caption
{/* Overlay with Actions */} {(hasPermission('gallery.edit') || hasPermission('gallery.delete')) && (
{hasPermission('gallery.edit') && ( )} {hasPermission('gallery.delete') && ( )}
)} {/* Caption Preview */} {image.caption && (

{image.caption}

)} {/* File Size */}

{formatFileSize(image.file_size_bytes)}

))}
) : (

No Images Yet

Upload images to create a gallery for this event.

)}
)} {/* Edit Caption Dialog */} setEditingCaption(null)}> Edit Image Caption {editingCaption && (
Preview