feat(06-02): branded signing request email + mailer utilities

- Add SigningRequestEmail.tsx React Email component (navy/gold brand colors, CTA button)
- Add signing-mailer.tsx with sendSigningRequestEmail() and sendAgentNotificationEmail()
- Uses CONTACT_SMTP_* env vars (same SMTP provider as contact form)
- Sender: "Teressa Copeland" <teressa@teressacopelandhomes.com>
This commit is contained in:
Chandler Copeland
2026-03-20 11:29:05 -06:00
parent e1306dab69
commit f41db49ff7
2 changed files with 145 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
import {
Html,
Head,
Body,
Container,
Heading,
Text,
Button,
Hr,
Preview,
} from '@react-email/components';
interface SigningRequestEmailProps {
documentName: string;
signingUrl: string;
expiryDate: string; // e.g. "March 25, 2026"
clientName?: string;
}
export function SigningRequestEmail({
documentName,
signingUrl,
expiryDate,
clientName,
}: SigningRequestEmailProps) {
return (
<Html>
<Head />
<Preview>Please review and sign: {documentName}</Preview>
<Body style={{ backgroundColor: '#ffffff', fontFamily: 'Georgia, serif' }}>
<Container style={{ maxWidth: '560px', margin: '40px auto', padding: '0 20px' }}>
<Heading style={{ color: '#1B2B4B', fontSize: '22px', marginBottom: '8px' }}>
Teressa Copeland Homes
</Heading>
<Hr style={{ borderColor: '#C9A84C', marginBottom: '24px' }} />
{clientName && (
<Text style={{ color: '#333', fontSize: '16px' }}>Hello {clientName},</Text>
)}
<Text style={{ color: '#333', fontSize: '16px', lineHeight: '1.6' }}>
You have a document ready for your review and signature:
</Text>
<Text
style={{ color: '#1B2B4B', fontSize: '18px', fontWeight: 'bold', margin: '16px 0' }}
>
{documentName}
</Text>
<Text style={{ color: '#555', fontSize: '15px' }}>
No account needed just click the button below.
</Text>
<Button
href={signingUrl}
style={{
backgroundColor: '#C9A84C',
color: '#ffffff',
padding: '14px 32px',
borderRadius: '4px',
fontSize: '16px',
fontWeight: 'bold',
textDecoration: 'none',
display: 'inline-block',
margin: '20px 0',
}}
>
Review &amp; Sign
</Button>
<Text style={{ color: '#888', fontSize: '13px' }}>
This link expires on {expiryDate}. If you did not expect this document, you can safely
ignore this email.
</Text>
<Hr style={{ borderColor: '#e0e0e0', marginTop: '32px' }} />
<Text style={{ color: '#aaa', fontSize: '12px' }}>
Teressa Copeland Homes · Utah Licensed Real Estate Agent
</Text>
</Container>
</Body>
</Html>
);
}

View File

@@ -0,0 +1,67 @@
import { render } from '@react-email/render';
import nodemailer from 'nodemailer';
import { SigningRequestEmail } from '@/emails/SigningRequestEmail';
import React from 'react';
function createTransporter() {
return nodemailer.createTransport({
host: process.env.CONTACT_SMTP_HOST!,
port: Number(process.env.CONTACT_SMTP_PORT ?? 587),
secure: false,
auth: {
user: process.env.CONTACT_EMAIL_USER!,
pass: process.env.CONTACT_EMAIL_PASS!,
},
});
}
export async function sendSigningRequestEmail(opts: {
to: string;
clientName?: string;
documentName: string;
signingUrl: string;
expiresAt: Date;
}): Promise<void> {
const expiryDate = opts.expiresAt.toLocaleDateString('en-US', {
month: 'long',
day: 'numeric',
year: 'numeric',
});
const html = await render(
React.createElement(SigningRequestEmail, {
documentName: opts.documentName,
signingUrl: opts.signingUrl,
expiryDate,
clientName: opts.clientName,
})
);
const transporter = createTransporter();
await transporter.sendMail({
from: '"Teressa Copeland" <teressa@teressacopelandhomes.com>',
to: opts.to,
subject: `Please sign: ${opts.documentName}`,
html,
});
}
export async function sendAgentNotificationEmail(opts: {
clientName: string;
documentName: string;
signedAt: Date;
}): Promise<void> {
const formattedTime = opts.signedAt.toLocaleString('en-US', {
month: 'long',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: '2-digit',
timeZoneName: 'short',
});
const transporter = createTransporter();
await transporter.sendMail({
from: '"Teressa Copeland Homes" <teressa@teressacopelandhomes.com>',
to: 'teressa@teressacopelandhomes.com',
subject: `Signed: ${opts.documentName}`,
text: `${opts.clientName} has signed "${opts.documentName}" on ${formattedTime}.`,
});
}