Send contact form submissions by email through Resend

Server Action validates the fields, drops honeypot hits and emails the
enquiry to rey@aigrowth360.io with the visitor as reply-to. Needs
RESEND_API_KEY in the environment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Alejandro Veras
2026-10-12 12:06:39 -04:00
parent 39b2c0a857
commit e21a23a245
3 changed files with 129 additions and 32 deletions
+26 -32
View File
@@ -1,55 +1,49 @@
"use client";
import { useState, type FormEvent } from "react";
import { useActionState } from "react";
import { sendContact, type ContactState } from "@/app/contact/actions";
import { CONTACT_EMAIL, serviceNav } from "@/lib/routes";
const interests = [...serviceNav.map((s) => s.name), "Not sure yet"];
const initial: ContactState = { status: "idle" };
// No form backend is wired up yet, so submitting composes an email to the team
// in the visitor's mail client. Swap `onSubmit` for a real endpoint when one exists.
export default function ContactForm() {
const [sent, setSent] = useState(false);
const [state, action, pending] = useActionState(sendContact, initial);
function onSubmit(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
const data = new FormData(e.currentTarget);
const get = (k: string) => String(data.get(k) ?? "").trim();
const subject = `Growth conversation — ${get("company") || get("name")}`;
const body = [
`Name: ${get("name")}`,
`Email: ${get("email")}`,
`Company: ${get("company")}`,
`Website: ${get("website")}`,
`Interested in: ${data.getAll("interest").join(", ") || "—"}`,
"",
get("message"),
].join("\n");
window.location.href = `mailto:${CONTACT_EMAIL}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
setSent(true);
if (state.status === "sent") {
return (
<div className="contact-form form-sent" role="status">
<span className="card-num">Received</span>
<h3 className="card-title">Thanks — your message is with the team.</h3>
<p className="copy">We’ll review what you shared and get back to you shortly.</p>
</div>
);
}
return (
<form className="contact-form" onSubmit={onSubmit}>
<form className="contact-form" action={action}>
<div className="field-row">
<label className="field">
<span>Name</span>
<input name="name" autoComplete="name" required />
<input name="name" autoComplete="name" required maxLength={120} />
</label>
<label className="field">
<span>Work email</span>
<input name="email" type="email" autoComplete="email" required />
<input name="email" type="email" autoComplete="email" required maxLength={200} />
</label>
</div>
<div className="field-row">
<label className="field">
<span>Company</span>
<input name="company" autoComplete="organization" />
<input name="company" autoComplete="organization" maxLength={160} />
</label>
<label className="field">
<span>Website</span>
<input name="website" type="url" inputMode="url" placeholder="https://" />
<input name="website" type="url" inputMode="url" placeholder="https://" maxLength={300} />
</label>
</div>
{/* Honeypot for bots — hidden from people and assistive tech. */}
<input name="company_url" className="hp" tabIndex={-1} autoComplete="off" aria-hidden="true" />
<fieldset className="field">
<legend>What are you looking at?</legend>
<div className="chips">
@@ -63,15 +57,15 @@ export default function ContactForm() {
</fieldset>
<label className="field">
<span>What’s happening in the business?</span>
<textarea name="message" rows={5} required />
<textarea name="message" rows={5} required maxLength={5000} />
</label>
<button type="submit" className="btn btn-solid">
Send to the Team <span className="arrow">→</span>
<button type="submit" className="btn btn-solid" disabled={pending}>
{pending ? "Sending…" : "Send to the Team"} <span className="arrow">→</span>
</button>
<p className="form-note" role="status">
{sent
? `Your email app should have opened with the details. If it didn’t, write to ${CONTACT_EMAIL}.`
: `Opens your email app addressed to ${CONTACT_EMAIL}.`}
<p className={`form-note${state.status === "error" ? " is-error" : ""}`} role="status">
{state.status === "error"
? `${state.message} You can also write to ${CONTACT_EMAIL}.`
: "We reply personally — no automated sequences."}
</p>
</form>
);