diff --git a/.env.local.example b/.env.local.example index 2fa613e3e..9872e55dd 100644 --- a/.env.local.example +++ b/.env.local.example @@ -14,3 +14,6 @@ ANTHROPIC_API_KEY= NEXT_PUBLIC_ALGOLIA_APP_ID= NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY= NEXT_PUBLIC_ALGOLIA_INDEX_NAME= + +# Required for Contact Sales form submission (n8n → Attio pipeline) +NEXT_PUBLIC_ATTIO_WEBHOOK_URL= diff --git a/app/en/resources/contact-us/contact-cards.tsx b/app/en/resources/contact-us/contact-cards.tsx index 292ae1ca9..aaa95f343 100644 --- a/app/en/resources/contact-us/contact-cards.tsx +++ b/app/en/resources/contact-us/contact-cards.tsx @@ -1,30 +1,247 @@ "use client"; import { + Button, Dialog, DialogContent, + DialogHeader, + DialogTitle, Discord, Github, + Input, + Textarea, } from "@arcadeai/design-system"; -import { HeartPulse, Mail, Shield, Users } from "lucide-react"; +import { + AlertOctagon, + CheckCircle, + HeartPulse, + Mail, + Shield, + Users, +} from "lucide-react"; import posthog from "posthog-js"; -import { useEffect, useRef, useState } from "react"; +import type React from "react"; +import { useState } from "react"; import { QuickStartCard } from "../../../_components/quick-start-card"; +const WEBHOOK_URL = process.env.NEXT_PUBLIC_ATTIO_WEBHOOK_URL; + +function getUtmParams(): Record { + if (typeof window === "undefined") { + return {}; + } + const params = new URLSearchParams(window.location.search); + const utms: Record = {}; + for (const key of [ + "utm_source", + "utm_medium", + "utm_campaign", + "utm_content", + ]) { + const value = params.get(key); + if (value) { + utms[key] = value; + } + } + return utms; +} + +function collectFormFields(formData: FormData): Record { + const fields: Record = {}; + for (const [key, value] of formData.entries()) { + if (key === "website") { + continue; + } + if (typeof value === "string" && value.trim()) { + fields[key] = value.trim(); + } + } + return fields; +} + +async function submitForm( + fields: Record +): Promise<{ success: boolean; error?: string }> { + if (!WEBHOOK_URL) { + return { success: false, error: "Webhook URL not configured" }; + } + + const payload = { + submission_id: crypto.randomUUID(), + form_type: "contact_sales", + fields, + context: { + pageUri: window.location.href, + pageName: "Contact Sales", + timestamp: new Date().toISOString(), + ...getUtmParams(), + }, + }; + + const response = await fetch(WEBHOOK_URL, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); + + if (response.ok) { + return { success: true }; + } + return { + success: false, + error: `HTTP ${response.status}: ${response.statusText}`, + }; +} + +async function submitHoneypot(): Promise { + if (!WEBHOOK_URL) { + return; + } + try { + await fetch(WEBHOOK_URL, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ _hp: true }), + }); + } catch { + // Swallow errors for spam submissions + } +} + +function ContactSalesForm({ onSuccess }: { onSuccess: () => void }) { + const [isSubmitting, setIsSubmitting] = useState(false); + const [error, setError] = useState(""); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setIsSubmitting(true); + setError(""); + + const formData = new FormData(e.currentTarget); + const honeypot = formData.get("website") as string; + + if (honeypot) { + await submitHoneypot(); + onSuccess(); + return; + } + + const fields = collectFormFields(formData); + + try { + const result = await submitForm(fields); + + if (result.success) { + posthog.capture("contact_sales_form_submitted", { + form_type: "contact_sales", + page: "Contact Sales", + source: "contact_us_page", + }); + onSuccess(); + } else { + posthog.capture("contact_sales_form_submit_failed", { + form_type: "contact_sales", + page: "Contact Sales", + error: result.error, + }); + setError("Oops! Something went wrong. Please try again."); + } + } catch (err) { + const errorMessage = err instanceof Error ? err.message : "Network error"; + posthog.capture("contact_sales_form_submit_failed", { + form_type: "contact_sales", + page: "Contact Sales", + error: errorMessage, + }); + setError("Oops! Something went wrong. Please try again."); + } finally { + setIsSubmitting(false); + } + }; + + return ( +
+
+ + +
+ + +