feat: add food interest tracking to registration flow

Add "I would like to include food for the week" checkbox to the payment
step with co-producing meals messaging. Track food interest in Google
Sheets as column Q ("Want Food").

- Add wantFood field to RegistrationData interface
- Add interactive food checkbox replacing static note in payment step
- Pass wantFood through register API to Google Sheets
- Expand sheet ranges from A:P to A:Q, add "Want Food" header
- Preserve food column on payment status updates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-12 13:42:23 -07:00
parent c64c4fb554
commit b8567b0f54
3 changed files with 32 additions and 12 deletions

View File

@ -5,7 +5,7 @@ export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { name, email, contact, contributions, expectations, howHeard, dietary, crewConsent } = body
const { name, email, contact, contributions, expectations, howHeard, dietary, crewConsent, wantFood } = body
// Validate required fields
if (!name || !email || !contact || !contributions || !expectations || !crewConsent) {
@ -28,6 +28,7 @@ export async function POST(request: NextRequest) {
howHeard: howHeard || "",
dietary: Array.isArray(dietary) ? dietary.join(", ") : dietary || "",
crewConsent,
wantFood: wantFood || false,
})
console.log(`[Register API] Registration added for ${name} at row ${rowNumber}`)

View File

@ -16,6 +16,7 @@ export default function RegisterPage() {
const [step, setStep] = useState<"form" | "payment">("form")
const [isSubmitting, setIsSubmitting] = useState(false)
const [includeAccommodation, setIncludeAccommodation] = useState(true)
const [wantFood, setWantFood] = useState(false)
const [accommodationVenue, setAccommodationVenue] = useState<"commons-hub" | "herrnhof">("commons-hub")
const [accommodationType, setAccommodationType] = useState("ch-multi")
const [formData, setFormData] = useState({
@ -85,6 +86,7 @@ export default function RegisterPage() {
howHeard: formData.howHeard,
dietary: dietaryString,
crewConsent: formData.crewConsent,
wantFood,
}),
})
@ -283,14 +285,27 @@ export default function RegisterPage() {
</p>
)}
{/* Food note */}
{/* Food */}
<div className="py-4 border-b border-border">
<p className="text-sm text-muted-foreground">
<span className="font-medium text-foreground">Food:</span>{" "}
We&apos;ll follow up via email with food options and pricing closer to the event.
<div className="flex items-start gap-3">
<Checkbox
id="include-food"
checked={wantFood}
onCheckedChange={(checked) => setWantFood(checked as boolean)}
className="mt-1"
/>
<div className="flex-1">
<Label htmlFor="include-food" className="font-medium cursor-pointer">
I would like to include food for the week
</Label>
<p className="text-sm text-muted-foreground mt-1">
We are exploring co-producing our own meals as a community. More details and costs
will be shared soon checking this box registers your interest so we can plan accordingly.
Your dietary preferences from step 1 have been noted.
</p>
</div>
</div>
</div>
{/* Processing fee */}
<div className="flex items-start justify-between py-3">

View File

@ -24,6 +24,7 @@ export interface RegistrationData {
howHeard: string
dietary: string
crewConsent: string
wantFood: boolean
}
export interface PaymentUpdateData {
@ -65,12 +66,13 @@ export async function addRegistration(data: RegistrationData): Promise<number> {
"", // N: Payment Date
"", // O: Accommodation Venue
"", // P: Accommodation Type
data.wantFood ? "Yes" : "No", // Q: Want Food
],
]
const response = await sheets.spreadsheets.values.append({
spreadsheetId: SPREADSHEET_ID,
range: `${SHEET_NAME}!A:P`,
range: `${SHEET_NAME}!A:Q`,
valueInputOption: "USER_ENTERED",
insertDataOption: "INSERT_ROWS",
requestBody: { values },
@ -97,7 +99,7 @@ export async function updatePaymentStatus(data: PaymentUpdateData): Promise<bool
// First, get all rows to find the matching registration
const response = await sheets.spreadsheets.values.get({
spreadsheetId: SPREADSHEET_ID,
range: `${SHEET_NAME}!A:P`,
range: `${SHEET_NAME}!A:Q`,
})
const rows = response.data.values || []
@ -124,7 +126,7 @@ export async function updatePaymentStatus(data: PaymentUpdateData): Promise<bool
// Update the row with payment information
const existingRow = rows[targetRowIndex - 1]
const updateRange = `${SHEET_NAME}!C${targetRowIndex}:P${targetRowIndex}`
const updateRange = `${SHEET_NAME}!C${targetRowIndex}:Q${targetRowIndex}`
const updateValues = [
[
data.email || existingRow[2] || "", // C: Email (from Mollie or existing)
@ -141,6 +143,7 @@ export async function updatePaymentStatus(data: PaymentUpdateData): Promise<bool
data.paymentDate || new Date().toISOString(), // N: Payment Date
data.accommodationVenue || "", // O: Accommodation Venue
data.accommodationType || "", // P: Accommodation Type
existingRow[16] || "", // Q: Want Food (preserve)
],
]
@ -169,7 +172,7 @@ export async function initializeSheetHeaders(): Promise<void> {
// Check if first row has data
const response = await sheets.spreadsheets.values.get({
spreadsheetId: SPREADSHEET_ID,
range: `${SHEET_NAME}!A1:P1`,
range: `${SHEET_NAME}!A1:Q1`,
})
if (!response.data.values || response.data.values.length === 0) {
@ -192,12 +195,13 @@ export async function initializeSheetHeaders(): Promise<void> {
"Payment Date",
"Accommodation Venue",
"Accommodation Type",
"Want Food",
],
]
await sheets.spreadsheets.values.update({
spreadsheetId: SPREADSHEET_ID,
range: `${SHEET_NAME}!A1:P1`,
range: `${SHEET_NAME}!A1:Q1`,
valueInputOption: "USER_ENTERED",
requestBody: { values: headers },
})