TCA-Admin-Frontend/components/gallery/ListOfUpcomingEvents.tsx

267 lines
14 KiB
TypeScript

'use client';
import axios from 'axios';
import Cookies from 'universal-cookie';
import { Metadata } from 'next';
import Link from 'next/link';
import React, { useEffect, useState } from 'react';
import IconTrashLines from '../icon/icon-trash-lines';
import IconPencil from '../icon/icon-pencil';
import IconEye from '../icon/icon-eye';
import Swal from 'sweetalert2';
import { useRouter } from 'next/navigation';
import { buildApiUrl } from '@/utils/BaseUrl.utils';
export const metadata: Metadata = {
title: 'Upcoming Events',
};
const ListOfUpcomingEvents = () => {
const router = useRouter();
const [events, setEvents] = useState<any[]>([]);
const [selectedEvent, setSelectedEvent] = useState<any | null>(null);
useEffect(() => {
getEvents();
}, []);
const getEvents = async () => {
try {
const cookies = new Cookies();
const token = cookies.get('token');
const eventRes: any = await axios.get(buildApiUrl('upcoming-events'), {
headers: { Authorization: `Bearer ${token}` },
});
console.log('Upcoming Events Res:', eventRes);
if (eventRes?.data?.success) {
setEvents(eventRes?.data?.data || []);
}
} catch (error) {
console.error('Error fetching upcoming events:', error);
}
};
const handleEdit = (event: any) => {
router.push(`/edit-upcoming-event?id=${event.id}`);
};
const handleView = (event: any) => {
setSelectedEvent(event);
};
const showAlert = async (event: any) => {
Swal.fire({
icon: 'warning',
title: 'Are you sure?',
text: `You are about to delete "${event.title || event.eventtitle}". You won't be able to revert this!`,
showCancelButton: true,
confirmButtonText: 'Delete',
padding: '2em',
customClass: { popup: 'sweet-alerts' },
}).then(async (result) => {
if (result.isConfirmed) {
try {
const cookies = new Cookies();
const token = cookies.get('token');
await axios.delete(buildApiUrl(`upcoming-events/${event.id}`), {
headers: { Authorization: `Bearer ${token}` },
});
Swal.fire({
title: 'Deleted!',
text: 'Upcoming event has been deleted.',
icon: 'success',
customClass: { popup: 'sweet-alerts' },
});
getEvents();
} catch (error) {
Swal.fire({
title: 'Error!',
text: 'Failed to delete the upcoming event.',
icon: 'error',
customClass: { popup: 'sweet-alerts' },
});
}
}
});
};
return (
<div>
<div className="mt-10 container">
<div className="flex justify-between items-center mb-6">
<h3 className="text-xl font-bold md:text-3xl">Upcoming Events</h3>
<Link
href="/create-upcoming-event"
className="bg-primary text-white px-4 py-2 rounded-md font-semibold hover:bg-primary/90 transition"
>
+ Add New Event
</Link>
</div>
<div className="grid grid-cols-1 gap-5 sm:grid-cols-2 xl:grid-cols-3">
{/* First box: Create New Upcoming Event */}
<Link
href="/create-upcoming-event"
className="flex flex-col items-center justify-center min-h-[300px] rounded-md border border-dashed border-blue-500 bg-blue-50 p-5 text-center shadow hover:bg-blue-100 transition dark:border-blue-800 dark:bg-blue-900/20 dark:hover:bg-blue-900/40"
>
<div>
<div className="mb-3 text-5xl text-blue-600 dark:text-white">+</div>
<h5 className="text-lg font-semibold text-blue-800 dark:text-white">Create New Upcoming Event</h5>
</div>
</Link>
{/* List dynamic event cards */}
{events.map((event: any, index: number) => {
const title = event.title || event.eventtitle || 'Untitled Event';
const date = event.date || event.eventdate || 'Date TBD';
const time = event.time || '';
const location = event.location || '';
const image = event.image || event.eventimageurl || '/assets/images/placeholder.jpg';
const description = event.description || event.desc || event.eventdescription || '';
const btnText = event.btn_text || event.btnText || 'Details Coming Soon';
return (
<div
key={event.id || index}
className="relative flex flex-col justify-between rounded-md border border-white-light bg-white p-5 shadow-[0px_0px_2px_0px_rgba(145,158,171,0.20),0px_12px_24px_-4px_rgba(145,158,171,0.12)] dark:border-[#1B2E4B] dark:bg-black"
>
{/* Top-right action buttons */}
<div className="absolute top-5 right-5 z-10 flex gap-2">
<button
type="button"
onClick={() => handleView(event)}
className="bg-emerald-600 text-white p-1.5 rounded hover:bg-emerald-700 transition"
title="View Details"
>
<IconEye />
</button>
<button
type="button"
onClick={() => handleEdit(event)}
className="bg-blue-600 text-white p-1.5 rounded hover:bg-blue-700 transition"
title="Edit Event"
>
<IconPencil />
</button>
<button
type="button"
onClick={() => showAlert(event)}
className="bg-red-600 text-white p-1.5 rounded hover:bg-red-700 transition"
title="Delete Event"
>
<IconTrashLines />
</button>
</div>
<div>
{/* Image */}
<div className="max-h-48 overflow-hidden rounded-md mt-0 bg-gray-100 dark:bg-gray-800 flex items-center justify-center">
<img
src={image}
alt={title}
className="w-full h-48 object-cover"
onError={(e: any) => {
e.target.src = 'https://via.placeholder.com/400x200?text=Upcoming+Event';
}}
/>
</div>
{/* Event content */}
<div className="flex-1 mt-4">
<h4 className="text-xl mb-1.5 font-bold text-gray-800 dark:text-white line-clamp-1">
{title}
</h4>
<p className="text-sm font-semibold text-blue-600 dark:text-blue-400 mb-1">
📅 {date} {time ? `| 🕒 ${time}` : ''}
</p>
{location && (
<p className="text-sm text-gray-600 dark:text-gray-400 mb-2">
📍 {location}
</p>
)}
<p className="text-sm text-gray-500 dark:text-gray-400 line-clamp-2 mt-2">
{description}
</p>
</div>
</div>
{/* Footer badge */}
<div className="mt-4 pt-3 border-t border-gray-100 dark:border-gray-800 flex justify-between items-center">
<span className="text-xs font-semibold px-2.5 py-1 rounded bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300">
{btnText}
</span>
</div>
</div>
);
})}
</div>
</div>
{/* View Modal */}
{selectedEvent && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4">
<div className="relative w-full max-w-2xl rounded-lg bg-white p-6 shadow-xl dark:bg-gray-900 max-h-[90vh] overflow-y-auto">
<button
type="button"
onClick={() => setSelectedEvent(null)}
className="absolute top-4 right-4 text-gray-500 hover:text-black dark:hover:text-white text-2xl font-bold"
>
&times;
</button>
<h3 className="text-2xl font-bold mb-4 pr-8 text-gray-900 dark:text-white">
{selectedEvent.title || selectedEvent.eventtitle}
</h3>
{(selectedEvent.image || selectedEvent.eventimageurl) && (
<img
src={selectedEvent.image || selectedEvent.eventimageurl}
alt={selectedEvent.title}
className="w-full max-h-64 object-cover rounded-md mb-4"
onError={(e: any) => {
e.target.onerror = null;
e.target.src = 'https://via.placeholder.com/400x200?text=No+Image';
}}
/>
)}
<div className="space-y-3 text-sm text-gray-700 dark:text-gray-300">
<p><strong>Date:</strong> {selectedEvent.date || selectedEvent.eventdate || 'N/A'}</p>
{selectedEvent.time && <p><strong>Time:</strong> {selectedEvent.time}</p>}
{selectedEvent.location && <p><strong>Location:</strong> {selectedEvent.location}</p>}
{selectedEvent.admission && <p><strong>Admission:</strong> {selectedEvent.admission}</p>}
{selectedEvent.link && <p><strong>Link:</strong> {selectedEvent.link}</p>}
<p><strong>Button Text:</strong> {selectedEvent.btn_text || selectedEvent.btnText || 'Details Coming Soon'}</p>
<div className="pt-2 border-t border-gray-200 dark:border-gray-700">
<strong>Description:</strong>
<p className="mt-1 whitespace-pre-line">{selectedEvent.description || selectedEvent.desc || selectedEvent.eventdescription || 'No description provided.'}</p>
</div>
</div>
<div className="mt-6 flex justify-end gap-2">
<button
type="button"
onClick={() => {
const ev = selectedEvent;
setSelectedEvent(null);
handleEdit(ev);
}}
className="bg-blue-600 text-white px-4 py-2 rounded font-semibold hover:bg-blue-700"
>
Edit Event
</button>
<button
type="button"
onClick={() => setSelectedEvent(null)}
className="bg-gray-500 text-white px-4 py-2 rounded font-semibold hover:bg-gray-600"
>
Close
</button>
</div>
</div>
</div>
)}
</div>
);
};
export default ListOfUpcomingEvents;