mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-26 02:24:43 +00:00
feat(enterprise): contact form now pushes to salesforce 🙄 (#17964)
Co-authored-by: slickstef11 <stefan@wundergraph.com> Co-authored-by: Frank <frank@anoma.ly>
This commit is contained in:
@@ -2,11 +2,15 @@ import type { APIEvent } from "@solidjs/start/server"
|
||||
import { AWS } from "@opencode-ai/console-core/aws.js"
|
||||
import { i18n } from "~/i18n"
|
||||
import { localeFromRequest } from "~/lib/language"
|
||||
import { createLead } from "~/lib/salesforce"
|
||||
|
||||
interface EnterpriseFormData {
|
||||
name: string
|
||||
role: string
|
||||
company?: string
|
||||
email: string
|
||||
phone?: string
|
||||
alias?: string
|
||||
message: string
|
||||
}
|
||||
|
||||
@@ -14,33 +18,56 @@ export async function POST(event: APIEvent) {
|
||||
const dict = i18n(localeFromRequest(event.request))
|
||||
try {
|
||||
const body = (await event.request.json()) as EnterpriseFormData
|
||||
const trap = typeof body.alias === "string" ? body.alias.trim() : ""
|
||||
|
||||
if (trap) {
|
||||
return Response.json({ success: true, message: dict["enterprise.form.success.submitted"] }, { status: 200 })
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if (!body.name || !body.role || !body.email || !body.message) {
|
||||
return Response.json({ error: dict["enterprise.form.error.allFieldsRequired"] }, { status: 400 })
|
||||
}
|
||||
|
||||
// Validate email format
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
if (!emailRegex.test(body.email)) {
|
||||
return Response.json({ error: dict["enterprise.form.error.invalidEmailFormat"] }, { status: 400 })
|
||||
}
|
||||
|
||||
// Create email content
|
||||
const emailContent = `
|
||||
${body.message}<br><br>
|
||||
--<br>
|
||||
${body.name}<br>
|
||||
${body.role}<br>
|
||||
${body.email}`.trim()
|
||||
${body.company ? `${body.company}<br>` : ""}${body.email}<br>
|
||||
${body.phone ? `${body.phone}<br>` : ""}`.trim()
|
||||
|
||||
// Send email using AWS SES
|
||||
await AWS.sendEmail({
|
||||
to: "contact@anoma.ly",
|
||||
subject: `Enterprise Inquiry from ${body.name}`,
|
||||
body: emailContent,
|
||||
replyTo: body.email,
|
||||
})
|
||||
const [lead, mail] = await Promise.all([
|
||||
createLead({
|
||||
name: body.name,
|
||||
role: body.role,
|
||||
company: body.company,
|
||||
email: body.email,
|
||||
phone: body.phone,
|
||||
message: body.message,
|
||||
}),
|
||||
AWS.sendEmail({
|
||||
to: "contact@anoma.ly",
|
||||
subject: `Enterprise Inquiry from ${body.name}`,
|
||||
body: emailContent,
|
||||
replyTo: body.email,
|
||||
}).then(
|
||||
() => true,
|
||||
(err) => {
|
||||
console.error("Failed to send enterprise email:", err)
|
||||
return false
|
||||
},
|
||||
),
|
||||
])
|
||||
|
||||
if (!lead && !mail) {
|
||||
console.error("Enterprise inquiry delivery failed", { email: body.email })
|
||||
return Response.json({ error: dict["enterprise.form.error.internalServer"] }, { status: 500 })
|
||||
}
|
||||
|
||||
return Response.json({ success: true, message: dict["enterprise.form.success.submitted"] }, { status: 200 })
|
||||
} catch (error) {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
--color-text-strong: hsl(0, 5%, 12%);
|
||||
--color-text-inverted: hsl(0, 20%, 99%);
|
||||
--color-text-success: hsl(119, 100%, 35%);
|
||||
--color-text-error: hsl(4, 72%, 45%);
|
||||
|
||||
--color-border: hsl(30, 2%, 81%);
|
||||
--color-border-weak: hsl(0, 1%, 85%);
|
||||
@@ -50,6 +51,7 @@
|
||||
--color-text-strong: hsl(0, 15%, 94%);
|
||||
--color-text-inverted: hsl(0, 9%, 7%);
|
||||
--color-text-success: hsl(119, 60%, 72%);
|
||||
--color-text-error: hsl(4, 76%, 72%);
|
||||
|
||||
--color-border: hsl(0, 3%, 28%);
|
||||
--color-border-weak: hsl(0, 4%, 23%);
|
||||
@@ -454,6 +456,13 @@
|
||||
color: var(--color-text-success);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
[data-component="error-message"] {
|
||||
margin-top: 1rem;
|
||||
padding: 1rem 0;
|
||||
color: var(--color-text-error);
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,15 @@ export default function Enterprise() {
|
||||
const [formData, setFormData] = createSignal({
|
||||
name: "",
|
||||
role: "",
|
||||
company: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
alias: "",
|
||||
message: "",
|
||||
})
|
||||
const [isSubmitting, setIsSubmitting] = createSignal(false)
|
||||
const [showSuccess, setShowSuccess] = createSignal(false)
|
||||
const [error, setError] = createSignal("")
|
||||
|
||||
const handleInputChange = (field: string) => (e: Event) => {
|
||||
const target = e.target as HTMLInputElement | HTMLTextAreaElement
|
||||
@@ -26,6 +30,8 @@ export default function Enterprise() {
|
||||
|
||||
const handleSubmit = async (e: Event) => {
|
||||
e.preventDefault()
|
||||
setError("")
|
||||
setShowSuccess(false)
|
||||
setIsSubmitting(true)
|
||||
|
||||
try {
|
||||
@@ -42,13 +48,21 @@ export default function Enterprise() {
|
||||
setFormData({
|
||||
name: "",
|
||||
role: "",
|
||||
company: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
alias: "",
|
||||
message: "",
|
||||
})
|
||||
setTimeout(() => setShowSuccess(false), 5000)
|
||||
return
|
||||
}
|
||||
|
||||
const data = (await response.json().catch(() => null)) as { error?: string } | null
|
||||
setError(data?.error ?? i18n.t("enterprise.form.error.internalServer"))
|
||||
} catch (error) {
|
||||
console.error("Failed to submit form:", error)
|
||||
setError(i18n.t("enterprise.form.error.internalServer"))
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
@@ -147,6 +161,19 @@ export default function Enterprise() {
|
||||
<div data-component="enterprise-column-2">
|
||||
<div data-component="enterprise-form">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div class="sr-only" aria-hidden="true">
|
||||
<input
|
||||
type="text"
|
||||
name="alias"
|
||||
tabIndex={-1}
|
||||
autocomplete="new-password"
|
||||
inputmode="none"
|
||||
spellcheck={false}
|
||||
value={formData().alias}
|
||||
onInput={handleInputChange("alias")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div data-component="form-group">
|
||||
<label for="name">{i18n.t("enterprise.form.name.label")}</label>
|
||||
<input
|
||||
@@ -171,6 +198,17 @@ export default function Enterprise() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div data-component="form-group">
|
||||
<label for="company">{i18n.t("enterprise.form.company.label")}</label>
|
||||
<input
|
||||
id="company"
|
||||
type="text"
|
||||
value={formData().company}
|
||||
onInput={handleInputChange("company")}
|
||||
placeholder={i18n.t("enterprise.form.company.placeholder")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div data-component="form-group">
|
||||
<label for="email">{i18n.t("enterprise.form.email.label")}</label>
|
||||
<input
|
||||
@@ -183,6 +221,17 @@ export default function Enterprise() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div data-component="form-group">
|
||||
<label for="phone">{i18n.t("enterprise.form.phone.label")}</label>
|
||||
<input
|
||||
id="phone"
|
||||
type="tel"
|
||||
value={formData().phone}
|
||||
onInput={handleInputChange("phone")}
|
||||
placeholder={i18n.t("enterprise.form.phone.placeholder")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div data-component="form-group">
|
||||
<label for="message">{i18n.t("enterprise.form.message.label")}</label>
|
||||
<textarea
|
||||
@@ -201,6 +250,7 @@ export default function Enterprise() {
|
||||
</form>
|
||||
|
||||
{showSuccess() && <div data-component="success-message">{i18n.t("enterprise.form.success")}</div>}
|
||||
{error() && <div data-component="error-message">{error()}</div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user