perfect but the email template has to have an attached pdf where the es
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
|
||||
'use server';
|
||||
|
||||
import { z } from 'zod';
|
||||
import nodemailer from 'nodemailer';
|
||||
import { PDFDocument, rgb, StandardFonts } from 'pdf-lib';
|
||||
import db from '@/lib/db';
|
||||
import { getEmailTemplate } from './email';
|
||||
import type { FormData } from '@/components/cost-estimator/cost-estimator-form';
|
||||
|
||||
const inputSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
email: z.string().email(),
|
||||
customHours: z.number(),
|
||||
readyMadeHours: z.number(),
|
||||
formData: z.any(), // Keeping this flexible for now
|
||||
});
|
||||
|
||||
type InputType = z.infer<typeof inputSchema>;
|
||||
|
||||
// Helper to get SMTP settings from the database
|
||||
async function getSmtpSettings() {
|
||||
const stmt = db.prepare("SELECT key, value FROM settings WHERE key LIKE 'smtp_%'");
|
||||
const settings = stmt.all() as { key: string, value: string }[];
|
||||
const config = settings.reduce((acc, setting) => {
|
||||
acc[setting.key.replace('smtp_', '')] = setting.value;
|
||||
return acc;
|
||||
}, {} as Record<string, string>);
|
||||
return {
|
||||
host: config.server,
|
||||
port: parseInt(config.port || '587', 10),
|
||||
secure: parseInt(config.port || '587', 10) === 465, // true for 465, false for other ports
|
||||
auth: {
|
||||
user: config.username,
|
||||
pass: config.password,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
async function createEstimatePdf(data: InputType): Promise<Buffer> {
|
||||
const { name, customHours, readyMadeHours, formData } = data;
|
||||
const pdfDoc = await PDFDocument.create();
|
||||
const page = pdfDoc.addPage();
|
||||
const { width, height } = page.getSize();
|
||||
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
|
||||
const boldFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
|
||||
const brandColor = rgb(0.16, 0.47, 0.94); // #2979FF
|
||||
|
||||
let y = height - 50;
|
||||
|
||||
// Header
|
||||
page.drawText('EstimateFlow', { x: 50, y, font: boldFont, size: 24, color: brandColor });
|
||||
y -= 20;
|
||||
page.drawLine({
|
||||
start: { x: 50, y: y },
|
||||
end: { x: width - 50, y: y },
|
||||
thickness: 2,
|
||||
color: brandColor,
|
||||
});
|
||||
y -= 30;
|
||||
|
||||
// Subheader
|
||||
page.drawText(`Project Estimate for: ${name}`, { x: 50, y, font: boldFont, size: 18 });
|
||||
y -= 20;
|
||||
page.drawText(`Date: ${new Date().toLocaleDateString()}`, { x: 50, y, font, size: 12, color: rgb(0.3, 0.3, 0.3) });
|
||||
y -= 40;
|
||||
|
||||
// Summary
|
||||
page.drawText('Estimate Summary', { x: 50, y, font: boldFont, size: 16 });
|
||||
y -= 25;
|
||||
|
||||
const drawEstimateBox = (title: string, hours: number, description: string, boxY: number) => {
|
||||
page.drawRectangle({
|
||||
x: 50,
|
||||
y: boxY - 80,
|
||||
width: width - 100,
|
||||
height: 90,
|
||||
borderColor: rgb(0.9, 0.9, 0.9),
|
||||
borderWidth: 1,
|
||||
color: rgb(0.98, 0.98, 0.98),
|
||||
borderRadius: 5
|
||||
});
|
||||
page.drawText(title, { x: 65, y: boxY, font: boldFont, size: 14 });
|
||||
page.drawText(`${hours}+ hours`, { x: 65, y: boxY - 30, font: boldFont, size: 28, color: brandColor });
|
||||
page.drawText(description, { x: 65, y: boxY - 60, font, size: 10, color: rgb(0.4, 0.4, 0.4) });
|
||||
}
|
||||
|
||||
drawEstimateBox('Custom Development', customHours, 'A fully custom solution tailored to your specific needs.', y);
|
||||
y -= 110;
|
||||
drawEstimateBox('Ready-Made Tools', readyMadeHours, 'Leveraging pre-built components for a faster turnaround.', y);
|
||||
y -= 110;
|
||||
|
||||
// Disclaimer
|
||||
page.drawText('Disclaimer:', { x: 50, y, font: boldFont, size: 12 });
|
||||
y -= 20;
|
||||
const disclaimer = 'This is a preliminary estimate based on the provided selections. The final cost may vary after a detailed project analysis and scope definition. Please contact us for a comprehensive quote.';
|
||||
page.drawText(disclaimer, {
|
||||
x: 50,
|
||||
y,
|
||||
font,
|
||||
size: 10,
|
||||
color: rgb(0.5, 0.5, 0.5),
|
||||
lineHeight: 14,
|
||||
maxWidth: width - 100
|
||||
});
|
||||
|
||||
const pdfBytes = await pdfDoc.save();
|
||||
return Buffer.from(pdfBytes);
|
||||
}
|
||||
|
||||
|
||||
export async function sendEstimateEmail(data: InputType): Promise<{ success: boolean, message?: string }> {
|
||||
const validation = inputSchema.safeParse(data);
|
||||
if (!validation.success) {
|
||||
return { success: false, message: 'Invalid data provided.' };
|
||||
}
|
||||
|
||||
try {
|
||||
const smtpSettings = await getSmtpSettings();
|
||||
if (!smtpSettings.host || !smtpSettings.auth.user || !smtpSettings.auth.pass) {
|
||||
return { success: false, message: 'SMTP settings are not configured. Please configure them in the admin panel.' };
|
||||
}
|
||||
|
||||
const transporter = nodemailer.createTransport(smtpSettings);
|
||||
|
||||
await transporter.verify();
|
||||
|
||||
const pdfBuffer = await createEstimatePdf(validation.data);
|
||||
const emailTemplate = await getEmailTemplate();
|
||||
|
||||
const mailOptions = {
|
||||
from: `EstimateFlow <${smtpSettings.auth.user}>`,
|
||||
to: validation.data.email,
|
||||
subject: emailTemplate.subject.replace('[User Name]', validation.data.name),
|
||||
html: emailTemplate.body.replace('[User Name]', validation.data.name).replace('[EstimateDetails]', `
|
||||
<p style="font-size: 16px; line-height: 1.6;">
|
||||
Please find your detailed project estimate attached to this email as a PDF.
|
||||
</p>
|
||||
`),
|
||||
attachments: [
|
||||
{
|
||||
filename: 'EstimateFlow_Project_Estimate.pdf',
|
||||
content: pdfBuffer,
|
||||
contentType: 'application/pdf',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
await transporter.sendMail(mailOptions);
|
||||
return { success: true };
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('Failed to send estimate email:', error);
|
||||
|
||||
if (error.code === 'ECONNREFUSED') {
|
||||
return { success: false, message: 'Could not connect to SMTP server. Check server address and port.' };
|
||||
}
|
||||
if (error.code === 'EAUTH') {
|
||||
return { success: false, message: 'SMTP authentication failed. Check username and password.' };
|
||||
}
|
||||
|
||||
return { success: false, message: error.message || 'An unexpected error occurred while sending the email.' };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user