84 lines
2.7 KiB
TypeScript
84 lines
2.7 KiB
TypeScript
"use server"
|
|
|
|
import { redirect } from "next/navigation"
|
|
|
|
import { replaceInvitationInteractions } from "@/lib/db/queries/interactions"
|
|
import { getInvitationByToken, submitInvitationResponse } from "@/lib/db/queries/invitations"
|
|
import { isRsvpClosed } from "@/lib/rsvp-deadline"
|
|
import { interactionSubmissionSchema } from "@/lib/validation/interactions"
|
|
import {
|
|
mealPreferenceSchema,
|
|
submitInvitationResponseSchema,
|
|
} from "@/lib/validation/response"
|
|
|
|
function optionalString(formData: FormData, key: string) {
|
|
return formData.get(key)?.toString() ?? ""
|
|
}
|
|
|
|
function transportHelp(value: FormDataEntryValue | null) {
|
|
if (value === "yes") return true
|
|
if (value === "no") return false
|
|
return null
|
|
}
|
|
|
|
function toastRequest(formData: FormData) {
|
|
if (!formData.has("wantsToast")) return null
|
|
|
|
const speakerName = optionalString(formData, "toastSpeakerName")
|
|
if (!speakerName) return null
|
|
|
|
return {
|
|
memberId: optionalString(formData, "toastMemberId") || null,
|
|
speakerName,
|
|
estimatedMinutes: optionalString(formData, "toastEstimatedMinutes"),
|
|
}
|
|
}
|
|
|
|
export async function submitRsvpAction(token: string, formData: FormData) {
|
|
if (isRsvpClosed()) redirect(`/invite/${token}?error=closed`)
|
|
|
|
const invitation = await getInvitationByToken(token)
|
|
if (!invitation) redirect(`/invite/${token}?error=invalid`)
|
|
|
|
const members = invitation.members.map((member) => {
|
|
const attendingValue = formData.get(`attending-${member.id}`)
|
|
if (attendingValue !== "yes" && attendingValue !== "no") {
|
|
redirect(`/invite/${token}?error=generic`)
|
|
}
|
|
|
|
const attending = attendingValue === "yes"
|
|
const meal = mealPreferenceSchema.safeParse(
|
|
formData.get(`meal-${member.id}`)?.toString() ?? "",
|
|
)
|
|
|
|
return {
|
|
memberId: member.id,
|
|
attending,
|
|
mealPreference: attending && meal.success ? meal.data : null,
|
|
dietaryRestrictions: optionalString(formData, `dietary-${member.id}`),
|
|
}
|
|
})
|
|
|
|
const response = submitInvitationResponseSchema.safeParse({
|
|
token,
|
|
members,
|
|
arrivalDate: formData.get("arrivalDate"),
|
|
departureDate: formData.get("departureDate"),
|
|
stayLocation: formData.get("stayLocation"),
|
|
needsTransportHelp: transportHelp(formData.get("needsTransportHelp")),
|
|
messageToCouple: formData.get("messageToCouple"),
|
|
})
|
|
const interactions = interactionSubmissionSchema.safeParse({
|
|
invitationId: invitation.invitation.id,
|
|
toastRequest: toastRequest(formData),
|
|
})
|
|
|
|
if (!response.success || !interactions.success) {
|
|
redirect(`/invite/${token}?error=generic`)
|
|
}
|
|
|
|
await submitInvitationResponse(response.data)
|
|
await replaceInvitationInteractions(interactions.data)
|
|
redirect(`/invite/${token}?submitted=1`)
|
|
}
|