chris 9d02417059 fix: pre-launch audit, calendar closed days, delivery rate reset, and swatch paths
- Fix vinyl add-on checkout: product line item was dropped when vinyl selected; entryUnitPrice also excluded base product price
- Store vinyl per-letter price on cart entry so CartDrawer charges the config price, not hardcoded 65¢
- Fix two bare modifiers.find() calls (use optional chaining) to prevent checkout crash on bad data
- Validate deliveryCents (must be non-negative integer) and customer name fields (no control chars) in checkout API
- Validate rateOverride values are non-negative numbers in delivery-quote API
- Add RFC 5545 iCalendar escaping to SUMMARY/LOCATION/DESCRIPTION fields to prevent calendar injection
- Add public /api/hours route; pickup and delivery calendars now fetch admin-saved hours and pre-grey closed days
- Reset delivery quote and slot when high-rate item is removed from cart
- Change delivery window copy from 2 hours to 1 hour (DeliveryDatePicker + terms page)
- Fix SVG paths: /color/images/ → /color-picker/images/ (balloon mask, shine, color backgrounds); was causing Safari ? placeholders
- Enlarge padlock icon in PaymentForm from 11px to 14px for better alignment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 09:22:42 -04:00

51 lines
1.6 KiB
TypeScript

import { NextResponse } from 'next/server'
import { geocode, calcDelivery, inferTier } from '@/lib/delivery'
import { readDeliveryRates } from '@/lib/delivery-rates'
export async function POST(request: Request) {
const { address, itemNames, rateOverride } = await request.json() as {
address: string
itemNames: string[]
rateOverride?: { base: number; perMile: number }
}
if (!address?.trim()) {
return NextResponse.json({ error: 'Address required' }, { status: 400 })
}
const coords = await geocode(address)
if (!coords) {
return NextResponse.json({ error: 'Address not found — please try a more specific address.' }, { status: 422 })
}
const tier = inferTier(itemNames ?? [])
const rates = readDeliveryRates()
// Apply per-item rate override if provided (overrides just base and perMile for the inferred tier)
if (rateOverride) {
if (typeof rateOverride.base !== 'number' || rateOverride.base < 0 ||
typeof rateOverride.perMile !== 'number' || rateOverride.perMile < 0) {
return NextResponse.json({ error: 'Invalid rate override' }, { status: 400 })
}
rates[tier] = {
...rates[tier],
base: rateOverride.base,
perMile: rateOverride.perMile,
}
}
const quote = await calcDelivery(coords.lat, coords.lng, tier, rates)
if (quote.miles > 40) {
return NextResponse.json(
{
error: `This address is ${quote.miles.toFixed(1)} miles away — online scheduling is available within 40 miles. Please contact us directly to arrange delivery.`,
tooFar: true,
},
{ status: 422 }
)
}
return NextResponse.json(quote)
}