feat(email): implement professional responsive b2b html email template
All checks were successful
Staging Build / build (push) Successful in 3m9s

This commit is contained in:
DanielS
2026-09-08 00:09:16 +02:00
parent ec11ad8498
commit e78fe7c4b8
4 changed files with 482 additions and 222 deletions

View File

@@ -12,7 +12,13 @@ import { buildCustomerSnapshot, buildOrderSnapshot } from '@/lib/license-transfo
import type { Category, EndCustomer, Order, Product, Profile, WizardSelections } from '@/lib/types'
import { getProducts, getCategories } from '@/lib/actions/products'
import { validateWizardSelections } from '@/lib/actions/validation'
import { getOrderEmailTemplate, getStatusEmailTemplate, buildEmailItemsSection } from '@/lib/actions/email-templates'
import {
generateOrderEmailHtml,
generateOrderEmailSubject,
getOrderEmailTemplate,
getStatusEmailTemplate,
buildEmailItemsSection,
} from '@/lib/actions/email-templates'
// ─── Hilfsfunktionen ─────────────────────────────────────────────────────────
@@ -301,24 +307,25 @@ export async function submitOrder(params: {
`
}
const itemsSection = buildEmailItemsSection(items)
const emailTemplate = getOrderEmailTemplate({
const emailTemplate = generateOrderEmailHtml({
orderNumber,
status: 'pending',
formattedDate,
customerCompanyName: customerSnapshot.company_name,
totalDetailsText,
totalDetailsHtml,
itemsDetailsText: itemsSection.text,
itemsDetailsHtml: itemsSection.html,
items,
partnerCompanyName,
partnerUserName,
partnerUserEmail
}, `${process.env.NEXT_PUBLIC_SITE_URL || 'https://staging.hephex.de'}`, false)
partnerUserEmail,
taxRate,
oneTimeNet,
monthlyNet,
})
const mailSubject = generateOrderEmailSubject(orderNumber, 'pending')
await sendMail({
to: user.email,
subject: `Anfragebestätigung ${orderNumber}`,
subject: mailSubject,
text: emailTemplate.text,
html: emailTemplate.html,
attachments: [
@@ -343,7 +350,7 @@ export async function submitOrder(params: {
if (adminEmail !== user.email) {
await sendMail({
to: adminEmail,
subject: `[Admin-Kopie] Anfragebestätigung ${orderNumber}`,
subject: `[Admin-Kopie] ${mailSubject}`,
text: emailTemplate.text,
html: emailTemplate.html,
attachments: [
@@ -455,24 +462,27 @@ export async function updateOrderStatus(
if (!userError && user && user.email) {
const orderNumber = order.order_number || order.id.slice(0, 8)
const statusLabelMap: Record<string, string> = {
pending: 'Eingegangen',
pending_approval: 'Wartet auf Freigabe',
in_review: 'In Prüfung',
approved: 'Freigegeben',
active: 'Aktiviert',
completed: 'Abgeschlossen',
cancelled: 'Storniert',
rejected: 'Abgelehnt',
}
const oldLabel = statusLabelMap[oldStatus] || oldStatus
const newLabel = statusLabelMap[newStatus] || newStatus
const customerSnapshot = updatedOrder.customer_data || {}
const orderSnapshot = updatedOrder.order_data || {}
const formattedDate = new Date(updatedOrder.created_at || Date.now()).toLocaleDateString('de-DE')
const statusEmail = getStatusEmailTemplate(orderNumber, oldLabel, newLabel, newStatus)
const statusEmail = generateOrderEmailHtml({
orderNumber,
status: newStatus,
formattedDate,
customerCompanyName: customerSnapshot.company_name || 'Endkunde',
items: orderSnapshot.items || [],
partnerCompanyName: partnerInfo.partnerCompanyName,
partnerUserName: partnerInfo.partnerUserName,
partnerUserEmail: partnerInfo.partnerUserEmail,
taxRate: orderSnapshot.tax_rate ?? 19,
})
const mailSubject = generateOrderEmailSubject(orderNumber, newStatus)
const mailOptions: any = {
to: user.email,
subject: `Statusänderung Ihrer Anfrage ${orderNumber}`,
subject: mailSubject,
text: statusEmail.text,
html: statusEmail.html,
}
@@ -504,7 +514,7 @@ export async function updateOrderStatus(
await sendMail({
...mailOptions,
to: adminEmail,
subject: `[Admin-Kopie] Statusänderung Ihrer Anfrage ${orderNumber}`
subject: `[Admin-Kopie] ${mailSubject}`
})
}
}
@@ -642,11 +652,29 @@ export async function rejectOrder(
const { data: { user: orderUser }, error: userError } = await admin.auth.admin.getUserById(order.user_id)
if (!userError && orderUser && orderUser.email) {
const orderNumber = order.order_number || order.id.slice(0, 8)
const statusEmail = getStatusEmailTemplate(orderNumber, 'Wartet auf Freigabe', 'Abgelehnt', 'rejected', reason)
const partnerInfo = await fetchOrderPartnerInfo(admin, updatedOrder.company_id, updatedOrder.user_id)
const customerSnapshot = updatedOrder.customer_data || {}
const orderSnapshot = updatedOrder.order_data || {}
const formattedDate = new Date(updatedOrder.created_at || Date.now()).toLocaleDateString('de-DE')
const statusEmail = generateOrderEmailHtml({
orderNumber,
status: 'rejected',
formattedDate,
customerCompanyName: customerSnapshot.company_name || 'Endkunde',
rejectionReason: reason,
items: orderSnapshot.items || [],
partnerCompanyName: partnerInfo.partnerCompanyName,
partnerUserName: partnerInfo.partnerUserName,
partnerUserEmail: partnerInfo.partnerUserEmail,
taxRate: orderSnapshot.tax_rate ?? 19,
})
const mailSubject = generateOrderEmailSubject(orderNumber, 'rejected')
await sendMail({
to: orderUser.email,
subject: `Anfrage abgelehnt: ${orderNumber}`,
subject: mailSubject,
text: statusEmail.text,
html: statusEmail.html
})
@@ -664,7 +692,7 @@ export async function rejectOrder(
if (adminEmail !== orderUser.email) {
await sendMail({
to: adminEmail,
subject: `[Admin-Kopie] Anfrage abgelehnt: ${orderNumber}`,
subject: `[Admin-Kopie] ${mailSubject}`,
text: statusEmail.text,
html: statusEmail.html
})
@@ -672,7 +700,7 @@ export async function rejectOrder(
}
}
} catch (adminMailError) {
console.error('Failed to notify admins of order rejection:', adminMailError)
console.error('Failed to notify admins of rejected order:', adminMailError)
}
}
} catch (mailError) {