chris 0ea1b98a1f feat: required delivery toggle with custom rates per item
Items can now be marked as "requires delivery" in admin — these items
cannot be picked up and must be delivered (and struck).

- Admin item editor: "Requires delivery" checkbox + custom base/per-mile
  rate fields that appear when the toggle is on
- ProductCard: "Delivery & setup required" note on the card
- CartDrawer: pickup toggle is hidden and replaced with an explanation
  when any cart item requires delivery; the quote call passes the
  item's custom rate override (highest base + highest per-mile wins
  when multiple requires-delivery items are in the cart)
- delivery-quote API: accepts optional rateOverride to apply per-item
  pricing on top of the inferred tier

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 09:31:29 -04:00

47 lines
1.4 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) {
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)
}