diff --git a/src/actions/attend-event.ts b/src/actions/attend-event.ts index 4111ec3..190baa1 100644 --- a/src/actions/attend-event.ts +++ b/src/actions/attend-event.ts @@ -1,7 +1,9 @@ "use server"; +import { count, eq } from "drizzle-orm"; + import db from "@/db"; -import { eventAttendees } from "@/db/schema"; +import { eventAttendees, events } from "@/db/schema"; import { auth } from "@/lib/auth"; export async function attendEvent(eventId: string): Promise { @@ -10,6 +12,35 @@ export async function attendEvent(eventId: string): Promise { if (!session?.user?.id) { throw new Error("Unauthorized"); } + + const event = await db + .select() + .from(events) + .where(eq(events.id, eventId)) + .then((res) => res[0]); + + if (!event) { + throw new Error("Event not found"); + } + + const capacity = event.capacity ?? 0; + + const attendeeCount = await db + .select({ count: count() }) + .from(eventAttendees) + .where(eq(eventAttendees.eventId, eventId)); + + if (attendeeCount[0].count >= capacity || capacity == 0) { + throw new Error("Event capacity reached"); + } + + const newCount = attendeeCount[0].count + 1; + + await db + .update(events) + .set({ registeredUsers: newCount }) + .where(eq(events.id, eventId)); + await db .insert(eventAttendees) .values({ diff --git a/src/actions/leave-event.ts b/src/actions/leave-event.ts index 3186db2..5aaf9a0 100644 --- a/src/actions/leave-event.ts +++ b/src/actions/leave-event.ts @@ -1,9 +1,9 @@ "use server"; -import { and, eq } from "drizzle-orm"; +import { and, count, eq } from "drizzle-orm"; import db from "@/db"; -import { eventAttendees } from "@/db/schema"; +import { eventAttendees, events } from "@/db/schema"; import { auth } from "@/lib/auth"; export async function leaveEvent(eventId: string): Promise { @@ -13,6 +13,27 @@ export async function leaveEvent(eventId: string): Promise { throw new Error("Unauthorized"); } + const event = await db + .select() + .from(events) + .where(eq(events.id, eventId)) + .then((res) => res[0]); + + if (!event) { + throw new Error("Event not found"); + } + const attendeeCount = await db + .select({ count: count() }) + .from(eventAttendees) + .where(eq(eventAttendees.eventId, eventId)); + + const newCount = attendeeCount[0].count - 1; + + await db + .update(events) + .set({ registeredUsers: newCount }) + .where(eq(events.id, eventId)); + // ✅ Remove the attendee row for this user + event await db .delete(eventAttendees) diff --git a/src/app/HomePageClient.tsx b/src/app/HomePageClient.tsx index 7ef7f29..d7086a1 100644 --- a/src/app/HomePageClient.tsx +++ b/src/app/HomePageClient.tsx @@ -15,6 +15,7 @@ type HomePageClientProps = { startTime: string; endTime: string; capacity: number | null; + registeredUsers: number | null; streetLine: string; description: string; isRegistered?: boolean; @@ -77,6 +78,7 @@ export default function HomePageClient({ startTime={event.startTime} endTime={event.endTime} capacity={event.capacity} + registeredUsers={event.registeredUsers} streetLine={event.streetLine} description={event.description} isRegistered={event.isRegistered} diff --git a/src/app/VolunteerEventCard.tsx b/src/app/VolunteerEventCard.tsx index e5ea31f..c5d39db 100644 --- a/src/app/VolunteerEventCard.tsx +++ b/src/app/VolunteerEventCard.tsx @@ -20,6 +20,7 @@ type VolunteerEventCardProps = { startTime: string; endTime: string; capacity: number | null; + registeredUsers: number | null; streetLine: string; description: string; isRegistered?: boolean; @@ -40,9 +41,9 @@ export default function VolunteerEventCard( event.isRegistered ?? false, ); - React.useEffect(() => { - setIsRegistered(event.isRegistered ?? false); - }, [event.isRegistered]); + const [registeredUsers, setRegisteredUsers] = React.useState( + event.registeredUsers ?? 0, + ); const [isPending, setIsPending] = React.useState(false); @@ -72,12 +73,20 @@ export default function VolunteerEventCard( if (isRegistered) { await leaveEvent(event.id); setIsRegistered(false); + setRegisteredUsers((prev) => Math.max(prev - 1, 0)); } else { await attendEvent(event.id); setIsRegistered(true); + setRegisteredUsers((prev) => prev + 1); } } catch (error) { - console.error(error); + if (error instanceof Error) { + if (error.message === "Event capacity reached") { + alert("Sorry, this event is full! You cannot register."); + } else { + console.error(error.message); + } + } } finally { setIsPending(false); } @@ -105,7 +114,7 @@ export default function VolunteerEventCard( - {event.capacity} slots remaining + {(event.capacity ?? 0) - registeredUsers} slots remaining diff --git a/src/app/api/events/route.ts b/src/app/api/events/route.ts index eb5304e..dab1230 100644 --- a/src/app/api/events/route.ts +++ b/src/app/api/events/route.ts @@ -56,6 +56,7 @@ export async function POST(req: Request): Promise { const firstResult = Array.isArray(result) ? result[0] : undefined; const latitude = firstResult?.lat ?? null; const longitude = firstResult?.lon ?? null; + const registeredUsers = 0; if (endTime <= startTime) { return NextResponse.json( @@ -70,6 +71,7 @@ export async function POST(req: Request): Promise { startTime, endTime, capacity: capacity ?? null, + registeredUsers, streetLine, city, state, diff --git a/src/db/schema/events.ts b/src/db/schema/events.ts index 5437b4d..0642dfa 100644 --- a/src/db/schema/events.ts +++ b/src/db/schema/events.ts @@ -20,6 +20,7 @@ export const events = pgTable("events", { endTime: time("end_time").notNull(), capacity: integer("capacity"), + registeredUsers: integer("registered_users"), streetLine: text("street_line").notNull(), city: text("city").notNull(),