Skip to content
Open
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
136 changes: 96 additions & 40 deletions src/controllers/eventController.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,51 +10,104 @@ const updateEventStatus = (event) => {
return event.status;
};

const getEvents = async (req, res) => {
const { page = 1, limit = 9, type = '', location = '', sortBy = 'date' } = req.query;
const VALID_TYPES = new Set(['Workshop', 'Meeting', 'Webinar', 'Social Gathering']);
const VALID_LOCATIONS = new Set(['Virtual', 'In person', 'TBD']);
const VALID_SORT_FIELDS = new Set(['date', 'title', 'type', 'location', 'currentAttendees']);

try {
const validSortFields = ['date', 'title', 'type', 'location', 'currentAttendees'];
const sortField = validSortFields.includes(sortBy) ? sortBy : 'date';
function validateQuery({ type, location, sortBy }) {
if (type && !VALID_TYPES.has(type)) {
throw new Error('Invalid Type of Event.');
}

if (location && !VALID_LOCATIONS.has(location)) {
throw new Error('Invalid Location for the Event.');
}

const query = { isActive: true };
if (type) query.type = type;
if (location) query.location = location;
if (sortBy && !VALID_SORT_FIELDS.has(sortBy)) {
throw new Error('Invalid Sort Field.');
}
}

const pageNumber = Math.max(1, Number(page));
const limitNumber = Math.max(1, Number(limit));
function buildSafeQuery(location, type) {
const query = { isActive: true };

const totalEvents = await Event.countDocuments(query);
let events = await Event.find(query)
.populate('resources.userID')
.sort({ [sortField]: 1 })
.skip((pageNumber - 1) * limitNumber)
.limit(limitNumber);
if (location === 'Virtual') {
query.location = 'Virtual';
} else if (location === 'In person') {
query.location = 'In person';
} else if (location === 'TBD') {
query.location = 'TBD';
}

events = events.map((event) => {
event.status = updateEventStatus(event);
if (type === 'Workshop') {
query.type = 'Workshop';
} else if (type === 'Meeting') {
query.type = 'Meeting';
} else if (type === 'Webinar') {
query.type = 'Webinar';
} else if (type === 'Social Gathering') {
query.type = 'Social Gathering';
}

const eventObj = event.toObject();
return query;
}

eventObj.waitlistCount = event.waitlist?.length || 0;
function getPagination(page, limit, total) {
if (!page || !limit) {
return {
pageNumber: 1,
limitNumber: total,
skip: 0,
};
}

eventObj.waitlistEnabled = event.currentAttendees >= event.maxAttendees;
const pageNumber = Math.max(1, Number(page));
const limitNumber = Math.max(1, Number(limit));

const { userId } = req.query;
return {
pageNumber,
limitNumber,
skip: (pageNumber - 1) * limitNumber,
};
}

if (userId && mongoose.Types.ObjectId.isValid(userId)) {
const index = event.waitlist.findIndex(
(entry) => entry.userId?.toString() === userId.toString(),
);
function formatEvent(event, userId) {
const eventObj = event.toObject();
const waitlist = Array.isArray(event.waitlist) ? event.waitlist : [];

eventObj.userWaitlistPosition = index !== -1 ? index + 1 : null;
}
eventObj.status = updateEventStatus(event);
eventObj.waitlistCount = waitlist.length;
eventObj.waitlistEnabled = event.currentAttendees >= event.maxAttendees;

return eventObj;
});
if (userId) {
const index = waitlist.findIndex((entry) => entry.userId?.toString() === userId.toString());

eventObj.userWaitlistPosition = index !== -1 ? index + 1 : null;
}

return eventObj;
}

const getEvents = async (req, res) => {
try {
const { page, limit, type, location, sortBy, userId } = req.query;

validateQuery({ type, location, sortBy });

const safeQuery = buildSafeQuery(location, type);
const totalEvents = await Event.countDocuments(safeQuery);
const { pageNumber, limitNumber, skip } = getPagination(page, limit, totalEvents);

const events = await Event.find(safeQuery)
.populate('resources.userID')
.sort(sortBy ? { [sortBy]: 1 } : {})
.skip(skip)
.limit(limitNumber);

const formattedEvents = events.map((event) => formatEvent(event, userId));

res.json({
events,
events: formattedEvents,
pagination: {
total: totalEvents,
totalPages: Math.ceil(totalEvents / limitNumber),
Expand All @@ -63,7 +116,14 @@ const getEvents = async (req, res) => {
},
});
} catch (error) {
res.status(500).json({ error: 'Failed to fetch events', details: error.message });
if (error.message.startsWith('Invalid')) {
return res.status(400).send(error.message);
}

res.status(500).json({
error: 'Failed to fetch events',
details: error.message,
});
}
};

Expand Down Expand Up @@ -91,11 +151,10 @@ const autoPromoteFromWaitlist = (event) => {

while (event.currentAttendees < event.maxAttendees && event.waitlist.length > 0) {
const nextEntry = event.waitlist.shift();

if (!nextEntry?.userId) continue;
console.log(`Auto-promoting user from waitlist for event ${event._id}`);
event.currentAttendees += 1;
promotedUsers.push(nextEntry.userId);
if (nextEntry?.userId) {
event.currentAttendees += 1;
promotedUsers.push(nextEntry.userId);
}
}

return promotedUsers;
Expand Down Expand Up @@ -136,7 +195,6 @@ const joinWaitlist = async (req, res) => {
try {
const { eventId } = req.params;
const { userId } = req.body;
console.log('Join waitlist request received');

if (!userId || !mongoose.Types.ObjectId.isValid(userId)) {
return res.status(400).json({ error: 'Invalid userId' });
Expand Down Expand Up @@ -165,7 +223,6 @@ const joinWaitlist = async (req, res) => {
position,
});
} catch (error) {
console.error('JOIN WAITLIST ERROR:', error);
res.status(500).json({
error: 'Failed to join waitlist',
details: error.message,
Expand All @@ -175,7 +232,6 @@ const joinWaitlist = async (req, res) => {

const sendWaitlistNotification = async (user, event) => {
// TODO: Integrate with real notification service (email/queue)
console.log(`Sending waitlist notification for event ${event._id}`);
};

const leaveEvent = async (req, res) => {
Expand Down
Loading