feat: CC admins on orders and include partner info
This commit is contained in:
@@ -6,6 +6,9 @@ interface EmailDetails {
|
|||||||
totalDetailsHtml: string
|
totalDetailsHtml: string
|
||||||
itemsDetailsText?: string
|
itemsDetailsText?: string
|
||||||
itemsDetailsHtml?: string
|
itemsDetailsHtml?: string
|
||||||
|
partnerCompanyName?: string
|
||||||
|
partnerUserName?: string
|
||||||
|
partnerUserEmail?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOrderEmailTemplate(
|
export function getOrderEmailTemplate(
|
||||||
@@ -18,7 +21,47 @@ export function getOrderEmailTemplate(
|
|||||||
? `Deine Anfrage ${details.orderNumber} wurde aktualisiert.`
|
? `Deine Anfrage ${details.orderNumber} wurde aktualisiert.`
|
||||||
: `Deine Anfrage ${details.orderNumber} ist bei uns eingegangen und wird bearbeitet.`
|
: `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.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`
|
let partnerText = ''
|
||||||
|
let partnerHtml = ''
|
||||||
|
if (details.partnerCompanyName || details.partnerUserName || details.partnerUserEmail) {
|
||||||
|
partnerText = `\nPartner-Informationen:`
|
||||||
|
if (details.partnerCompanyName) partnerText += `\n- Firma: ${details.partnerCompanyName}`
|
||||||
|
if (details.partnerUserName) partnerText += `\n- Benutzer: ${details.partnerUserName}`
|
||||||
|
if (details.partnerUserEmail) partnerText += `\n- E-Mail: ${details.partnerUserEmail}`
|
||||||
|
partnerText += `\n`
|
||||||
|
|
||||||
|
partnerHtml = `
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="padding: 8px 0; border-top: 1px solid #e2e8f0; font-weight: bold; color: #0f172a;">Partner-Informationen:</td>
|
||||||
|
</tr>
|
||||||
|
`
|
||||||
|
if (details.partnerCompanyName) {
|
||||||
|
partnerHtml += `
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 4px 0; padding-left: 10px; color: #475569; font-weight: bold;">Firma:</td>
|
||||||
|
<td style="padding: 4px 0; color: #475569;">${details.partnerCompanyName}</td>
|
||||||
|
</tr>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
if (details.partnerUserName) {
|
||||||
|
partnerHtml += `
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 4px 0; padding-left: 10px; color: #475569; font-weight: bold;">Benutzer:</td>
|
||||||
|
<td style="padding: 4px 0; color: #475569;">${details.partnerUserName}</td>
|
||||||
|
</tr>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
if (details.partnerUserEmail) {
|
||||||
|
partnerHtml += `
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 4px 0; padding-left: 10px; color: #475569; font-weight: bold;">E-Mail:</td>
|
||||||
|
<td style="padding: 4px 0; color: #475569;">${details.partnerUserEmail}</td>
|
||||||
|
</tr>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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${partnerText}${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 = `
|
const html = `
|
||||||
<div style="font-family: sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #e2e8f0; border-radius: 8px;">
|
<div style="font-family: sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #e2e8f0; border-radius: 8px;">
|
||||||
@@ -40,6 +83,7 @@ export function getOrderEmailTemplate(
|
|||||||
<td style="padding: 4px 0; font-weight: bold;">Kunde:</td>
|
<td style="padding: 4px 0; font-weight: bold;">Kunde:</td>
|
||||||
<td style="padding: 4px 0;">${details.customerCompanyName}</td>
|
<td style="padding: 4px 0;">${details.customerCompanyName}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
${partnerHtml}
|
||||||
${details.itemsDetailsHtml || ''}
|
${details.itemsDetailsHtml || ''}
|
||||||
${details.totalDetailsHtml}
|
${details.totalDetailsHtml}
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export async function submitOrder(params: {
|
|||||||
// Check if user is assigned to a company or is admin
|
// Check if user is assigned to a company or is admin
|
||||||
const { data: dbUser } = await supabase
|
const { data: dbUser } = await supabase
|
||||||
.from('users')
|
.from('users')
|
||||||
.select('role, company_id')
|
.select('role, company_id, first_name, last_name, email')
|
||||||
.eq('id', user.id)
|
.eq('id', user.id)
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
@@ -77,6 +77,22 @@ export async function submitOrder(params: {
|
|||||||
throw new Error('Sie müssen einer Firma zugewiesen sein, um eine Anfrage zu erstellen.')
|
throw new Error('Sie müssen einer Firma zugewiesen sein, um eine Anfrage zu erstellen.')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let partnerCompanyName = ''
|
||||||
|
if (dbUser?.company_id) {
|
||||||
|
const { data: comp } = await supabase
|
||||||
|
.from('companies')
|
||||||
|
.select('name')
|
||||||
|
.eq('id', dbUser.company_id)
|
||||||
|
.single()
|
||||||
|
if (comp) {
|
||||||
|
partnerCompanyName = comp.name
|
||||||
|
}
|
||||||
|
} else if (isAdminUser) {
|
||||||
|
partnerCompanyName = 'Administrator / Direktbestellung'
|
||||||
|
}
|
||||||
|
const partnerUserName = dbUser ? [dbUser.first_name, dbUser.last_name].filter(Boolean).join(' ') : ''
|
||||||
|
const partnerUserEmail = dbUser?.email || user.email
|
||||||
|
|
||||||
// Fetch catalog directly from DB to prevent client tampering
|
// Fetch catalog directly from DB to prevent client tampering
|
||||||
const dbProducts = await getProducts()
|
const dbProducts = await getProducts()
|
||||||
const dbCategories = await getCategories()
|
const dbCategories = await getCategories()
|
||||||
@@ -267,7 +283,10 @@ export async function submitOrder(params: {
|
|||||||
totalDetailsText,
|
totalDetailsText,
|
||||||
totalDetailsHtml,
|
totalDetailsHtml,
|
||||||
itemsDetailsText: itemsSection.text,
|
itemsDetailsText: itemsSection.text,
|
||||||
itemsDetailsHtml: itemsSection.html
|
itemsDetailsHtml: itemsSection.html,
|
||||||
|
partnerCompanyName,
|
||||||
|
partnerUserName,
|
||||||
|
partnerUserEmail
|
||||||
}, `${process.env.NEXT_PUBLIC_SITE_URL || 'https://staging.hephex.de'}`, false)
|
}, `${process.env.NEXT_PUBLIC_SITE_URL || 'https://staging.hephex.de'}`, false)
|
||||||
|
|
||||||
await sendMail({
|
await sendMail({
|
||||||
@@ -283,6 +302,37 @@ export async function submitOrder(params: {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Notify all admins
|
||||||
|
try {
|
||||||
|
const adminClient = createAdminClient()
|
||||||
|
const { data: admins } = await adminClient
|
||||||
|
.from('users')
|
||||||
|
.select('email')
|
||||||
|
.eq('role', 'admin')
|
||||||
|
if (admins) {
|
||||||
|
const adminEmails = admins.map((a: any) => a.email).filter(Boolean)
|
||||||
|
for (const adminEmail of adminEmails) {
|
||||||
|
if (adminEmail !== user.email) {
|
||||||
|
await sendMail({
|
||||||
|
to: adminEmail,
|
||||||
|
subject: `[Admin-Kopie] Anfragebestätigung ${orderNumber}`,
|
||||||
|
text: emailTemplate.text,
|
||||||
|
html: emailTemplate.html,
|
||||||
|
attachments: [
|
||||||
|
{
|
||||||
|
filename: `Anfragebestaetigung_${orderNumber}.pdf`,
|
||||||
|
content: buffer,
|
||||||
|
contentType: 'application/pdf',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (adminMailError) {
|
||||||
|
console.error('Failed to notify admins of new order:', adminMailError)
|
||||||
|
}
|
||||||
} catch (mailError) {
|
} catch (mailError) {
|
||||||
console.error('Failed to send order confirmation mail:', mailError)
|
console.error('Failed to send order confirmation mail:', mailError)
|
||||||
}
|
}
|
||||||
@@ -407,6 +457,29 @@ export async function updateOrderStatus(
|
|||||||
}
|
}
|
||||||
|
|
||||||
await sendMail(mailOptions)
|
await sendMail(mailOptions)
|
||||||
|
|
||||||
|
// Notify admins
|
||||||
|
try {
|
||||||
|
const adminClient = createAdminClient()
|
||||||
|
const { data: admins } = await adminClient
|
||||||
|
.from('users')
|
||||||
|
.select('email')
|
||||||
|
.eq('role', 'admin')
|
||||||
|
if (admins) {
|
||||||
|
const adminEmails = admins.map((a: any) => a.email).filter(Boolean)
|
||||||
|
for (const adminEmail of adminEmails) {
|
||||||
|
if (adminEmail !== user.email) {
|
||||||
|
await sendMail({
|
||||||
|
...mailOptions,
|
||||||
|
to: adminEmail,
|
||||||
|
subject: `[Admin-Kopie] Statusänderung Ihrer Anfrage ${orderNumber}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (adminMailError) {
|
||||||
|
console.error('Failed to notify admins of status update:', adminMailError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (mailError) {
|
} catch (mailError) {
|
||||||
console.error('Failed to send status update email:', mailError)
|
console.error('Failed to send status update email:', mailError)
|
||||||
@@ -545,6 +618,30 @@ export async function rejectOrder(
|
|||||||
text: statusEmail.text,
|
text: statusEmail.text,
|
||||||
html: statusEmail.html
|
html: statusEmail.html
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Notify admins
|
||||||
|
try {
|
||||||
|
const adminClient = createAdminClient()
|
||||||
|
const { data: admins } = await adminClient
|
||||||
|
.from('users')
|
||||||
|
.select('email')
|
||||||
|
.eq('role', 'admin')
|
||||||
|
if (admins) {
|
||||||
|
const adminEmails = admins.map((a: any) => a.email).filter(Boolean)
|
||||||
|
for (const adminEmail of adminEmails) {
|
||||||
|
if (adminEmail !== orderUser.email) {
|
||||||
|
await sendMail({
|
||||||
|
to: adminEmail,
|
||||||
|
subject: `[Admin-Kopie] Anfrage abgelehnt: ${orderNumber}`,
|
||||||
|
text: statusEmail.text,
|
||||||
|
html: statusEmail.html
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (adminMailError) {
|
||||||
|
console.error('Failed to notify admins of order rejection:', adminMailError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (mailError) {
|
} catch (mailError) {
|
||||||
console.error('Failed to send rejection email:', mailError)
|
console.error('Failed to send rejection email:', mailError)
|
||||||
@@ -772,6 +869,32 @@ export async function updateOrder(
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load partner details for update email
|
||||||
|
let partnerCompanyName = ''
|
||||||
|
let partnerUserName = ''
|
||||||
|
let partnerUserEmail = ''
|
||||||
|
if (order?.user_id) {
|
||||||
|
const { data: orderDbUser } = await supabase
|
||||||
|
.from('users')
|
||||||
|
.select('first_name, last_name, email, company_id')
|
||||||
|
.eq('id', order.user_id)
|
||||||
|
.single()
|
||||||
|
if (orderDbUser) {
|
||||||
|
partnerUserName = [orderDbUser.first_name, orderDbUser.last_name].filter(Boolean).join(' ')
|
||||||
|
partnerUserEmail = orderDbUser.email || ''
|
||||||
|
if (orderDbUser.company_id) {
|
||||||
|
const { data: comp } = await supabase
|
||||||
|
.from('companies')
|
||||||
|
.select('name')
|
||||||
|
.eq('id', orderDbUser.company_id)
|
||||||
|
.single()
|
||||||
|
if (comp) {
|
||||||
|
partnerCompanyName = comp.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const itemsSection = buildEmailItemsSection(items)
|
const itemsSection = buildEmailItemsSection(items)
|
||||||
|
|
||||||
const emailTemplate = getOrderEmailTemplate({
|
const emailTemplate = getOrderEmailTemplate({
|
||||||
@@ -781,7 +904,10 @@ export async function updateOrder(
|
|||||||
totalDetailsText,
|
totalDetailsText,
|
||||||
totalDetailsHtml,
|
totalDetailsHtml,
|
||||||
itemsDetailsText: itemsSection.text,
|
itemsDetailsText: itemsSection.text,
|
||||||
itemsDetailsHtml: itemsSection.html
|
itemsDetailsHtml: itemsSection.html,
|
||||||
|
partnerCompanyName,
|
||||||
|
partnerUserName,
|
||||||
|
partnerUserEmail
|
||||||
}, `${process.env.NEXT_PUBLIC_SITE_URL || 'https://staging.hephex.de'}`, true)
|
}, `${process.env.NEXT_PUBLIC_SITE_URL || 'https://staging.hephex.de'}`, true)
|
||||||
|
|
||||||
await sendMail({
|
await sendMail({
|
||||||
@@ -797,6 +923,37 @@ export async function updateOrder(
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Notify admins
|
||||||
|
try {
|
||||||
|
const adminClient = createAdminClient()
|
||||||
|
const { data: admins } = await adminClient
|
||||||
|
.from('users')
|
||||||
|
.select('email')
|
||||||
|
.eq('role', 'admin')
|
||||||
|
if (admins) {
|
||||||
|
const adminEmails = admins.map((a: any) => a.email).filter(Boolean)
|
||||||
|
for (const adminEmail of adminEmails) {
|
||||||
|
if (adminEmail !== orderUserEmail) {
|
||||||
|
await sendMail({
|
||||||
|
to: adminEmail,
|
||||||
|
subject: `[Admin-Kopie] Anfrageänderung ${order.order_number}`,
|
||||||
|
text: emailTemplate.text,
|
||||||
|
html: emailTemplate.html,
|
||||||
|
attachments: [
|
||||||
|
{
|
||||||
|
filename: `Anfragebestaetigung_${order.order_number}.pdf`,
|
||||||
|
content: buffer,
|
||||||
|
contentType: 'application/pdf',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (adminMailError) {
|
||||||
|
console.error('Failed to notify admins of order update:', adminMailError)
|
||||||
|
}
|
||||||
} catch (mailError) {
|
} catch (mailError) {
|
||||||
console.error('Failed to send order update confirmation mail:', mailError)
|
console.error('Failed to send order update confirmation mail:', mailError)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user