feat: wire up email sending for configurator submissions
All checks were successful
Build & Push / build-and-push (push) Successful in 1m22s
All checks were successful
Build & Push / build-and-push (push) Successful in 1m22s
- Created src/lib/email.ts with Nodemailer + Poste.io SMTP - sendBriefToClient: formatted HTML brief email to the lead - sendLeadNotification: admin notification with submission details - Emails fire non-blocking (don't delay the brief response) - Only sends if SMTP_HOST and SMTP_PASS are configured Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
import { sendBriefToClient, sendLeadNotification } from '@/lib/email';
|
||||||
|
|
||||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -206,6 +207,29 @@ export async function POST(request: NextRequest) {
|
|||||||
// Generate the brief
|
// Generate the brief
|
||||||
const brief = generateMockBrief(body);
|
const brief = generateMockBrief(body);
|
||||||
|
|
||||||
|
// Send emails (non-blocking — don't fail the response if email fails)
|
||||||
|
if (process.env.SMTP_HOST && process.env.SMTP_PASS) {
|
||||||
|
Promise.allSettled([
|
||||||
|
sendBriefToClient({
|
||||||
|
to: body.email,
|
||||||
|
name: body.name,
|
||||||
|
company: body.company,
|
||||||
|
brief,
|
||||||
|
}),
|
||||||
|
sendLeadNotification({
|
||||||
|
to: body.email,
|
||||||
|
name: body.name,
|
||||||
|
company: body.company,
|
||||||
|
brief,
|
||||||
|
services: body.services,
|
||||||
|
email: body.email,
|
||||||
|
}),
|
||||||
|
]).catch(() => {
|
||||||
|
// Silently log — don't break the user flow
|
||||||
|
console.error('Email sending failed');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return NextResponse.json({ success: true, brief });
|
return NextResponse.json({ success: true, brief });
|
||||||
} catch {
|
} catch {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
|
|||||||
84
src/lib/email.ts
Normal file
84
src/lib/email.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import nodemailer from 'nodemailer'
|
||||||
|
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
host: process.env.SMTP_HOST,
|
||||||
|
port: Number(process.env.SMTP_PORT) || 587,
|
||||||
|
secure: false,
|
||||||
|
auth: {
|
||||||
|
user: process.env.SMTP_USER,
|
||||||
|
pass: process.env.SMTP_PASS,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
interface SendBriefEmailOptions {
|
||||||
|
to: string
|
||||||
|
name: string
|
||||||
|
company: string
|
||||||
|
brief: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendBriefToClient({ to, name, brief }: SendBriefEmailOptions) {
|
||||||
|
const firstName = name.split(' ')[0] || 'there'
|
||||||
|
|
||||||
|
// Convert markdown-style **bold** to HTML <strong>
|
||||||
|
const htmlBrief = brief
|
||||||
|
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
||||||
|
.replace(/---/g, '<hr style="border:none;border-top:1px solid #e5e7eb;margin:24px 0">')
|
||||||
|
.replace(/\n\n/g, '</p><p style="margin:0 0 12px;line-height:1.6">')
|
||||||
|
.replace(/\n/g, '<br>')
|
||||||
|
|
||||||
|
await transporter.sendMail({
|
||||||
|
from: `"LetsBe." <${process.env.SMTP_FROM || 'hello@letsbe.biz'}>`,
|
||||||
|
to,
|
||||||
|
subject: 'Your Project Brief from LetsBe.',
|
||||||
|
html: `
|
||||||
|
<div style="font-family:'Inter',Helvetica,Arial,sans-serif;max-width:600px;margin:0 auto;color:#191c1d">
|
||||||
|
<div style="padding:32px 0;border-bottom:2px solid #006494">
|
||||||
|
<h1 style="font-family:Georgia,serif;font-size:24px;margin:0;color:#006494">LetsBe.</h1>
|
||||||
|
</div>
|
||||||
|
<div style="padding:32px 0">
|
||||||
|
<p style="margin:0 0 16px;font-size:16px;line-height:1.6">Hi ${firstName},</p>
|
||||||
|
<p style="margin:0 0 24px;font-size:15px;line-height:1.6;color:#555">
|
||||||
|
Thank you for configuring your project with us. Here's your personalized brief:
|
||||||
|
</p>
|
||||||
|
<div style="background:#f8f9fa;border-radius:12px;padding:24px;font-size:14px;line-height:1.7;color:#333">
|
||||||
|
<p style="margin:0 0 12px;line-height:1.6">${htmlBrief}</p>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top:32px;text-align:center">
|
||||||
|
<a href="https://scheduling.letsbe.solutions/matt-ciaccio/letsbe"
|
||||||
|
style="display:inline-block;padding:14px 32px;background:linear-gradient(135deg,#006494,#5BA4D9);color:#fff;text-decoration:none;border-radius:8px;font-size:14px;font-weight:500">
|
||||||
|
Book a Consultation
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<p style="margin:32px 0 0;font-size:13px;color:#999;text-align:center">
|
||||||
|
Or reply to this email — we'll get back to you within 24 hours.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div style="border-top:1px solid #e5e7eb;padding:24px 0;font-size:12px;color:#999;text-align:center">
|
||||||
|
LetsBe. Digital Studio · Côte d'Azur, France
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendLeadNotification({ to, name, company, brief }: SendBriefEmailOptions & { services: string[]; email: string }) {
|
||||||
|
const adminEmail = process.env.ADMIN_EMAIL || 'hello@letsbe.biz'
|
||||||
|
|
||||||
|
await transporter.sendMail({
|
||||||
|
from: `"LetsBe. Configurator" <${process.env.SMTP_FROM || 'hello@letsbe.biz'}>`,
|
||||||
|
to: adminEmail,
|
||||||
|
subject: `New Lead: ${name}${company ? ` — ${company}` : ''}`,
|
||||||
|
html: `
|
||||||
|
<div style="font-family:'Inter',Helvetica,Arial,sans-serif;color:#191c1d">
|
||||||
|
<h2 style="font-family:Georgia,serif;color:#006494;margin:0 0 16px">New Configurator Submission</h2>
|
||||||
|
<table style="font-size:14px;line-height:1.6;border-collapse:collapse">
|
||||||
|
<tr><td style="padding:4px 16px 4px 0;font-weight:600">Name:</td><td>${name}</td></tr>
|
||||||
|
<tr><td style="padding:4px 16px 4px 0;font-weight:600">Company:</td><td>${company || '—'}</td></tr>
|
||||||
|
<tr><td style="padding:4px 16px 4px 0;font-weight:600">Email:</td><td><a href="mailto:${to}">${to}</a></td></tr>
|
||||||
|
</table>
|
||||||
|
<div style="margin-top:24px;background:#f8f9fa;border-radius:8px;padding:16px;font-size:13px;white-space:pre-wrap">${brief}</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user