diff --git a/estore/src/app/api/booking-request/route.ts b/estore/src/app/api/booking-request/route.ts new file mode 100644 index 0000000..c507aee --- /dev/null +++ b/estore/src/app/api/booking-request/route.ts @@ -0,0 +1,31 @@ +import { NextRequest, NextResponse } from 'next/server' +import { sendBookingRequest } from '@/lib/notify' +import type { EmailLineItem } from '@/lib/notify' + +export async function POST(req: NextRequest) { + try { + const body = await req.json() + const { customerName, customerPhone, customerEmail, preferredTime, address, lineItems } = body + + if (!customerName?.trim() || !customerPhone?.trim() || !customerEmail?.trim() || !preferredTime?.trim()) { + return NextResponse.json( + { error: 'Name, phone, email, and preferred time are required.' }, + { status: 400 } + ) + } + + await sendBookingRequest({ + customerName: customerName.trim(), + customerPhone: customerPhone.trim(), + customerEmail: customerEmail.trim(), + preferredTime: preferredTime.trim(), + address: address?.trim() ?? '', + lineItems: (lineItems ?? []) as EmailLineItem[], + }) + + return NextResponse.json({ ok: true }) + } catch (err) { + console.error('[booking-request]', err) + return NextResponse.json({ error: 'Failed to send request. Please try again.' }, { status: 500 }) + } +} diff --git a/estore/src/app/api/slots/route.ts b/estore/src/app/api/slots/route.ts index 98a7c76..daadab4 100644 --- a/estore/src/app/api/slots/route.ts +++ b/estore/src/app/api/slots/route.ts @@ -49,15 +49,17 @@ export async function GET(req: NextRequest) { } const { lat, lng } = coordsResult.value - const busyBlocks = busyResult.status === 'fulfilled' ? busyResult.value : [] - const calendarConnected = !!process.env.CALDAV_URL && busyResult.status === 'fulfilled' - if (busyResult.status === 'rejected') { console.error('[slots] CalDAV fetch failed:', busyResult.reason) - } else { - console.log(`[slots] ${date}: ${busyBlocks.length} busy block(s) from calendar`) + return NextResponse.json( + { error: 'Availability check failed — please contact us to book your delivery.' }, + { status: 503 } + ) } + const busyBlocks = busyResult.value + console.log(`[slots] ${date}: ${busyBlocks.length} busy block(s) from calendar`) + const driveResult = await drivingInfo(SHOP_LAT, SHOP_LNG, lat, lng) const driveMinutes = driveResult?.minutes ?? 30 // fall back to 30 min if OSRM is down @@ -65,7 +67,7 @@ export async function GET(req: NextRequest) { const slots = await getAvailableSlots(date, driveMinutes, tier, busyBlocks, lat, lng, hoursConfig) const blockMinutes = driveMinutes + JOB_MINUTES[tier] + driveMinutes - return NextResponse.json({ slots, driveMinutes, blockMinutes, calendarConnected }) + return NextResponse.json({ slots, driveMinutes, blockMinutes }) } catch (err) { console.error('[slots] Unexpected error:', err) return NextResponse.json( diff --git a/estore/src/components/BookingRequestPanel.tsx b/estore/src/components/BookingRequestPanel.tsx new file mode 100644 index 0000000..05f30a4 --- /dev/null +++ b/estore/src/components/BookingRequestPanel.tsx @@ -0,0 +1,107 @@ +'use client' + +import { useState } from 'react' +import { BASE } from '@/lib/basepath' +import type { EmailLineItem } from '@/lib/notify' + +interface Props { + address: string + defaultName: string + defaultPhone: string + defaultEmail: string + lineItems: EmailLineItem[] +} + +export default function BookingRequestPanel({ address, defaultName, defaultPhone, defaultEmail, lineItems }: Props) { + const [name, setName] = useState(defaultName) + const [phone, setPhone] = useState(defaultPhone) + const [email, setEmail] = useState(defaultEmail) + const [preferredTime, setPreferredTime] = useState('') + const [submitting, setSubmitting] = useState(false) + const [success, setSuccess] = useState(false) + const [error, setError] = useState('') + + const handleSubmit = async () => { + setError('') + setSubmitting(true) + try { + const res = await fetch(`${BASE}/api/booking-request`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ customerName: name, customerPhone: phone, customerEmail: email, preferredTime, address, lineItems }), + }) + const data = await res.json() + if (!res.ok) { setError(data.error ?? 'Something went wrong.'); return } + setSuccess(true) + } catch { + setError('Network error — please try again.') + } finally { + setSubmitting(false) + } + } + + if (success) { + return ( +
+ We’ll reach out to confirm your delivery time. Check your email for a copy of your request. +
++ Send us your order & we’ll confirm a time +
+ +{error}
+ )} + + +