diff --git a/copy/COPY-GUIDE.md b/copy/COPY-GUIDE.md index 71f4107..96453bb 100644 --- a/copy/COPY-GUIDE.md +++ b/copy/COPY-GUIDE.md @@ -536,6 +536,55 @@ Documento que se adjunta en la entrega por WhatsApp y se descarga desde el panel --- +## 6.b Emails al cliente final (funnel B2C) + +Emails que se envían al cliente desde la marca del reformista. Tono cercano, honesto y orientativo, +igual que el resto del funnel. `[Reformista]` = nombre de la empresa; se usa como remitente. + +### Email de entrega del presupuesto (PDF adjunto) + +Se envía siempre al terminar el perfil, con el PDF del presupuesto adjunto. + +- **Asunto:** *Tu presupuesto de reforma con [Reformista] ya está listo* +- **Cuerpo:** + +> Hola [Nombre], +> +> Aquí tienes tu **presupuesto orientativo de reforma**, preparado por [Reformista]. Lo encontrarás +> adjunto en PDF, con el render de cómo quedaría tu espacio y el desglose por partidas. +> +> ⚠️ Es una **estimación**. El precio definitivo lo confirma [Reformista] en una visita gratuita en +> tu casa, donde mide todo con detalle y lo ajusta. +> +> Si te encaja, responde a este email o escríbenos por WhatsApp y concertamos la visita. Sin +> compromiso. +> +> — +> [Reformista] + +### Email con enlace al formulario (subir imágenes) + +Se envía cuando el cliente eligió continuar por llamada y necesita un sitio donde subir las fotos +del espacio. `[url]` apunta a su formulario personal del funnel. + +- **Asunto:** *Sube las fotos de tu reforma para [Reformista]* +- **Cuerpo:** + +> Hola [Nombre], +> +> Para preparar tu render y tu presupuesto, [Reformista] necesita ver el espacio. Sube unas fotos +> de cada zona desde este enlace, cuando te venga bien: +> +> 👉 [Subir mis fotos]([url]) +> +> Tardas un minuto y puedes añadir las que quieras. En cuanto las tengamos, seguimos con tu +> presupuesto. +> +> — +> [Reformista] + +--- + ## 7. Microcopy del panel del reformista | Elemento | Texto | diff --git a/mvp/b2c/src/lib/email/mailer.ts b/mvp/b2c/src/lib/email/mailer.ts new file mode 100644 index 0000000..7c07137 --- /dev/null +++ b/mvp/b2c/src/lib/email/mailer.ts @@ -0,0 +1,93 @@ +import nodemailer, { type Transporter } from 'nodemailer'; +import { env, emailConfigurado } from '@/lib/env'; + +let _transport: Transporter | null = null; + +// Transport perezoso: solo se crea cuando hay SMTP configurado y se va a enviar de verdad. +function getTransport(): Transporter | null { + if (!emailConfigurado()) return null; + if (_transport) return _transport; + const port = Number(env.SMTP_PORT ?? 587); + _transport = nodemailer.createTransport({ + host: env.SMTP_HOST, + port, + secure: port === 465, // 465 = TLS implícito; 587/1025 = STARTTLS/none + auth: env.SMTP_USER ? { user: env.SMTP_USER, pass: env.SMTP_PASS } : undefined, + }); + return _transport; +} + +function escapeHtml(s: string): string { + return s.replace(/[&<>"]/g, (c) => + c === '&' ? '&' : c === '<' ? '<' : c === '>' ? '>' : '"', + ); +} + +// Email de entrega del presupuesto con el PDF adjunto (COPY-GUIDE §6.b). Best-effort: si no hay +// SMTP configurado o el envío falla devuelve false sin lanzar, para no romper el pipeline. +export async function enviarPresupuestoEmail(opts: { + to: string; + nombre: string; + empresa: string; + pdf: Buffer; + filename: string; +}): Promise { + const transport = getTransport(); + if (!transport) return false; + + const nombre = escapeHtml(opts.nombre.split(' ')[0] ?? opts.nombre); + const empresa = escapeHtml(opts.empresa); + const html = `

Hola ${nombre},

+

Aquí tienes tu presupuesto orientativo de reforma, preparado por ${empresa}. Lo encontrarás adjunto en PDF, con el render de cómo quedaría tu espacio y el desglose por partidas.

+

⚠️ Es una estimación. El precio definitivo lo confirma ${empresa} en una visita gratuita en tu casa, donde mide todo con detalle y lo ajusta.

+

Si te encaja, responde a este email o escríbenos por WhatsApp y concertamos la visita. Sin compromiso.

+


${empresa}

`; + + try { + await transport.sendMail({ + from: env.EMAIL_FROM, + to: opts.to, + subject: `Tu presupuesto de reforma con ${opts.empresa} ya está listo`, + html, + attachments: [{ filename: opts.filename, content: opts.pdf, contentType: 'application/pdf' }], + }); + return true; + } catch (err) { + console.error('enviarPresupuestoEmail error:', err); + return false; + } +} + +// Email con el enlace al formulario para subir imágenes (COPY-GUIDE §6.b), usado en el canal +// llamada. Best-effort, mismas garantías que el anterior. +export async function enviarEnlaceFormulario(opts: { + to: string; + nombre: string; + empresa: string; + url: string; +}): Promise { + const transport = getTransport(); + if (!transport) return false; + + const nombre = escapeHtml(opts.nombre.split(' ')[0] ?? opts.nombre); + const empresa = escapeHtml(opts.empresa); + const url = encodeURI(opts.url); + const html = `

Hola ${nombre},

+

Para preparar tu render y tu presupuesto, ${empresa} necesita ver el espacio. Sube unas fotos de cada zona desde este enlace, cuando te venga bien:

+

👉 Subir mis fotos

+

Tardas un minuto y puedes añadir las que quieras. En cuanto las tengamos, seguimos con tu presupuesto.

+


${empresa}

`; + + try { + await transport.sendMail({ + from: env.EMAIL_FROM, + to: opts.to, + subject: `Sube las fotos de tu reforma para ${opts.empresa}`, + html, + }); + return true; + } catch (err) { + console.error('enviarEnlaceFormulario error:', err); + return false; + } +}