Files
Lageplan/src/app/api/projects/[id]/journal/send-report/route.ts

169 lines
8.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/db'
import { getSession } from '@/lib/auth'
import { getProjectWithTenantCheck } from '@/lib/tenant'
import { sendEmail } from '@/lib/email'
export async function POST(req: NextRequest, { params }: { params: { id: string } }) {
try {
const user = await getSession()
if (!user) return NextResponse.json({ error: 'Nicht autorisiert' }, { status: 401 })
const project = await getProjectWithTenantCheck(params.id, user)
if (!project) return NextResponse.json({ error: 'Projekt nicht gefunden' }, { status: 404 })
// Load tenant logo
let tenantLogoUrl = ''
let tenantName = ''
if ((project as any).tenantId) {
const tenant = await (prisma as any).tenant.findUnique({
where: { id: (project as any).tenantId },
select: { logoUrl: true, name: true },
})
if (tenant?.logoUrl) tenantLogoUrl = tenant.logoUrl
if (tenant?.name) tenantName = tenant.name
}
const body = await req.json()
const { recipientEmail } = body
if (!recipientEmail) {
return NextResponse.json({ error: 'Empfänger-E-Mail erforderlich' }, { status: 400 })
}
// Load journal data
const entries = await (prisma as any).journalEntry.findMany({
where: { projectId: params.id },
orderBy: [{ time: 'asc' }, { sortOrder: 'asc' }],
})
const checkItems = await (prisma as any).journalCheckItem.findMany({
where: { projectId: params.id },
orderBy: { sortOrder: 'asc' },
})
const pendenzen = await (prisma as any).journalPendenz.findMany({
where: { projectId: params.id },
orderBy: { sortOrder: 'asc' },
})
// Build HTML report
const formatTime = (d: Date) => new Date(d).toLocaleTimeString('de-CH', { hour: '2-digit', minute: '2-digit' })
const formatDate = (d: Date) => new Date(d).toLocaleDateString('de-CH', { day: '2-digit', month: '2-digit', year: 'numeric' })
const p = project as any
let entriesHtml = ''
for (const e of entries) {
const correctedStyle = e.isCorrected ? 'text-decoration:line-through;opacity:0.5;' : ''
const correctionStyle = e.correctionOfId ? 'color:#b45309;font-style:italic;' : ''
const doneIcon = e.done ? '✅' : ''
const doneAtStr = e.done && e.doneAt ? ` <span style="color:#16a34a;font-size:10px;">(erledigt ${formatTime(e.doneAt)})</span>` : ''
entriesHtml += `
<tr style="${correctedStyle}${correctionStyle}">
<td style="padding:4px 8px;border-bottom:1px solid #e5e7eb;font-family:monospace;font-size:12px;white-space:nowrap;">${formatTime(e.time)}</td>
<td style="padding:4px 8px;border-bottom:1px solid #e5e7eb;font-size:13px;">${e.what}${e.isCorrected ? ' <span style="color:#ef4444;font-size:11px;">(korrigiert)</span>' : ''}${doneAtStr}</td>
<td style="padding:4px 8px;border-bottom:1px solid #e5e7eb;font-size:12px;color:#666;">${e.who || ''}</td>
<td style="padding:4px 8px;border-bottom:1px solid #e5e7eb;text-align:center;">${doneIcon}</td>
</tr>`
}
let checkHtml = ''
for (const c of checkItems) {
const confirmedTime = c.confirmed && c.confirmedAt ? ` <span style="font-size:10px;color:#666;">${formatTime(c.confirmedAt)}</span>` : ''
checkHtml += `
<tr>
<td style="padding:3px 8px;border-bottom:1px solid #e5e7eb;font-size:13px;">${c.label}${confirmedTime}</td>
<td style="padding:3px 8px;border-bottom:1px solid #e5e7eb;text-align:center;">${c.confirmed ? '✅' : ''}</td>
<td style="padding:3px 8px;border-bottom:1px solid #e5e7eb;text-align:center;">${c.ok ? '✅' : ''}</td>
</tr>`
}
let pendHtml = ''
for (const p of pendenzen) {
const pendDoneAt = p.done && p.doneAt ? ` <span style="color:#16a34a;font-size:10px;">(${formatTime(p.doneAt)})</span>` : ''
pendHtml += `
<tr>
<td style="padding:3px 8px;border-bottom:1px solid #e5e7eb;font-size:13px;">${p.what}${pendDoneAt}</td>
<td style="padding:3px 8px;border-bottom:1px solid #e5e7eb;font-size:12px;color:#666;">${p.who || ''}</td>
<td style="padding:3px 8px;border-bottom:1px solid #e5e7eb;font-size:12px;color:#666;">${p.whenHow || ''}</td>
<td style="padding:3px 8px;border-bottom:1px solid #e5e7eb;text-align:center;">${p.done ? '✅' : ''}</td>
</tr>`
}
const logoHtml = tenantLogoUrl
? `<img src="${tenantLogoUrl}" alt="${tenantName}" style="height:40px;max-width:120px;object-fit:contain;margin-right:16px;border-radius:4px;" />`
: ''
const html = `
<div style="font-family:sans-serif;max-width:800px;margin:0 auto;">
<div style="background:#dc2626;color:white;padding:20px 24px;border-radius:12px 12px 0 0;display:flex;align-items:center;">
${logoHtml}
<div>
<h1 style="margin:0;font-size:22px;">Einsatzrapport</h1>
<p style="margin:4px 0 0;opacity:0.9;">${p.title || 'Ohne Titel'}${tenantName ? `${tenantName}` : ''}</p>
</div>
</div>
<div style="border:1px solid #e5e7eb;border-top:none;padding:24px;border-radius:0 0 12px 12px;">
<div style="display:flex;gap:24px;margin-bottom:20px;flex-wrap:wrap;">
<div><strong>Standort:</strong> ${p.location || ''}</div>
<div><strong>Datum:</strong> ${p.createdAt ? formatDate(p.createdAt) : ''}</div>
</div>
<h2 style="font-size:16px;border-bottom:2px solid #dc2626;padding-bottom:4px;margin:20px 0 8px;">Journal-Einträge</h2>
<table style="width:100%;border-collapse:collapse;">
<thead>
<tr style="background:#f5f5f4;">
<th style="padding:6px 8px;text-align:left;font-size:11px;color:#666;border-bottom:2px solid #dc2626;">Zeit</th>
<th style="padding:6px 8px;text-align:left;font-size:11px;color:#666;border-bottom:2px solid #dc2626;">Was</th>
<th style="padding:6px 8px;text-align:left;font-size:11px;color:#666;border-bottom:2px solid #dc2626;">Wer</th>
<th style="padding:6px 8px;text-align:center;font-size:11px;color:#666;border-bottom:2px solid #dc2626;">Ok</th>
</tr>
</thead>
<tbody>${entriesHtml || '<tr><td colspan="4" style="padding:12px;text-align:center;color:#999;">Keine Einträge</td></tr>'}</tbody>
</table>
${checkItems.length > 0 ? `
<h2 style="font-size:16px;border-bottom:2px solid #dc2626;padding-bottom:4px;margin:20px 0 8px;">SOMA Checkliste</h2>
<table style="width:100%;border-collapse:collapse;">
<thead>
<tr style="background:#f5f5f4;">
<th style="padding:6px 8px;text-align:left;font-size:11px;color:#666;">Punkt</th>
<th style="padding:6px 8px;text-align:center;font-size:11px;color:#666;">Bestätigt</th>
<th style="padding:6px 8px;text-align:center;font-size:11px;color:#666;">Ok</th>
</tr>
</thead>
<tbody>${checkHtml}</tbody>
</table>` : ''}
${pendenzen.length > 0 ? `
<h2 style="font-size:16px;border-bottom:2px solid #dc2626;padding-bottom:4px;margin:20px 0 8px;">Pendenzen</h2>
<table style="width:100%;border-collapse:collapse;">
<thead>
<tr style="background:#f5f5f4;">
<th style="padding:6px 8px;text-align:left;font-size:11px;color:#666;">Was</th>
<th style="padding:6px 8px;text-align:left;font-size:11px;color:#666;">Wer</th>
<th style="padding:6px 8px;text-align:left;font-size:11px;color:#666;">Wann/Wie</th>
<th style="padding:6px 8px;text-align:center;font-size:11px;color:#666;">Erledigt</th>
</tr>
</thead>
<tbody>${pendHtml}</tbody>
</table>` : ''}
<hr style="margin:20px 0;border:none;border-top:1px solid #e5e7eb;" />
<p style="color:#999;font-size:11px;">Gesendet von Lageplan am ${new Date().toLocaleString('de-CH')} durch ${user.name || user.email}</p>
</div>
</div>`
await sendEmail(
recipientEmail,
`Einsatzrapport — ${p.title || 'Ohne Titel'}`,
html
)
return NextResponse.json({ success: true, message: `Rapport an ${recipientEmail} gesendet` })
} catch (error) {
console.error('Error sending report:', error)
return NextResponse.json({ error: 'Fehler beim Senden des Rapports' }, { status: 500 })
}
}