/** * OrderForm.tsx * --------------------------------------------------------------------------- * Store order-request form island. Modeled on ContactForm.tsx: same * idle/submitting/success/error states, honeypot, optional Turnstile, aria-live * status, and identical input styling. Posts to /api/order. * * This collects an ORDER REQUEST only — no payment fields, no checkout. The * submission is emailed to Greg. The server endpoint (/api/order) independently * re-checks store-availability.json and 403s if orders are closed, so the gate * can't be bypassed from the client. * * The form requires an explicit acknowledgment of the store policies (checkbox) * before it can be submitted. */ import { useState, useRef, useEffect, type FormEvent } from 'react'; import BrandIcon from '@components/brand/BrandIcon'; type Status = 'idle' | 'submitting' | 'success' | 'error'; type OrderType = 'cards' | 'deckBoxes' | 'both'; interface FieldErrors { name?: string; email?: string; details?: string; quantity?: string; agree?: string; } const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; declare global { interface Window { turnstile?: { render: (el: string | HTMLElement, opts: Record) => string; reset: (id?: string) => void; remove: (id: string) => void; }; } } interface ColorOption { name: string; hex: string; inStock: boolean; } interface Props { /** Public Cloudflare Turnstile site key. Omit to skip Turnstile (e.g. dev). */ turnstileSiteKey?: string; /** * Available filament colors for printed items, from store-products.ts. Only * in-stock colors are offered in the color dropdown. Omit to hide the color * selector entirely (e.g. if no colors are configured). */ colors?: ColorOption[]; } export default function OrderForm({ turnstileSiteKey, colors = [] }: Props) { const [status, setStatus] = useState('idle'); const [errors, setErrors] = useState({}); const [errorMsg, setErrorMsg] = useState(''); // Tracks the order type so the color selector can show/hide. Mirrors the //
{errors.name &&

{errors.name}

}
{errors.email &&

{errors.email}

}
{/* Color selector — only for printed items, only when colors are configured and in stock. The list comes from store-products.ts. */} {showColors && (

Colors shown are what’s loaded right now. See the deck-boxes page for swatches.

)}
{errors.quantity &&

{errors.quantity}

}