import { useMemo, useState, type FormEvent } from 'react' import { Link } from 'react-router-dom' import { salesApi, type SalesLeadSource } from '@/api/sales' import { PageMeta } from '@/components/common/PageMeta' import { MarketingFooter } from '@/components/common/MarketingFooter' import { useAppConfig } from '@/hooks/useAppConfig' import '@/styles/landing.css' /* --------------------------------------------------------------------------- * Source detection * * The backend `/sales-leads` endpoint requires a `source` enum. We classify * by document.referrer: visitors landing on /contact-sales after viewing the * pricing page get tagged `pricing_page`; everything else is `landing_page`. * `register_footer` is reserved for the (future) sign-up footer CTA. * * Acceptable for v1 — server-side PostHog event uses this same `source` value. * ------------------------------------------------------------------------- */ function detectSource(): SalesLeadSource { if (typeof document === 'undefined') return 'landing_page' const ref = document.referrer || '' if (ref.includes('/pricing')) return 'pricing_page' return 'landing_page' } const TEAM_SIZE_OPTIONS: Array<{ value: string; label: string }> = [ { value: '', label: 'Select team size' }, { value: '1-2', label: '1–2' }, { value: '3-5', label: '3–5' }, { value: '6-10', label: '6–10' }, { value: '11-25', label: '11–25' }, { value: '26+', label: 'More than 26' }, ] interface FormState { name: string email: string company: string team_size: string message: string } const INITIAL: FormState = { name: '', email: '', company: '', team_size: '', message: '', } function ContactSalesNotFound() { return (

Page not found

This page is not available.

Go to login
) } export function ContactSalesPage() { const appConfig = useAppConfig() const [form, setForm] = useState(INITIAL) const [submitting, setSubmitting] = useState(false) const [submitted, setSubmitted] = useState(false) const [error, setError] = useState(null) const calendlyUrl = useMemo(() => { const raw = import.meta.env.VITE_CALENDLY_URL return typeof raw === 'string' && raw.trim().length > 0 ? raw.trim() : '' }, []) // Self-serve disabled: 404. (Same pattern as PricingPage — done after hooks.) if (!appConfig.isLoading && !appConfig.self_serve_enabled) { return ( <> ) } const handleChange = (field: keyof FormState) => (e: React.ChangeEvent) => { setForm((prev) => ({ ...prev, [field]: e.target.value })) } const handleSubmit = async (e: FormEvent) => { e.preventDefault() if (submitting) return const name = form.name.trim() const email = form.email.trim() const company = form.company.trim() if (!name || !email || !company) { setError('Please fill in name, work email, and company.') return } if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { setError('Enter a valid work email address.') return } setSubmitting(true) setError(null) try { await salesApi.createLead({ name, email, company, team_size: form.team_size || undefined, message: form.message.trim() || undefined, source: detectSource(), }) setSubmitted(true) } catch { // The backend may rate-limit (429) or reject for validation; surface a // generic message and allow retry. Don't leak internal errors. setError('Something went wrong. Please try again or email hello@resolutionflow.com.') } finally { setSubmitting(false) } } return (

Talk to Sales

Tell us about your MSP. We’ll reach out within 1 business day.

{submitted ? (

Thanks — we’ll reach out within 1 business day.

{calendlyUrl && (

Want to skip ahead?

Book a time
)}
) : (