diff --git a/client/src/pages/EventDetailsPage.tsx b/client/src/pages/EventDetailsPage.tsx index 288fa8a..d13b60e 100644 --- a/client/src/pages/EventDetailsPage.tsx +++ b/client/src/pages/EventDetailsPage.tsx @@ -8,6 +8,11 @@ import { CardBody, Button, CardFooter, + Modal, + ModalContent, + ModalHeader, + ModalBody, + ModalFooter, } from "@nextui-org/react"; import { ArrowUTurnLeftIcon } from "../icons"; // Make sure this path is correct @@ -15,153 +20,292 @@ const EventDetailsPage = () => { const { id } = useParams<{ id: string }>(); // Get the event ID from the URL const [event, setEvent] = useState(null); // State to store event details const [similarEvents, setSimilarEvents] = useState([]); // State to store similar events + const [isSuccessModalOpen, setIsSuccessModalOpen] = useState(false); + const [isRegistering, setIsRegistering] = useState(false); + const [isAlreadyRegisteredModalOpen, setIsAlreadyRegisteredModalOpen] = useState(false); + const [hasRegistered, setHasRegistered] = useState(false); const navigate = useNavigate(); - useEffect(() => { - const fetchEventDetails = async () => { - try { - console.log("Fetching event details for ID:", id); // Debug log - const res = await instance.get(`${config.serverAddress}/events/${id}`); + let accessToken = localStorage.getItem("accessToken"); + if (!accessToken) { + navigate("/signin"); + } + const fetchEventDetails = () => { + try { + console.log("Fetching event details for ID:", id); // Debug log + instance.get(`${config.serverAddress}/events/${id}`).then((res) => { console.log("Fetched event details:", res.data); // Log the fetched data + if (res.data.slotsAvailable === 0) { + navigate("/events"); // Redirect if there are no slots available + return; + } setEvent(res.data); + // Log the image data to debug + console.log("Event image data:", res.data.evtPicture); + // Fetch all events to find similar ones - const allEventsRes = await instance.get( + instance.get( `${config.serverAddress}/events` - ); - const allEvents = allEventsRes.data; + ).then((allEventsRes) => { + const allEvents = allEventsRes.data; - // Find similar events based on location and category, excluding the current event - const similar = allEvents.filter( - (e: any) => - e.id !== Number(id) && // Make sure to exclude the current event - (e.location === res.data.location || - e.category === res.data.category) - ); + // Find similar events based on location and category, excluding the current event + const similar = allEvents.filter( + (e: any) => + e.id !== Number(id) && // Make sure to exclude the current event + e.slotsAvailable > 0 && // Ensure similar events have available slots + (e.location === res.data.location || + e.category === res.data.category) + ); - setSimilarEvents(similar); - } catch (error) { - console.error("Failed to fetch event details:", error); - } - }; + console.log("Similar events found:", similar); + setSimilarEvents(similar); + }); + + // Check if the user has already registered + const registeredStatus = localStorage.getItem(`hasRegistered_${id}`); + if (registeredStatus === 'true') { + setHasRegistered(true); + } else { + setHasRegistered(false); + } + }); + } catch (error) { + console.error("Failed to fetch event details:", error); + } + }; + + useEffect(() => { fetchEventDetails(); }, [id]); - if (!event) return

Loading...

; + const handleRegister = async () => { + console.log("Attempting to register..."); + console.log("Current hasRegistered value:", hasRegistered); + + if (hasRegistered) { + console.log("User has already registered, opening modal..."); + setIsAlreadyRegisteredModalOpen(true); + return; + } + + setIsRegistering(true); + + try { + // Make a request to register for the event + const response = await instance.post(`${config.serverAddress}/events/register/${id}`); + + // Check if registration was successful + if (response.status === 200) { + // Fetch updated event details to reflect the new number of available slots + const updatedEventRes = await instance.get(`${config.serverAddress}/events/${id}`); + setEvent(updatedEventRes.data); + + console.log("Registration successful"); + setHasRegistered(true); + localStorage.setItem(`hasRegistered_${id}`, 'true'); // Save registration status in local storage + + setIsSuccessModalOpen(true); + } else if (response.status === 400) { + // Handle case where the user is already registered + setIsAlreadyRegisteredModalOpen(true); + } + } catch (error) { + console.error("Registration failed:", error); + // Handle different types of errors + if (error) { + console.error("Server response error:", error); + } else { + console.error("Network error:", error); + } + } finally { + setIsRegistering(false); + } + + console.log("Updated hasRegistered value:", hasRegistered); + }; return ( -
- - -
- {/* Event Image Section */} - {event.evtPicture && ( -
- Event Picture + <> + {event &&
+ + +
+ {/* Event Image Section */} + {event.evtPicture && ( +
+ Event Picture +
+ )} + {/* Event Details Section */} +
+ +

{event.title}

+
+ +

{event.description}

+

+ Date: {new Date(event.date).toLocaleDateString()} +

+

+ Time: {event.time} +

+

+ Location: {event.location} +

+

+ Category: {event.category} +

+

+ Slots Available: {event.slotsAvailable} +

+ +
- )} - {/* Event Details Section */} -
- -

{event.title}

-
- -

{event.description}

-

- Date: {new Date(event.date).toLocaleDateString()} -

-

- Time: {event.time} -

-

- Location: {event.location} -

-

- Category: {event.category} -

-

- Slots Available: {event.slotsAvailable} -

- -
+
+ + + {/* Similar Events Section */} +
+

+ Similar Events You Might Be Interested In +

+
+ {similarEvents.length === 0 ? ( +

No similar events available.

+ ) : ( + similarEvents.map((similarEvent: any) => ( + + +

{similarEvent.title}

+
+ + {similarEvent.evtPicture && ( +
+ Event Picture { + e.currentTarget.src = '/path/to/placeholder-image.png'; // Fallback image if loading fails + }} + /> +
+ )} +
+ + + +
+ )) + )}
- - - {/* Similar Events Section */} -
-

- Similar Events You Might Be Interested In -

-
- {similarEvents.length === 0 ? ( -

No similar events available.

- ) : ( - similarEvents.map((similarEvent: any) => ( - - -

{similarEvent.title}

-
- - {similarEvent.evtPicture && ( -
- Event Picture -
- )} -
- + {/* Success Confirmation Modal */} + + + {(onClose) => ( + <> + + Success Message + + +

You have successfully registered for this event!!

+
+ + -
-
- )) - )} -
-
-
+ + + )} + + + + {/* Already Registered Modal */} + + + {(onClose) => ( + <> + + Already Registered + + +

You have already registered for this event.

+
+ + + + + )} +
+
+
} + {!event &&

Loading...

} + + ); }; diff --git a/client/src/pages/EventsPage.tsx b/client/src/pages/EventsPage.tsx index 768857b..1360f29 100644 --- a/client/src/pages/EventsPage.tsx +++ b/client/src/pages/EventsPage.tsx @@ -68,6 +68,14 @@ const EventsPage: React.FC = () => { fetchTownCouncils(); }, []); + const getTimePeriod = (time: string): string => { + const [hours] = time.split(':').map(Number); + if (hours >= 5 && hours < 12) return 'morning'; + if (hours >= 12 && hours < 17) return 'afternoon'; + if (hours >= 17 || hours < 5) return 'evening'; + return ''; // Default case if none of the above match + }; + useEffect(() => { // Filter events based on selected criteria const filtered = events.filter((event) => { @@ -78,7 +86,7 @@ const EventsPage: React.FC = () => { ? event.location === selectedTownCouncil : true; const matchTime = selectedTime - ? event.time.toLowerCase().trim() === selectedTime.toLowerCase().trim() + ? getTimePeriod(event.time) === selectedTime.toLowerCase().trim() : true; console.log("Event Time:", event.time); diff --git a/client/src/pages/ManageEventsPage.tsx b/client/src/pages/ManageEventsPage.tsx index b68ec43..432f6c4 100644 --- a/client/src/pages/ManageEventsPage.tsx +++ b/client/src/pages/ManageEventsPage.tsx @@ -27,6 +27,7 @@ const ManageEventsPage = () => { const fetchEvents = async () => { try { const res = await axios.get(config.serverAddress + "/events"); + console.log(res.data); setEvents(res.data); } catch (error) { console.error("Failed to fetch events:", error); diff --git a/client/src/pages/RegisterPage.tsx b/client/src/pages/RegisterPage.tsx deleted file mode 100644 index 3638dc8..0000000 --- a/client/src/pages/RegisterPage.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from "react"; -import { useParams } from "react-router-dom"; -import { Button } from "@nextui-org/react"; - -const RegisterPage = () => { - const { id } = useParams<{ id: string }>(); - - const handleRegister = () => { - // Add logic for registration here, if needed - console.log(`Registered for event with ID: ${id}`); - }; - - return ( -
-

Register for Event

- -
- ); -}; - -export default RegisterPage; diff --git a/server/routes/events.js b/server/routes/events.js index d46905f..4b25b40 100644 --- a/server/routes/events.js +++ b/server/routes/events.js @@ -12,7 +12,7 @@ const upload = multer({ storage: multer.memoryStorage() }); router.get("/", async (req, res) => { try { - const { category, townCouncil, time } = req.query; + const { category, townCouncil, time} = req.query; console.log("Filter Parameters:", { category, townCouncil, time }); @@ -26,7 +26,9 @@ router.get("/", async (req, res) => { const timeCategory = timeToCategory(time); - let filter = {}; + let filter = { + slotsAvailable: { [Op.gt]: 0 } + }; if (category) filter.category = category; if (townCouncil) filter.townCouncil = townCouncil; if (timeCategory) filter.timeCategory = timeCategory; @@ -35,13 +37,7 @@ router.get("/", async (req, res) => { const filteredEvents = await Events.findAll({ attributes: { exclude: ["evtPicture"] }, - where: { - [Op.and]: [ - category ? { category } : {}, - townCouncil ? { townCouncil } : {}, - timeCategory ? { timeCategory } : {}, - ], - }, + where: filter, }); res.json(filteredEvents); @@ -241,19 +237,25 @@ router.post("/register/:id", async (req, res) => { return res.status(404).json({ message: "Event not found" }); } - // Process registration data (e.g., save to a Registration table or similar) - // Here we assume that you might just be updating the event for simplicity - // Modify this logic as needed based on your actual requirements - await Events.update(data, { where: { id: id } }); + // Check if there are available slots + if (event.slotsAvailable <= 0) { + return res.status(400).json({ message: "No available slots for this event" }); + } + + // Decrease the number of available slots + await Events.update( + { slotsAvailable: event.slotsAvailable - 1 }, + { where: { id: id } } + ); res.json({ message: "Registration successful" }); } catch (err) { console.error("Error during registration:", err); res.status(400).json({ errors: err.errors }); + res.status(500).json({ message: "Error during registration", error: err.message }); } }); - router.delete("/:id", async (req, res) => { const id = req.params.id; const event = await Events.findByPk(id);