Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions frontend/src/endpoints/mainEventsEndpoints.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { getToken } from "../utils/helper";

export const fetchEvents = async () => {
try {
const response = await fetch("http://localhost:5300/api/v1/events", {
method: "GET",
headers: {
Authorization: `Bearer ${getToken()}`,
}
});

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

const data = await response.json();
return data;
} catch (error) {
console.error("Error fetching events:", error);
return null;
}
};

export const fetchBookmarkedEvents = async () => {
try {
const response = await fetch("http://localhost:5300/api/v1/events/bookmarks", {
method: "GET",
headers: {
Authorization: `Bearer ${getToken()}`,
}
});

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

const data = await response.json();
return data;
} catch (error) {
console.error("Error fetching bookmarked events:", error);
return null;
}
};

export const bookEvent = async (eventID) => {
try {
const response = await fetch(`http://localhost:5300/api/v1/events/${eventID}/bookmark`, {
method: "POST",
headers: {
Authorization: `Bearer ${getToken()}`,
}
});

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return true;
}
catch (error) {
console.error("Error booking event:", error);
return false;
}
};

export const unbookEvent = async (eventID) => {
try {
const response = await fetch(`http://localhost:5300/api/v1/events/${eventID}/remove-bookmark`, {
method: "POST",
headers: {
Authorization: `Bearer ${getToken()}`,
}
});

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return true;
}
catch (error) {
console.error("Error unbooking event:", error);
return false;
}
};
163 changes: 34 additions & 129 deletions frontend/src/pages/events/Events.jsx
Original file line number Diff line number Diff line change
@@ -1,155 +1,59 @@
import withNavbar from '../../components/HOC/withNavbar'
import EventPoster from './components/eventPoster'
import '@/styles/eventPage.css'

import { fetchEvents, fetchBookmarkedEvents } from '../../endpoints/mainEventsEndpoints';

import { PiHeartBreak } from "react-icons/pi";
import { FaChevronUp } from "react-icons/fa";
import { FaChevronDown } from "react-icons/fa6";
import { FaChevronLeft } from "react-icons/fa";
import { FaChevronRight } from "react-icons/fa";
import { useState, useEffect } from 'react';
import { useState, useEffect, useRef } from 'react';

const Events = () => {
const eventssample = [
{
title: 'Event 1',
date: '2022-12-12',
description: 'This is a sample event description for event 1',
location: 'Sample Location 1',
saved: true,
finished: false
},
{
title: 'Event 2',
date: '2022-12-12',
description: 'This is a sample event description for event 2',
location: 'Sample Location 2',
saved: true,
finished: false
},
{
title: 'Event 3',
date: '2022-12-12',
description: 'This is a sample event description for event 3',
location: 'Sample Location 3',
saved: true,
finished: false
},
{
title: 'Event 4',
date: '2022-12-12',
description: 'This is a sample event description for event 4',
location: 'Sample Location 4',
saved: true,
finished: false
},
{
title: 'Event 5',
date: '2022-12-12',
description: 'This is a sample event description for event 5',
location: 'Sample Location 5',
saved: true,
finished: false
},
{
title: 'Event 6',
date: '2022-12-12',
description: 'This is a sample event description for event 6',
location: 'Sample Location 6',
saved: true,
finished: false
},
{
title: 'Event 7',
date: '2022-12-12',
description: 'This is a sample event description for event 7',
location: 'Sample Location 7',
saved: true,
finished: false
},
{
title: 'Event 8',
date: '2022-12-12',
description: 'This is a sample event description for event 8',
location: 'Sample Location 8',
saved: true,
finished: false
},
{
title: 'Event 9',
date: '2022-12-12',
description: 'This is a sample event description for event 9',
location: 'Sample Location 9',
saved: true,
finished: false
},
{
title: 'Event 10',
date: '2022-12-12',
description: 'This is a sample event description for event 10',
location: 'Sample Location 10',
saved: true,
finished: false
}
]

const sampleOldEvents = [
{
title: 'Event 1',
date: '2022-12-12',
description: 'This is a sample event description for event 1',
location: 'Sample Location 1',
saved: true,
finished: true
},
{
title: 'Event 2',
date: '2022-12-12',
description: 'This is a sample event description for event 2',
location: 'Sample Location 2',
saved: true,
finished: true
},
{
title: 'Event 3',
date: '2022-12-12',
description: 'This is a sample event description for event 3',
location: 'Sample Location 3',
saved: true,
finished: true
},
{
title: 'Event 4',
date: '2022-12-12',
description: 'This is a sample event description for event 4',
location: 'Sample Location 4',
saved: true,
finished: true
}
];

const [upcomingEvents, setUpcomingEvents] = useState([]);
const [pastEvents, setPastEvents] = useState([]);
const [loading, setLoading] = useState(true);
const [listUpcomingNum, setListUpcomingNum] = useState(0);
const [listPastNum, setListPastNum] = useState(0);
const eventRefs = useRef([]);
const containerRef = useRef(null);

useEffect(() => {
setUpcomingEvents(eventssample);
setPastEvents(sampleOldEvents);
setLoading(false);

fetchEvents().then((events) => {
if(!events) return;
console.log(events);
fetchBookmarkedEvents().then((bookmarkedEvents) => {
const eventsBookmarked = events.map((event) => {if(bookmarkedEvents.events.includes(event._id)) return {...event, saved: true}; return {...event, saved: false}});
const eventListProcess = eventsBookmarked.map((event) => {if(event.date > new Date()) return {...event, finished: false}; return {...event, finished: true}});
setUpcomingEvents(eventListProcess.filter((event) => !event.finished));
setPastEvents(eventListProcess.filter((event) => event.finished));
setLoading(false);
});
});
}, []);

const startUpcomingIndex = listUpcomingNum * 3;
const displayedUpcomingEvents = upcomingEvents.slice(startUpcomingIndex, startUpcomingIndex + 3);
const totalUpcomingPages = Math.ceil(upcomingEvents.length / 3);

const startPastIndex = listPastNum * 3;
const displayedPastEvents = pastEvents.slice(startPastIndex, startPastIndex + 3);
const totalPastPages = Math.ceil(pastEvents.length / 3);

const scrollToEvent = (index) => {
if (eventRefs.current[index] && containerRef.current) {
containerRef.current.scrollTo({
top: eventRefs.current[index].offsetTop - containerRef.current.offsetTop,
behavior: 'smooth',
});
}
};

useEffect(() => {
scrollToEvent(listUpcomingNum * 3);
}, [listUpcomingNum]);

return (
<>
{!loading ? (
Expand All @@ -164,11 +68,12 @@ const Events = () => {
<p className='text-xl font-semibold text-center'>No upcoming events at the moment</p>
</div>
) : (
<div className='flex gap-4 w-full pb-10'>
<div className='flex-col flex w-[99%] h-full gap-2 sm:border-t-2 border-[#a79d9d]'>
{displayedUpcomingEvents.map((event, index) => (
<EventPoster key={index} eventData={event} eventsList={upcomingEvents} setEventsList={setUpcomingEvents} />
))}
<div className='flex gap-4 w-full h-1/2 pb-10'>
<div ref={containerRef} className='flex-col flex w-[99%] max-h-[740px] overflow-y-hidden gap-2 sm:border-t-2 border-[#a79d9d]'>
{upcomingEvents.map((event, index) => (
<div key={index} ref={(el) => { eventRefs.current[index] = el; }}>
<EventPoster eventData={event} eventsList={upcomingEvents} setEventsList={setListUpcomingNum} />
</div>))}
</div>
{totalUpcomingPages > 1 ? (<div className='select-none flex flex-col gap-4 justify-center'>
<div onClick={() => setListUpcomingNum(Math.max(listUpcomingNum - 1, 0))} disabled={listUpcomingNum === 0} className='px-4 py-2 disabled:opacity-50 hover:cursor-pointer'>
Expand Down
49 changes: 36 additions & 13 deletions frontend/src/pages/events/components/eventPoster.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import PropTypes from 'prop-types';
import fakeEventImage from '@/assets/tccdbackground.jpeg';
import "@/styles/eventPage.css";
import { bookEvent, unbookEvent } from "@/endpoints/mainEventsEndpoints";

import { FaRegStar } from "react-icons/fa";
import { LuStarOff } from "react-icons/lu";
Expand All @@ -10,7 +11,7 @@ import { LuStarOff } from "react-icons/lu";
export default function EventPoster({ eventData, eventsList, setEventsList }) {
const [fadeIn, setFadeIn] = useState('opacity-0 -translate-x-1/2');
const [registerEvent, setRegisterEvent] = useState(false);

const formattedDate = new Date(eventData.date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
Expand All @@ -24,7 +25,6 @@ export default function EventPoster({ eventData, eventsList, setEventsList }) {
setFadeIn('opacity-100');
}, 300);
}, 10);

}, []);


Expand All @@ -36,11 +36,37 @@ export default function EventPoster({ eventData, eventsList, setEventsList }) {
}
}, [registerEvent]);

const handleBookEvent = async () => {
const success = await bookEvent(eventData._id);
if (success) {
const updatedEventsList = eventsList.map(event => {
if (event._id === eventData._id) {
return { ...event, saved: !event.saved };
}
return event;
});
setEventsList(updatedEventsList);
}
};

const handleUnBookEvent = async () => {
const success = await unbookEvent(eventData._id);
if (success) {
const updatedEventsList = eventsList.map(event => {
if (event._id === eventData._id) {
return { ...event, saved: !event.saved };
}
return event;
});
setEventsList(updatedEventsList);
}
};

return (
<>
<div className="relative group flex-none sm:flex hidden w-full h-full z-10 gap-4 border-b-2 border-[#a79d9d] p-3">
{!eventData.saved ? <FaRegStar className="absolute z-30 top-4 right-4 text-2xl hover:opacity-100 opacity-50 hover:cursor-pointer transition-all duration-150 ease-in-out" />
: <LuStarOff className="absolute z-30 top-4 right-4 text-2xl hover:opacity-100 opacity-50 hover:cursor-pointer transition-all duration-150 ease-in-out" />}
<div className="relative group flex-none sm:flex hidden w-full h-[240px] z-10 gap-4 border-b-2 border-[#a79d9d] p-3">
{!eventData.saved ? <FaRegStar onClick={() => handleBookEvent()} className="absolute z-30 top-4 right-4 text-2xl hover:opacity-100 opacity-50 hover:cursor-pointer transition-all duration-150 ease-in-out" />
: <LuStarOff onClick={() => handleUnBookEvent()} className="absolute z-30 top-4 right-4 text-2xl hover:opacity-100 opacity-50 hover:cursor-pointer transition-all duration-150 ease-in-out" />}
<div className="relative w-[30%] min-w-[300px] aspect-w-16 aspect-h-9">
<img src={fakeEventImage} alt="event" className="absolute inset-0 w-full h-full object-fit rounded-lg" />
</div>
Expand All @@ -67,15 +93,12 @@ export default function EventPoster({ eventData, eventsList, setEventsList }) {
<div className="relative group sm:hidden w-full min-h-[180px] z-10">
<img className="absolute top-0 h-full w-full object-fit z-0 rounded-lg" src={fakeEventImage} alt="event" />
<div className="absolute w-full bottom-0 bg-black bg-opacity-55 h-1/3 group-hover:h-full z-10 transition-all duration-300 ease-in-out rounded-b-lg group-hover:rounded-lg" />

{!eventData.saved ?
<div className="absolute py-3 bottom-0 right-3 h-1/3 z-20 text-md text-white group-hover:h-full transition-all duration-300 ease-in-out">
<FaRegStar className="hover:opacity-60 opacity-100 hover:cursor-pointer transition-all duration-150 ease-in-out" />
<div className={`absolute py-3 bottom-0 right-3 h-1/3 text-md text-white group-hover:h-full transition-all duration-300 ease-in-out ${eventData.saved ? "-z-50 opacity-0" : "opacity-100 z-20"}`}>
<FaRegStar onClick={() => handleBookEvent()} className={`hover:opacity-60 opacity-100 hover:cursor-pointer transition-all duration-150 ease-in-out`} />
</div>
<div className={`absolute py-3 bottom-0 right-3 h-1/3 text-md text-white group-hover:h-full transition-all duration-300 ease-in-out ${!eventData.saved ? "-z-50 opacity-0" : "opacity-100 z-20"}`}>
<LuStarOff onClick={() => handleUnBookEvent()} className="hover:opacity-60 opacity-100 hover:cursor-pointer transition-all duration-150 ease-in-out" />
</div>
:
<div className="absolute py-3 bottom-0 right-3 h-1/3 z-20 text-md text-white group-hover:h-full transition-all duration-300 ease-in-out">
<LuStarOff className="hover:opacity-60 opacity-100 hover:cursor-pointer transition-all duration-150 ease-in-out" />
</div>}

<div className="w-full h-fit p-3 z-20 absolute top-0 translate-y-[200%] group-hover:translate-y-0 text-white transition-all duration-300 ease-in-out">
<p className="text-sm">{eventData.title}</p>
Expand Down