263 lines
13 KiB
TypeScript
263 lines
13 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"
|
|
>
|
|
×
|
|
</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"
|
|
/>
|
|
)}
|
|
|
|
<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;
|