Skip to content

Commit 91bff93

Browse files
committed
feat: add endpoint to remove a guest from an event and enhance guest group deletion process
1 parent 9b86c8f commit 91bff93

File tree

2 files changed

+129
-13
lines changed

2 files changed

+129
-13
lines changed

src/routes/guestRoutes.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import {
1111
removeUserFromGroup,
1212
getMyGuestGroups,
1313
getAvailableGuestGroupsForEvent,
14-
removeGuestGroupFromEvent
14+
removeGuestGroupFromEvent,
15+
removeGuestFromEvent
1516
} from '../services/guestService';
1617
import { verifyIdToken } from '../middleware/verifyIdToken';
1718

@@ -405,4 +406,38 @@ router.delete('/:eventId/groups/:groupId/members', verifyIdToken, async (req: Re
405406
}
406407
});
407408

409+
// Remove a specific guest from an event
410+
router.delete('/:eventId/guests/:guestId', verifyIdToken, async (req: Request, res: Response): Promise<void> => {
411+
try {
412+
const { eventId, guestId } = req.params;
413+
const userId = req.userId;
414+
415+
if (!userId) {
416+
res.status(401).json({ message: 'Unauthorized' });
417+
return;
418+
}
419+
420+
const isAuthorized = await isEventHostOrCoHost(userId, eventId);
421+
if (!isAuthorized) {
422+
res.status(403).json({ message: 'Only event hosts and co-hosts can remove guests from events' });
423+
return;
424+
}
425+
426+
const result = await removeGuestFromEvent(guestId, eventId);
427+
428+
if (!result.success) {
429+
res.status(400).json({ message: result.error });
430+
return;
431+
}
432+
433+
res.status(200).json({
434+
message: result.message,
435+
removedGuest: result.removedGuest
436+
});
437+
} catch (error) {
438+
console.error('Remove guest from event error:', error);
439+
res.status(500).json({ message: 'Internal Server Error' });
440+
}
441+
});
442+
408443
export default router;

src/services/guestService.ts

Lines changed: 93 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -289,22 +289,35 @@ export const deleteGuestGroup = async (groupId: string) => {
289289
};
290290
}
291291

292-
// Check if group has active guests/RSVPs
293-
if (guestGroup.guests.length > 0) {
294-
return {
295-
success: false,
296-
error: 'Cannot delete guest group with existing guests. Remove all guests first or transfer them to another group.'
297-
};
298-
}
292+
// Delete the guest group and all related records in a transaction
293+
await prisma.$transaction(async (tx) => {
294+
// Delete all messages related to guests in this group
295+
const guestIds = guestGroup.guests.map(g => g.id);
296+
if (guestIds.length > 0) {
297+
await tx.message.deleteMany({
298+
where: { guest_id: { in: guestIds } }
299+
});
300+
}
301+
302+
// Delete all guests in this group
303+
await tx.guest.deleteMany({
304+
where: { group_id: groupId }
305+
});
306+
307+
// Delete all invites associated with this group
308+
await tx.invite.deleteMany({
309+
where: { group_id: groupId }
310+
});
299311

300-
// Delete the guest group (cascade will handle relations)
301-
await prisma.guestGroup.delete({
302-
where: { id: groupId }
312+
// Delete the guest group (cascade will handle GuestGroupUsers, InviteLinks, and EventGuestGroup)
313+
await tx.guestGroup.delete({
314+
where: { id: groupId }
315+
});
303316
});
304317

305318
return {
306319
success: true,
307-
message: 'Guest group deleted successfully'
320+
message: 'Guest group and all associated members and guests deleted successfully'
308321
};
309322
} catch (error: unknown) {
310323
return {
@@ -1163,4 +1176,72 @@ export const getEventRsvpsWithUnlinked = async (eventId: string, userId: string)
11631176
error: error instanceof Error ? error.message : 'Failed to get event RSVPs'
11641177
};
11651178
}
1166-
};
1179+
};
1180+
1181+
// New function to remove a guest from an event
1182+
export const removeGuestFromEvent = async (guestId: string, eventId: string) => {
1183+
try {
1184+
// Check if guest exists and belongs to the specified event
1185+
const guest = await prisma.guest.findUnique({
1186+
where: { id: guestId },
1187+
include: {
1188+
user: {
1189+
select: {
1190+
id: true,
1191+
name: true,
1192+
mobile_number: true
1193+
}
1194+
},
1195+
group: {
1196+
select: {
1197+
id: true,
1198+
name: true
1199+
}
1200+
}
1201+
}
1202+
});
1203+
1204+
if (!guest) {
1205+
return {
1206+
success: false,
1207+
error: 'Guest not found'
1208+
};
1209+
}
1210+
1211+
if (guest.event_id !== eventId) {
1212+
return {
1213+
success: false,
1214+
error: 'Guest does not belong to this event'
1215+
};
1216+
}
1217+
1218+
// Delete the guest and related messages in a transaction
1219+
await prisma.$transaction(async (tx) => {
1220+
// Delete all messages from this guest
1221+
await tx.message.deleteMany({
1222+
where: { guest_id: guestId }
1223+
});
1224+
1225+
// Delete the guest record
1226+
await tx.guest.delete({
1227+
where: { id: guestId }
1228+
});
1229+
});
1230+
1231+
return {
1232+
success: true,
1233+
message: 'Guest removed from event successfully',
1234+
removedGuest: {
1235+
id: guest.id,
1236+
name: guest.name || guest.user?.name,
1237+
phone: guest.phone_no || guest.user?.mobile_number,
1238+
groupName: guest.group?.name
1239+
}
1240+
};
1241+
} catch (error: unknown) {
1242+
return {
1243+
success: false,
1244+
error: error instanceof Error ? error.message : 'Failed to remove guest from event'
1245+
};
1246+
}
1247+
};

0 commit comments

Comments
 (0)