"use client" import { useEffect, useMemo, useState } from "react" import { useForm } from "react-hook-form" import { AlertCircle, CheckCircle, MessageSquare, PhoneCall, Wrench } from "lucide-react" import { FormButton } from "./form-button" import { FormInput } from "./form-input" import { FormSelect } from "./form-select" import { FormTextarea } from "./form-textarea" import { SmsConsentFields } from "./sms-consent-fields" import { PublicSectionHeader } from "@/components/public-surface" import { CONTACT_INTENT_OPTIONS } from "@/lib/site-chat/intents" import { businessConfig } from "@/lib/seo-config" import { SMS_CONSENT_VERSION } from "@/lib/sms-compliance" interface ContactFormData { firstName: string lastName: string email: string phone: string company: string intent: string message: string serviceTextConsent: boolean marketingTextConsent: boolean consentVersion: string consentCapturedAt: string consentSourcePage: string confirmEmail?: string source?: string page?: string } interface ContactFormProps { onSubmit?: (data: ContactFormData) => void className?: string defaultIntent?: string } export function ContactForm({ onSubmit, className, defaultIntent = "" }: ContactFormProps) { const [isSubmitting, setIsSubmitting] = useState(false) const [isSubmitted, setIsSubmitted] = useState(false) const [submitError, setSubmitError] = useState(null) const intentOptions = useMemo( () => CONTACT_INTENT_OPTIONS.map((option) => ({ label: option, value: option })), [], ) const { register, handleSubmit, formState: { errors }, reset, setValue, watch, } = useForm({ defaultValues: { source: "website", page: "", intent: defaultIntent, serviceTextConsent: false, marketingTextConsent: false, consentVersion: SMS_CONSENT_VERSION, consentCapturedAt: "", consentSourcePage: "", }, }) const watchedValues = watch() const canSubmit = Boolean(watchedValues.serviceTextConsent) useEffect(() => { if (typeof window !== "undefined") { const pathname = window.location.pathname setValue("page", pathname) setValue("consentSourcePage", pathname) setValue("consentVersion", SMS_CONSENT_VERSION) if (defaultIntent) { setValue("intent", defaultIntent) } } }, [defaultIntent, setValue]) const buildResetValues = () => ({ source: "website", page: typeof window !== "undefined" ? window.location.pathname : "", intent: defaultIntent, serviceTextConsent: false, marketingTextConsent: false, consentVersion: SMS_CONSENT_VERSION, consentCapturedAt: "", consentSourcePage: typeof window !== "undefined" ? window.location.pathname : "", firstName: "", lastName: "", email: "", phone: "", company: "", message: "", confirmEmail: "", }) const onFormSubmit = async (data: ContactFormData) => { setIsSubmitting(true) setSubmitError(null) try { const consentCapturedAt = new Date().toISOString() const formData = { ...data, consentCapturedAt, consentSourcePage: data.consentSourcePage || data.page || window.location.pathname, consentVersion: data.consentVersion || SMS_CONSENT_VERSION, timestamp: consentCapturedAt, url: window.location.href, } const response = await fetch("/api/contact", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(formData), }) const result = await response.json() if (!response.ok || !result.success) { throw new Error(result.error || result.message || "Failed to submit form") } setIsSubmitted(true) reset(buildResetValues()) if (onSubmit) { onSubmit(formData) } } catch (error) { console.error("Form submission error:", error) setSubmitError("We couldn't send that right now. Please try again or call us.") } finally { setIsSubmitting(false) } } const onFormReset = () => { setIsSubmitted(false) setSubmitError(null) reset(buildResetValues()) } if (isSubmitted) { return (

Thanks, we’ve got it.

Our team will review your request and follow up within one business day.

Send Another Request
) } return (
{ const phoneDigits = value.replace(/\D/g, "") return phoneDigits.length >= 10 && phoneDigits.length <= 15 ? true : "Please enter a valid phone number" }, })} />

For repairs or moving, include the machine model and a clear text description of the issue. You can also text photos or videos to{" "} {businessConfig.publicSmsNumber} {" "} if that's easier.

setValue("serviceTextConsent", checked, { shouldValidate: true })} onMarketingChange={(checked) => setValue("marketingTextConsent", checked, { shouldValidate: true })} serviceError={errors.serviceTextConsent?.message} marketingError={errors.marketingTextConsent?.message} />

Need a faster handoff?

Call during business hours or send us a detailed request here and we'll take it from there.

{submitError ? (
{submitError}
) : null} {isSubmitting ? "Sending..." : "Send Request"} value || "Please agree to receive service-related SMS for this request", })} />
) }