import React, { useState, useRef } from "react"; /** * PlateHub – Single-file React app * ------------------------------------------------------ * What this does * - Lets users submit: plate photos + proof of ownership + contact info * - You review and send them a 7-day cash offer * - Built to be embedded into an existing website as a single page * * How to integrate * - Drop this component into your React site, or export to a static bundle * - Replace the `SUBMIT_ENDPOINT` with your backend or a form service * - Add your analytics / CRM hook in `handleSubmit` where indicated */ const SUBMIT_ENDPOINT = "/api/platehub/submit"; // TODO: replace with your endpoint or Formspree URL export default function PlateHubApp() { const [step, setStep] = useState(1); const [isSubmitting, setIsSubmitting] = useState(false); const [submittedId, setSubmittedId] = useState(""); const [error, setError] = useState(""); const [previewUrls, setPreviewUrls] = useState({ plate: "", proof: "" }); const [form, setForm] = useState({ firstName: "", lastName: "", email: "", phone: "", plateNumber: "", plateState: "DE", plateType: "Passenger (PC)", askingPrice: "", notes: "", agree: false, }); const plateFileRef = useRef(null); const proofFileRef = useRef(null); function onChange(e) { const { name, value, type, checked } = e.target; setForm((f) => ({ ...f, [name]: type === "checkbox" ? checked : value })); } function onFileChange(kind, file) { if (!file) return; const url = URL.createObjectURL(file); setPreviewUrls((p) => ({ ...p, [kind]: url })); } function validate() { const problems = []; if (!form.firstName.trim()) problems.push("First name is required."); if (!form.lastName.trim()) problems.push("Last name is required."); if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)) problems.push("Valid email is required."); if (!form.phone.trim()) problems.push("Phone is required."); if (!form.plateNumber.trim()) problems.push("Plate number is required."); if (!plateFileRef.current?.files?.[0]) problems.push("A clear photo of the plate is required."); if (!proofFileRef.current?.files?.[0]) problems.push("Proof of ownership is required."); if (!form.agree) problems.push("You must agree to the 7-day offer policy."); return problems; } async function handleSubmit(e) { e.preventDefault(); setError(""); const problems = validate(); if (problems.length) { setError(problems.join("\n")); return; } try { setIsSubmitting(true); // Build multipart form data const fd = new FormData(); Object.entries(form).forEach(([k, v]) => fd.append(k, String(v))); if (plateFileRef.current?.files?.[0]) fd.append("platePhoto", plateFileRef.current.files[0]); if (proofFileRef.current?.files?.[0]) fd.append("proofDoc", proofFileRef.current.files[0]); // Send to your backend (replace endpoint above). For demo, we'll simulate success if no real endpoint. let ok = true, id = "PH-" + Math.random().toString(36).slice(2, 8).toUpperCase(); if (SUBMIT_ENDPOINT && !SUBMIT_ENDPOINT.startsWith("/api/platehub/submit")) { // Real submit mode const res = await fetch(SUBMIT_ENDPOINT, { method: "POST", body: fd }); ok = res.ok; id = res.ok ? (await res.json()).id || id : ""; } else { // Demo mode await new Promise((r) => setTimeout(r, 800)); } if (!ok) throw new Error("Submission failed. Please try again."); setSubmittedId(id); setStep(3); // TODO: Add your analytics/CRM hook here (HubSpot, Zapier, etc.) } catch (err) { setError(err.message || "Something went wrong."); } finally { setIsSubmitting(false); } } return (
PlateHub
7‑day cash offers for your plate
Get an offer for your license plate in minutes.
Upload a photo of your plate and proof of ownership. We’ll review and send you a no‑obligation cash offer that’s valid for 7 days.
- ✔️ Fast, private valuation
- ✔️ Secure file uploads
- ✔️ Delaware low‑digit friendly
Submission received 🎉
Thanks, {form.firstName}. Your reference ID is {id || "pending"}.
We’ll review your documents and send your offer (valid for 7 days) to {form.email}.