@@ -3,7 +3,7 @@ import { prisma } from "src/utils/db";
33import type { NextRequest } from "next/server" ;
44import { NextResponse } from "next/server" ;
55import { sendEventEmail } from "src/utils/email/send" ;
6- import { RSVP_TYPE , type User } from "@prisma/client" ;
6+ import { RSVP_TYPE , RSVP_APPROVAL_STATUS , type User } from "@prisma/client" ;
77import { rsvpTypeToEmailType } from "src/utils/rsvpTypetoEmailType" ;
88import {
99 scheduleReminderEmails ,
@@ -70,8 +70,11 @@ export async function POST(req: NextRequest) {
7070 } ,
7171 } ) ;
7272
73- if ( existingApprovalRequest ) {
74- // Return the status of existing approval request
73+ if (
74+ existingApprovalRequest &&
75+ existingApprovalRequest . status === RSVP_APPROVAL_STATUS . PENDING
76+ ) {
77+ // Only block if there's a PENDING approval request
7578 return NextResponse . json ( {
7679 data : {
7780 status : "APPROVAL_PENDING" ,
@@ -83,15 +86,16 @@ export async function POST(req: NextRequest) {
8386 } ) ;
8487 }
8588
86- // If user doesn't have an existing RSVP or approval request, they need to go through approval flow
87- if ( ! existingRsvp ) {
89+ // Only require approval for "GOING" RSVPs
90+ // Users can freely RSVP as "MAYBE" or "NOT_GOING" without approval
91+ if ( rsvp . type === RSVP_TYPE . GOING && ! existingRsvp ) {
8892 return NextResponse . json (
8993 {
9094 data : {
9195 status : "APPROVAL_REQUIRED" ,
9296 eventId : rsvp . eventId ,
9397 message :
94- "This event requires approval. Please use the approval request endpoint." ,
98+ "This event requires approval to RSVP as 'Going' . Please use the approval request endpoint." ,
9599 } ,
96100 } ,
97101 { status : 400 }
@@ -127,18 +131,64 @@ export async function POST(req: NextRequest) {
127131 `User ${ user . id } changing from GOING to ${ rsvp . type } for event ${ event . id } `
128132 ) ;
129133
134+ // Safety check: existingRsvp should exist if wasGoing is true
135+ if ( ! existingRsvp ) {
136+ console . error (
137+ `User ${ user . id } was marked as GOING but no existing RSVP found for event ${ event . id } `
138+ ) ;
139+ return NextResponse . json (
140+ { error : "Invalid RSVP state - no existing RSVP found" } ,
141+ { status : 500 }
142+ ) ;
143+ }
144+
130145 // Increment the event sequence to ensure calendar clients recognize the update
131146 await prisma . event . update ( {
132147 where : { id : event . id } ,
133148 data : { sequence : event . sequence + 1 } ,
134149 } ) ;
135150
136- await prisma . rsvp . update ( {
137- where : { id : existingRsvp . id } , // Use existingRsvp.id which is guaranteed
138- data : { rsvpType : rsvp . type } ,
139- } ) ;
140- finalRsvpTypeForUser = rsvp . type ;
141- emailTypeForUser = rsvpTypeToEmailType ( finalRsvpTypeForUser , true ) ; // Send update email
151+ // For approval-required events: if user is changing to NOT_GOING, delete both RSVP and approval request
152+ // This makes it simpler for the frontend - user starts completely fresh if they want to rejoin
153+ if ( event . requiresApproval && rsvp . type === RSVP_TYPE . NOT_GOING ) {
154+ try {
155+ // Delete the RSVP record entirely
156+ await prisma . rsvp . delete ( {
157+ where : { id : existingRsvp . id } ,
158+ } ) ;
159+
160+ // Delete any approval requests for this user/event
161+ await prisma . rsvpApprovalRequest . deleteMany ( {
162+ where : {
163+ eventId : event . id ,
164+ userId : user . id ,
165+ } ,
166+ } ) ;
167+
168+ console . log (
169+ `Deleted RSVP and approval request for user ${ user . id } on event ${ event . id } (marked NOT_GOING)`
170+ ) ;
171+ finalRsvpTypeForUser = RSVP_TYPE . NOT_GOING ;
172+ emailTypeForUser = rsvpTypeToEmailType ( RSVP_TYPE . NOT_GOING , true ) ; // Send update email
173+ } catch ( deleteError ) {
174+ console . error ( "Error deleting RSVP/approval request:" , deleteError ) ;
175+ // Fallback to updating RSVP if deletion fails
176+ await prisma . rsvp . update ( {
177+ where : { id : existingRsvp . id } ,
178+ data : { rsvpType : rsvp . type } ,
179+ } ) ;
180+ finalRsvpTypeForUser = rsvp . type ;
181+ emailTypeForUser = rsvpTypeToEmailType ( finalRsvpTypeForUser , true ) ;
182+ }
183+ } else {
184+ // For non-approval events or non-NOT_GOING changes, just update the RSVP
185+ await prisma . rsvp . update ( {
186+ where : { id : existingRsvp . id } ,
187+ data : { rsvpType : rsvp . type } ,
188+ } ) ;
189+ finalRsvpTypeForUser = rsvp . type ;
190+ emailTypeForUser = rsvpTypeToEmailType ( finalRsvpTypeForUser , true ) ; // Send update email
191+ }
142192
143193 // A spot potentially opened up, try to promote
144194 if ( eventLimit > 0 ) {
@@ -150,6 +200,29 @@ export async function POST(req: NextRequest) {
150200 console . log (
151201 `User ${ user . id } wants to RSVP as GOING for event ${ event . id } `
152202 ) ;
203+
204+ // For approval-required events, check if user needs approval to change to GOING
205+ if (
206+ event . requiresApproval &&
207+ ! rsvp . adminOverride &&
208+ existingRsvp &&
209+ ! wasGoing
210+ ) {
211+ // User has existing RSVP but wasn't GOING, and now wants to be GOING
212+ // They need approval for this change
213+ return NextResponse . json (
214+ {
215+ data : {
216+ status : "APPROVAL_REQUIRED" ,
217+ eventId : rsvp . eventId ,
218+ message :
219+ "This event requires approval to change your RSVP to 'Going'. Please use the approval request endpoint." ,
220+ } ,
221+ } ,
222+ { status : 400 }
223+ ) ;
224+ }
225+
153226 const currentGoingCount = event . rsvps . filter (
154227 ( r ) => r . rsvpType === RSVP_TYPE . GOING && r . attendeeId !== user . id // Exclude self if updating from Maybe->Going
155228 ) . length ;
0 commit comments