From d76ecbda7ab000442ff84004073ac90c344511ad Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 25 Mar 2026 21:35:47 +0100 Subject: [PATCH] feat: wire up email sending for configurator submissions - 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) --- src/app/(frontend)/api/configure/route.ts | 24 +++++++ src/lib/email.ts | 84 +++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 src/lib/email.ts diff --git a/src/app/(frontend)/api/configure/route.ts b/src/app/(frontend)/api/configure/route.ts index ba5e404..1b66c44 100644 --- a/src/app/(frontend)/api/configure/route.ts +++ b/src/app/(frontend)/api/configure/route.ts @@ -1,4 +1,5 @@ import { NextRequest, NextResponse } from 'next/server'; +import { sendBriefToClient, sendLeadNotification } from '@/lib/email'; // ─── Types ──────────────────────────────────────────────────────────────────── @@ -206,6 +207,29 @@ export async function POST(request: NextRequest) { // Generate the brief 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 }); } catch { return NextResponse.json( diff --git a/src/lib/email.ts b/src/lib/email.ts new file mode 100644 index 0000000..611f648 --- /dev/null +++ b/src/lib/email.ts @@ -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 + const htmlBrief = brief + .replace(/\*\*(.*?)\*\*/g, '$1') + .replace(/---/g, '
') + .replace(/\n\n/g, '

') + .replace(/\n/g, '
') + + await transporter.sendMail({ + from: `"LetsBe." <${process.env.SMTP_FROM || 'hello@letsbe.biz'}>`, + to, + subject: 'Your Project Brief from LetsBe.', + html: ` +

+
+

LetsBe.

+
+
+

Hi ${firstName},

+

+ Thank you for configuring your project with us. Here's your personalized brief: +

+
+

${htmlBrief}

+
+ +

+ Or reply to this email — we'll get back to you within 24 hours. +

+
+
+ LetsBe. Digital Studio · Côte d'Azur, France +
+
+ `, + }) +} + +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: ` +
+

New Configurator Submission

+ + + + +
Name:${name}
Company:${company || '—'}
Email:${to}
+
${brief}
+
+ `, + }) +}