Skip to content

Commit

Permalink
feat: events | event participants, connect to backend and some more f…
Browse files Browse the repository at this point in the history
…ixes (#628)

* fix: date input is not valid Date error

why: so user could input time and then date
how: add check

* feat: event participants, connect to backend and some more fixes

why: so users could join and leave event
what: 1. connect to backend with help of redux, 2. add toast messages
which gives more info, 3. show avatars of users
how: 1. redux , 2. toastify , 3. populate in backend and use here
  • Loading branch information
ArkadiK94 authored Feb 4, 2024
1 parent 61b311d commit 8f24798
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 57 deletions.
6 changes: 3 additions & 3 deletions src/components/CommunityEvents/CommunityEvents.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,12 @@ const CommunityEvents = ({
/>
)}
{filteredEvents.length !== 0 ? (
filteredEvents.map((data, index) => {
const dateObject = new Date(data.startTime);
filteredEvents.map((event, index) => {
const dateObject = new Date(event.startTime);
const dayName = daysOfWeek[dateObject.getDay()];
return (
<EventItemList
data={data}
event={event}
todayString={todayString}
currentTime={currentTime}
dayName={dayName}
Expand Down
65 changes: 32 additions & 33 deletions src/components/CommunityEvents/EventItemList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ import {
BiSolidChevronUpIcon,
AiFillExclamationCircleIcon,
} from "./CommunityEventsElement";
import { cdnContentImagesUrl } from "../../features/apiUrl";

const AddZeroToDateString = (dateValue) => {
return +dateValue < 10 ? `0${dateValue}` : dateValue;
};
export const EventItemList = ({
data,
event,
todayString,
dayName,
actions,
Expand All @@ -31,33 +32,32 @@ export const EventItemList = ({
const [leftPlacesToJoin, setLeftPlacesToJoin] = useState(100);

useEffect(() => {
if (!user) return setActionDisplay("Join*");
if (eventsJoinedId.includes(data._id)) {
if (!user) {
setActionDisplay("Join*");
setLeftPlacesToJoin(100);
return;
}
if (eventsJoinedId.includes(event._id)) {
setActionDisplay("Joined");
} else {
setActionDisplay("Join");
}
setLeftPlacesToJoin(data.maxParticipantsNumber - data.participants.length);
}, [eventsJoinedId, data, user]);
setLeftPlacesToJoin(event.maxParticipantsNumber - event.participants.length);
}, [eventsJoinedId, event, user]);

const handleDisplayActionClick = () => {
if (!user) toast.info("To Join An Event You First Need To Login/Register To The Website");
if (leftPlacesToJoin === 0 && !eventsJoinedId.includes(data._id)) return;
if (actionDisplay === "Join") {
console.log("join");
// data.participants.push(user._id);
} else {
data.participants = data.participants.filter((userId) => userId !== user._id);
}
onActionChange(actionDisplay, data._id);
if (!user) return toast.info("To Join An Event You First Need To Login/Register To The Website");
if (leftPlacesToJoin === 0 && !eventsJoinedId.includes(event._id))
return toast.info("This event is full , keep in touch for future events");
onActionChange(actionDisplay, event._id);
};
const startTimeDate = new Date(data.startTime);
const endTimeDate = new Date(data.endTime);
const startTimeDate = new Date(event.startTime);
const endTimeDate = new Date(event.endTime);
return (
<EventItem isRequestedEvent={data.reschedule} key={index}>
<EventItem isRequestedEvent={event.reschedule} key={index}>
<div
className={
data.startTime.split("T")[0] === todayString && data.status === "approved" && tabStatus !== "past"
event.startTime.split("T")[0] === todayString && event.status === "approved" && tabStatus !== "past"
? "date today-date"
: "date"
}
Expand All @@ -68,12 +68,12 @@ export const EventItemList = ({
month: "short",
})} ${AddZeroToDateString(startTimeDate.getDate())}`}
</p>
<p className="date-digit date-year">{`${data.startTime.split("-")[0]}`}</p>
<p className="date-digit date-year">{`${event.startTime.split("-")[0]}`}</p>
</div>
<div className="time-line">
<div className="time-line-detail">
<AiFillClockCircleIcon />
{data.startTime.split("T")[0] === data.endTime.split("T")[0] ? (
{event.startTime.split("T")[0] === event.endTime.split("T")[0] ? (
<p>
{`${AddZeroToDateString(startTimeDate.getHours())}:${AddZeroToDateString(
startTimeDate.getMinutes(),
Expand Down Expand Up @@ -104,7 +104,7 @@ export const EventItemList = ({
</div>
</div>
)}
{data.reschedule && (
{event.reschedule && (
<div className="time-line-request">
<AiFillExclamationCircleIcon />
</div>
Expand All @@ -113,19 +113,23 @@ export const EventItemList = ({

<div className="time-line-detail">
<MdLocationOnIcon />
<p className="text-over-flow">{data.location}</p>
<p className="text-over-flow">{event.location}</p>
</div>
</div>
<div className="details">
<div>
<p>{data.name.toUpperCase()}</p>
<p>{event.name.toUpperCase()}</p>
<div className="details-profile">
{data.participants.map((participant, pIndex) => (
<img src={participant.profileURL} alt={participant.name} key={pIndex} />
{event.participants.map((participant, pIndex) => (
<img
src={cdnContentImagesUrl("/user/" + (participant?.avatar || "avatarDummy.png"))}
alt={participant.name}
key={pIndex}
/>
))}
</div>
</div>
{data.reschedule && <div className="details-request">15:30 - 16:00 requested</div>}
{event.reschedule && <div className="details-request">15:30 - 16:00 requested</div>}
</div>
{modify && actions[tabStatus] && actions[tabStatus].length > 0 ? (
<div className="action" onMouseLeave={() => setOpenEventIndex(null)}>
Expand All @@ -142,7 +146,7 @@ export const EventItemList = ({
{actions[tabStatus].map(({ icon: Icon, text, onClick }) => (
<div
onClick={() => {
onClick(data);
onClick(event);
setOpenEventIndex(openEventIndex === index ? null : index);
}}
className="action-dropdown-list"
Expand All @@ -156,7 +160,7 @@ export const EventItemList = ({
)}
</div>
) : (
tabStatus === "upcoming" && (
(tabStatus === "upcoming" || tabStatus === "ongoing") && (
<div className="container-action">
<div className="action">
<div
Expand All @@ -172,11 +176,6 @@ export const EventItemList = ({
<p>{leftPlacesToJoin === 0 ? "Full" : actionDisplay}</p>
</div>
</div>
<div className="details">
<p>
{data.participants.length} / {data.maxParticipantsNumber}
</p>
</div>
</div>
)
)}
Expand Down
2 changes: 1 addition & 1 deletion src/components/CommunityEvents/ModifyCommunityEvent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const validURL = (str) => {
return !!pattern.test(str);
};
const setDateAndTime = (date, time) => {
const newDate = new Date(date) || new Date();
const newDate = date ? new Date(date) : new Date();
const newTime = time ? new Date(time) : newDate;
return new Date(
newDate?.getFullYear(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,49 @@
import React from "react";
import { useSelector } from "react-redux";
import React, { useEffect, useRef } from "react";
import { useDispatch, useSelector } from "react-redux";

import CommunityEvents from "../../CommunityEvents/CommunityEvents";
// import { userAddEventId, userRemoveEventId } from "../../../features/userDetail/userDetailSlice";
import { addParticipant, removeParticipant } from "../../../features/events/eventsSlice";
import { getUserDetail } from "../../../features/userDetail/userDetailSlice";
import { toast } from "react-toastify";

const DisplayCommunityEvents = () => {
// const dispatch = useDispatch();
const dispatch = useDispatch();
const { user } = useSelector((state) => state.auth);
const { userEventsId } = useSelector((state) => state.userDetail);
const { userDetail, isUserDetailError, userDetailMessage } = useSelector((state) => state.userDetail);
const countLeaveEvent = useRef(0);
useEffect(() => {
countLeaveEvent.current = 0;
if (isUserDetailError) {
toast.error(userDetailMessage);
console.log(userDetailMessage);
return;
}
if (user) {
dispatch(getUserDetail(user.username));
}
}, [dispatch, isUserDetailError, userDetailMessage]);

const handleActionChange = (actionDisplay, eventId) => {
if (!user) return;
if (actionDisplay === "Join") {
console.log(actionDisplay, eventId);
// dispatch(userAddEventId(eventId));
dispatch(addParticipant({ eventId, userId: user._id }));
} else {
console.log(actionDisplay, eventId);
// dispatch(userRemoveEventId(eventId));
if (countLeaveEvent.current === 0) {
toast.info(
"If you will leave this event and it becomes full you couldn't join again. To leave click the 'Joined/Full' button again.",
);
return (countLeaveEvent.current = 1);
}
dispatch(removeParticipant({ eventId, userId: user._id }));
countLeaveEvent.current = 0;
}
};
return (
<CommunityEvents
pageHeader
title="Community Events"
subtitle="Join to any of the Community Events to develop your skill set."
eventsJoinedId={userEventsId}
eventsJoinedId={userDetail.events}
user={user}
onActionChange={handleActionChange}
/>
Expand Down
23 changes: 23 additions & 0 deletions src/features/events/eventsService.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,33 @@ const updateEvent = async (id, eventData, token) => {
return response.data;
};

// Add Participant
const addParticipant = async (eventId, userId, token) => {
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.post(`${API_URL}${eventId}/participant/${userId}`, {}, config);
return response.data;
};
// Remove Participant
const removeParticipant = async (eventId, userId, token) => {
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.delete(`${API_URL}${eventId}/participant/${userId}`, config);
return response.data;
};

const eventsService = {
createEvent,
getEvents,
updateEvent,
addParticipant,
removeParticipant,
};

export default eventsService;
54 changes: 52 additions & 2 deletions src/features/events/eventsSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,33 @@ export const getEvents = createAsyncThunk("events/getEvents", async (_, thunkAPI
export const updateEvent = createAsyncThunk("events/update", async ({ id, eventData }, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
console.log(id, eventData);
return await eventsService.updateEvent(id, eventData, token);
} catch (err) {
const message =
(err.response && err.response.data && err.response.data.message) || err.message || err.toString();
return thunkAPI.rejectWithValue(message);
}
});
export const addParticipant = createAsyncThunk("events/addParticipant", async ({ eventId, userId }, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
return await eventsService.addParticipant(eventId, userId, token);
} catch (err) {
const message =
(err.response && err.response.data && err.response.data.message) || err.message || err.toString();
return thunkAPI.rejectWithValue(message);
}
});
export const removeParticipant = createAsyncThunk("events/removeParticipant", async ({ eventId, userId }, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
return await eventsService.removeParticipant(eventId, userId, token);
} catch (err) {
const message =
(err.response && err.response.data && err.response.data.message) || err.message || err.toString();
return thunkAPI.rejectWithValue(message);
}
});

export const eventSlice = createSlice({
name: "events",
Expand Down Expand Up @@ -85,7 +104,6 @@ export const eventSlice = createSlice({
state.isEventError = false;
state.isEventLoading = false;
state.isEventSuccess = true;
console.log(action.payload);
state.events = state.events.map((event) =>
event._id === action.payload._id ? { ...event, ...action.payload } : event,
);
Expand All @@ -95,6 +113,38 @@ export const eventSlice = createSlice({
state.isEventSuccess = false;
state.isEventError = true;
state.eventMessage = action.payload;
})
.addCase(addParticipant.pending, (state) => {
state.isEventLoading = true;
})
.addCase(addParticipant.fulfilled, (state, action) => {
state.isEventLoading = false;
state.isEventError = false;
state.isEventSuccess = true;
const eventIndex = state.events.findIndex((event) => event._id === action.payload.event._id);
state.events[eventIndex] = { ...state.events[eventIndex], ...action.payload.event };
})
.addCase(addParticipant.rejected, (state, action) => {
state.isEventLoading = false;
state.isEventSuccess = false;
state.isEventError = true;
state.eventMessage = action.payload;
})
.addCase(removeParticipant.pending, (state) => {
state.isEventLoading = true;
})
.addCase(removeParticipant.fulfilled, (state, action) => {
state.isEventLoading = false;
state.isEventError = false;
state.isEventSuccess = true;
const eventIndex = state.events.findIndex((event) => event._id === action.payload.event._id);
state.events[eventIndex] = { ...state.events[eventIndex], ...action.payload.event };
})
.addCase(removeParticipant.rejected, (state, action) => {
state.isEventLoading = false;
state.isEventSuccess = false;
state.isEventError = true;
state.eventMessage = action.payload;
});
},
});
Expand Down
Loading

0 comments on commit 8f24798

Please sign in to comment.