Initial commit: Lageplan v1.0 - Next.js 15.5, React 19

This commit is contained in:
Pepe Ziberi
2026-02-21 11:57:44 +01:00
commit adf3dc8c1d
167 changed files with 34265 additions and 0 deletions

184
src/lib/export.ts Normal file
View File

@@ -0,0 +1,184 @@
import html2canvas from 'html2canvas'
import jsPDF from 'jspdf'
import type { Project, DrawFeature } from '@/app/app/page'
import { formatDateTime } from './utils'
export interface ExportOptions {
includeTitle?: boolean
includeLegend?: boolean
includeTimestamp?: boolean
author?: string
}
export async function exportToPNG(
mapElement: HTMLElement,
project: Project,
options: ExportOptions = {}
): Promise<void> {
try {
const canvas = await html2canvas(mapElement, {
useCORS: true,
allowTaint: true,
scale: 2,
logging: false,
backgroundColor: '#ffffff',
})
const link = document.createElement('a')
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
link.download = `Lageplan_${project.title.replace(/[^a-z0-9äöüß]/gi, '_')}_${timestamp}.png`
link.href = canvas.toDataURL('image/png', 1.0)
link.click()
} catch (error) {
console.error('PNG export failed:', error)
throw new Error('PNG Export fehlgeschlagen')
}
}
export interface LegendEntry {
name: string
count: number
iconUrl?: string
unNummer?: string
gefahrennummer?: string
}
export async function exportToPDF(
mapElement: HTMLElement,
project: Project,
features: DrawFeature[],
legendEntries: LegendEntry[] = [],
options: ExportOptions = {}
): Promise<void> {
try {
const canvas = await html2canvas(mapElement, {
useCORS: true,
allowTaint: true,
scale: 2,
logging: false,
backgroundColor: '#ffffff',
})
const imgWidth = 190
const imgHeight = (canvas.height * imgWidth) / canvas.width
const pdf = new jsPDF('p', 'mm', 'a4')
// Title Block
pdf.setFillColor(220, 38, 38) // Red header
pdf.rect(0, 0, 210, 8, 'F')
pdf.setTextColor(255, 255, 255)
pdf.setFontSize(12)
pdf.setFont('helvetica', 'bold')
pdf.text('LAGEPLAN - FEUERWEHR', 10, 5.5)
pdf.setTextColor(0, 0, 0)
// Project info box
pdf.setFillColor(245, 245, 245)
pdf.rect(10, 12, 190, 28, 'F')
pdf.setDrawColor(200, 200, 200)
pdf.rect(10, 12, 190, 28, 'S')
pdf.setFontSize(14)
pdf.setFont('helvetica', 'bold')
pdf.text(project.title, 15, 22)
pdf.setFontSize(10)
pdf.setFont('helvetica', 'normal')
let infoY = 28
if (project.location) {
pdf.text(`Einsatzort: ${project.location}`, 15, infoY)
infoY += 5
}
const now = new Date()
pdf.text(`Datum: ${now.toLocaleDateString('de-DE')}`, 15, infoY)
pdf.text(`Uhrzeit: ${now.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })}`, 80, infoY)
if (options.author) {
pdf.text(`Ersteller: ${options.author}`, 130, infoY)
}
// Map image
const mapY = 45
const maxMapHeight = legendEntries.length > 0 ? 180 : 220
const actualMapHeight = Math.min(imgHeight, maxMapHeight)
pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 10, mapY, imgWidth, actualMapHeight)
pdf.setDrawColor(100, 100, 100)
pdf.rect(10, mapY, imgWidth, actualMapHeight, 'S')
// Legend
if (legendEntries.length > 0) {
const legendY = mapY + actualMapHeight + 8
pdf.setFillColor(245, 245, 245)
pdf.rect(10, legendY, 190, 6, 'F')
pdf.setFontSize(10)
pdf.setFont('helvetica', 'bold')
pdf.text('Legende', 15, legendY + 4.5)
pdf.setFontSize(8)
pdf.setFont('helvetica', 'normal')
let y = legendY + 12
const colWidth = 63
let col = 0
legendEntries.forEach((entry, i) => {
if (y < 280) {
const x = 15 + (col * colWidth)
let text = `${entry.name}`
if (entry.count > 1) text += ` (${entry.count})`
if (entry.unNummer) text += ` [UN ${entry.unNummer}]`
pdf.text(text, x, y)
col++
if (col >= 3) {
col = 0
y += 5
}
}
})
}
// Footer
pdf.setFontSize(7)
pdf.setTextColor(128, 128, 128)
pdf.text(
`Erstellt mit Lageplan-App | ${now.toLocaleDateString('de-DE')} ${now.toLocaleTimeString('de-DE')}`,
10,
292
)
pdf.text('Seite 1 von 1', 180, 292)
const timestamp = now.toISOString().replace(/[:.]/g, '-').slice(0, 19)
pdf.save(`Lageplan_${project.title.replace(/[^a-z0-9äöüß]/gi, '_')}_${timestamp}.pdf`)
} catch (error) {
console.error('PDF export failed:', error)
throw new Error('PDF Export fehlgeschlagen')
}
}
export async function exportToGeoJSON(projectId: string): Promise<void> {
try {
const res = await fetch(`/api/projects/${projectId}/export?format=geojson`)
if (!res.ok) throw new Error('Export fehlgeschlagen')
const blob = await res.blob()
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `project_${projectId}.geojson`
link.click()
URL.revokeObjectURL(url)
} catch (error) {
console.error('GeoJSON export failed:', error)
throw new Error('GeoJSON Export fehlgeschlagen')
}
}