feat(email): include order item configurations in confirmation email
All checks were successful
Staging Build / build (push) Successful in 2m54s
All checks were successful
Staging Build / build (push) Successful in 2m54s
This commit is contained in:
@@ -4,6 +4,8 @@ interface EmailDetails {
|
||||
customerCompanyName: string
|
||||
totalDetailsText: string
|
||||
totalDetailsHtml: string
|
||||
itemsDetailsText?: string
|
||||
itemsDetailsHtml?: string
|
||||
}
|
||||
|
||||
export function getOrderEmailTemplate(
|
||||
@@ -16,7 +18,7 @@ export function getOrderEmailTemplate(
|
||||
? `Deine Anfrage ${details.orderNumber} wurde aktualisiert.`
|
||||
: `Deine Anfrage ${details.orderNumber} ist bei uns eingegangen und wird bearbeitet.`
|
||||
|
||||
const text = `Hallo,\n\n${isUpdate ? 'Deine Anfrage wurde aktualisiert.' : 'Deine Anfrage ist bei uns eingegangen.'}\n\nDetails:\n- Nummer: ${details.orderNumber}\n- Datum: ${details.formattedDate}\n- Kunde: ${details.customerCompanyName}\n${details.totalDetailsText}\n\nDeine Anfragebestätigung findest du im Anhang als PDF.\n\nPDF-Link: ${siteUrl}/api/orders/${details.orderNumber}/download\n\nViele Grüße,\nDein CASPOS Team`
|
||||
const text = `Hallo,\n\n${isUpdate ? 'Deine Anfrage wurde aktualisiert.' : 'Deine Anfrage ist bei uns eingegangen.'}\n\nDetails:\n- Nummer: ${details.orderNumber}\n- Datum: ${details.formattedDate}\n- Kunde: ${details.customerCompanyName}\n${details.itemsDetailsText || ''}\n${details.totalDetailsText}\n\nDeine Anfragebestätigung findest du im Anhang als PDF.\n\nPDF-Link: ${siteUrl}/api/orders/${details.orderNumber}/download\n\nViele Grüße,\nDein CASPOS Team`
|
||||
|
||||
const html = `
|
||||
<div style="font-family: sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #e2e8f0; border-radius: 8px;">
|
||||
@@ -38,11 +40,14 @@ export function getOrderEmailTemplate(
|
||||
<td style="padding: 4px 0; font-weight: bold;">Kunde:</td>
|
||||
<td style="padding: 4px 0;">${details.customerCompanyName}</td>
|
||||
</tr>
|
||||
${details.itemsDetailsHtml || ''}
|
||||
${details.totalDetailsHtml}
|
||||
</table>
|
||||
</div>
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.5;">Die Bestätigung liegt als PDF im Anhang.</p>
|
||||
</div>
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.5;">
|
||||
<a href="${siteUrl}/api/orders/${details.orderNumber}/download" style="color: #3b82f6; text-decoration: underline; font-weight: 500;">Anfragebestätigung PDF herunterladen</a>
|
||||
</p>
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.5; margin-top: 24px;">Viele Grüße,<br>Dein CASPOS Team</p>
|
||||
</div>
|
||||
`
|
||||
|
||||
@@ -16,6 +16,69 @@ import { getOrderEmailTemplate, getStatusEmailTemplate } from '@/lib/actions/ema
|
||||
|
||||
// ─── Hilfsfunktionen ─────────────────────────────────────────────────────────
|
||||
|
||||
function buildEmailItemsSection(items: any[]) {
|
||||
const groupedItems: { [key: string]: any[] } = {}
|
||||
items.forEach((item: any) => {
|
||||
const devName = item.device_name || 'Kasse 1'
|
||||
if (!groupedItems[devName]) {
|
||||
groupedItems[devName] = []
|
||||
}
|
||||
groupedItems[devName].push(item)
|
||||
})
|
||||
|
||||
let itemsDetailsText = '\nKassen-Konfigurationen:'
|
||||
let itemsDetailsHtml = `
|
||||
<tr>
|
||||
<td colspan="2" style="padding: 12px 0 6px 0; border-top: 1px solid #e2e8f0; font-weight: bold; color: #0f172a;">Kassen-Konfigurationen:</td>
|
||||
</tr>
|
||||
`
|
||||
|
||||
Object.entries(groupedItems).forEach(([deviceName, devItems]) => {
|
||||
itemsDetailsText += `\n- Kasse: \${deviceName}`
|
||||
itemsDetailsHtml += `
|
||||
<tr style="background-color: #f1f5f9;">
|
||||
<td colspan="2" style="padding: 6px 8px; font-weight: bold; color: #1e3a8a; font-size: 13px;">Kasse: \${deviceName}</td>
|
||||
</tr>
|
||||
`
|
||||
|
||||
devItems.forEach((item: any) => {
|
||||
itemsDetailsText += `\n * \${item.product_name} (\${item.category_name}): \${item.base_price.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} \${item.billing_interval === 'one_time' ? 'einmalig' : 'mtl.'}`
|
||||
itemsDetailsHtml += `
|
||||
<tr>
|
||||
<td style="padding: 6px 8px; font-size: 13px; color: #334155; font-weight: 500;">
|
||||
\${item.product_name} <span style="font-size: 11px; color: #64748b;">(\${item.category_name})</span>
|
||||
</td>
|
||||
<td style="padding: 6px 8px; text-align: right; font-size: 13px; font-weight: bold; color: #0f172a;">
|
||||
\${item.base_price.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} \${item.billing_interval === 'one_time' ? 'einmalig' : 'mtl.'}
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
|
||||
item.selected_modules?.forEach((mod: any) => {
|
||||
const qty = mod.quantity || 1
|
||||
const price = mod.total_price ?? (mod.price * qty)
|
||||
itemsDetailsText += `\n + \${mod.module_name} \${qty > 1 ? \`(x\${qty})\` : ''}: +\${price.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} mtl.`
|
||||
itemsDetailsHtml += `
|
||||
<tr>
|
||||
<td style="padding: 4px 8px 4px 20px; color: #64748b; font-size: 12px;">
|
||||
+ \${mod.module_name} \${qty > 1 ? \`<span style="font-size: 10px; font-weight: 600;">(x\${qty})</span>\` : ''}
|
||||
</td>
|
||||
<td style="padding: 4px 8px; text-align: right; color: #64748b; font-size: 12px;">
|
||||
+\${price.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })} mtl.
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
text: itemsDetailsText + '\n',
|
||||
html: itemsDetailsHtml
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Erzeugt eine menschenlesbare, nicht-sequenzielle Bestellnummer.
|
||||
* Format: BE-YYYY-NNNNN (z.B. BE-2026-84731)
|
||||
@@ -256,12 +319,16 @@ export async function submitOrder(params: {
|
||||
`
|
||||
}
|
||||
|
||||
const itemsSection = buildEmailItemsSection(items)
|
||||
|
||||
const emailTemplate = getOrderEmailTemplate({
|
||||
orderNumber,
|
||||
formattedDate,
|
||||
customerCompanyName: customerSnapshot.company_name,
|
||||
totalDetailsText,
|
||||
totalDetailsHtml
|
||||
totalDetailsHtml,
|
||||
itemsDetailsText: itemsSection.text,
|
||||
itemsDetailsHtml: itemsSection.html
|
||||
}, `${process.env.NEXT_PUBLIC_SITE_URL || 'https://staging.hephex.de'}`, false)
|
||||
|
||||
await sendMail({
|
||||
@@ -577,12 +644,16 @@ export async function updateOrder(
|
||||
`
|
||||
}
|
||||
|
||||
const itemsSection = buildEmailItemsSection(items)
|
||||
|
||||
const emailTemplate = getOrderEmailTemplate({
|
||||
orderNumber: order.order_number,
|
||||
formattedDate,
|
||||
customerCompanyName: customerSnapshot.company_name,
|
||||
totalDetailsText,
|
||||
totalDetailsHtml
|
||||
totalDetailsHtml,
|
||||
itemsDetailsText: itemsSection.text,
|
||||
itemsDetailsHtml: itemsSection.html
|
||||
}, `${process.env.NEXT_PUBLIC_SITE_URL || 'https://staging.hephex.de'}`, true)
|
||||
|
||||
await sendMail({
|
||||
|
||||
Reference in New Issue
Block a user