/** * Server-side HTML email template renderers for receipts and repair estimates. * Uses inline CSS for email-client compatibility. */ interface Address { street?: string city?: string state?: string zip?: string } interface CompanyInfo { name: string phone?: string | null email?: string | null address?: Address | null } interface ReceiptLineItem { description: string qty: number unitPrice: string taxAmount: string lineTotal: string discountAmount?: string | null } interface ReceiptTransaction { transactionNumber: string subtotal: string discountTotal: string taxTotal: string total: string paymentMethod: string | null amountTendered: string | null changeGiven: string | null roundingAdjustment: string | null completedAt: string | null lineItems: ReceiptLineItem[] } interface ReceiptConfig { receipt_header?: string receipt_footer?: string receipt_return_policy?: string receipt_social?: string } interface ReceiptData { transaction: ReceiptTransaction company: CompanyInfo | null location: CompanyInfo | null } interface RepairLineItem { itemType: string description: string qty: number unitPrice: string totalPrice: string } interface RepairTicket { ticketNumber: string customerName: string customerPhone?: string | null itemDescription?: string | null serialNumber?: string | null problemDescription?: string | null estimatedCost?: string | null promisedDate?: string | null status: string } // ── Shared layout ────────────────────────────────────────────────────────── function wrapEmailLayout(storeName: string, bodyHtml: string): string { return `
Powered by LunarFront
${esc(config.receipt_header)}
` : '' const lineRows = txn.lineItems.map(item => `${esc(config.receipt_footer)}
` : '' const policyText = config?.receipt_return_policy ? `${esc(config.receipt_return_policy)}
` : '' const socialText = config?.receipt_social ? `${esc(config.receipt_social)}
` : '' const body = ` ${companyBlock} ${headerText}| Item | Qty | Price | Total |
|---|
| Subtotal | $${txn.subtotal} |
| Tax | $${txn.taxTotal} |
| Total | $${txn.total} |
| Type | Description | Qty | Price | Total |
|---|
This is an estimate and the final cost may vary. Please contact us with any questions.
${company.phone ? `Phone: ${esc(company.phone)}
` : ''} ` return wrapEmailLayout(storeName, body) } export function renderEstimateEmailText(ticket: RepairTicket, lineItems: RepairLineItem[], company: CompanyInfo): string { const lines = [ company.name, `Repair Estimate — Ticket #${ticket.ticketNumber}`, `Customer: ${ticket.customerName}`, '', ...(ticket.itemDescription ? [`Item: ${ticket.itemDescription}${ticket.serialNumber ? ` (S/N: ${ticket.serialNumber})` : ''}`] : []), ...(ticket.problemDescription ? [`Issue: ${ticket.problemDescription}`] : []), ...(ticket.promisedDate ? [`Estimated completion: ${formatDateOnly(ticket.promisedDate)}`] : []), '', ...lineItems.map(i => `${TYPE_LABELS[i.itemType] ?? i.itemType}: ${i.description} x${i.qty} — $${i.totalPrice}`), '', ...(ticket.estimatedCost ? [`Estimated Total: $${ticket.estimatedCost}`] : []), '', 'This is an estimate and the final cost may vary.', ...(company.phone ? [`Phone: ${company.phone}`] : []), ] return lines.join('\n') }