"use client"; import { useState, useEffect } from "react"; import { useSession } from "next-auth/react"; import { useRouter } from "next/navigation"; interface Device { id: string; name: string; color: string; } interface PreviewRow { datum: string; uhrzeit: string; latitude: string; longitude: string; adresse: string; distanz: string; geschwindigkeit: string; geraet: string; } export default function ExportPage() { const { data: session, status } = useSession(); const router = useRouter(); const [devices, setDevices] = useState([]); const [selectedDevice, setSelectedDevice] = useState("all"); const [startTime, setStartTime] = useState(""); const [endTime, setEndTime] = useState(""); const [previewData, setPreviewData] = useState([]); const [totalPoints, setTotalPoints] = useState(0); const [loading, setLoading] = useState(false); const [exporting, setExporting] = useState(false); const [exportProgress, setExportProgress] = useState(""); // Redirect if not authenticated useEffect(() => { if (status === "unauthenticated") { router.push("/login"); } }, [status, router]); // Fetch devices useEffect(() => { const fetchDevices = async () => { try { const response = await fetch("/api/devices/public"); if (response.ok) { const data = await response.json(); setDevices(data.devices || []); } } catch (err) { console.error("Failed to fetch devices:", err); } }; if (status === "authenticated") { fetchDevices(); } }, [status]); // Set default time range (last 7 days) useEffect(() => { const now = new Date(); const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); // Format for datetime-local input (YYYY-MM-DDTHH:mm) setEndTime(now.toISOString().slice(0, 16)); setStartTime(sevenDaysAgo.toISOString().slice(0, 16)); }, []); // Generate preview const handlePreview = async () => { setLoading(true); setPreviewData([]); setTotalPoints(0); try { const params = new URLSearchParams(); if (selectedDevice !== "all") { params.set("username", selectedDevice); } if (startTime) { params.set("startTime", new Date(startTime).toISOString()); } if (endTime) { params.set("endTime", new Date(endTime).toISOString()); } params.set("limit", "50"); // Preview only first 50 const response = await fetch(`/api/locations?${params.toString()}`); if (!response.ok) throw new Error("Failed to fetch preview"); const data = await response.json(); const locations = data.history || []; setTotalPoints(locations.length); // Sort chronologically locations.sort((a: any, b: any) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime() ); // Build preview rows (without geocoding) const rows: PreviewRow[] = []; for (let i = 0; i < Math.min(locations.length, 50); i++) { const loc = locations[i]; const lat = Number(loc.latitude); const lon = Number(loc.longitude); // Calculate distance let distance = 0; if (i > 0) { const prevLoc = locations[i - 1]; distance = calculateDistance( Number(prevLoc.latitude), Number(prevLoc.longitude), lat, lon ); } const date = new Date(loc.timestamp); rows.push({ datum: date.toLocaleDateString('de-DE'), uhrzeit: date.toLocaleTimeString('de-DE'), latitude: lat.toFixed(6), longitude: lon.toFixed(6), adresse: `${lat.toFixed(6)}, ${lon.toFixed(6)}`, distanz: distance.toFixed(3), geschwindigkeit: loc.speed != null ? Number(loc.speed).toFixed(1) : '-', geraet: loc.username || 'Unbekannt', }); } setPreviewData(rows); } catch (error) { console.error("Preview error:", error); alert("Fehler beim Laden der Vorschau"); } finally { setLoading(false); } }; // Export CSV with geocoding const handleExport = async () => { if (previewData.length === 0) { alert("Bitte zuerst Vorschau laden"); return; } const confirmed = confirm( `${totalPoints} GPS-Punkte werden exportiert.\n\n` + `ACHTUNG: Adressauflösung (Geocoding) kann bei vielen Punkten sehr lange dauern (ca. 1 Punkt pro Sekunde).\n\n` + `Geschätzte Dauer: ca. ${Math.ceil(totalPoints / 60)} Minuten.\n\n` + `Möchten Sie fortfahren?` ); if (!confirmed) return; setExporting(true); setExportProgress(`Starte Export von ${totalPoints} Punkten...`); try { const params = new URLSearchParams(); if (selectedDevice !== "all") { params.set("username", selectedDevice); } if (startTime) { params.set("startTime", new Date(startTime).toISOString()); } if (endTime) { params.set("endTime", new Date(endTime).toISOString()); } params.set("includeGeocoding", "true"); setExportProgress("Lade Daten und löse Adressen auf..."); const response = await fetch(`/api/export/csv?${params.toString()}`); if (!response.ok) throw new Error("Export failed"); const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `fahrtenbuch_${startTime ? new Date(startTime).toISOString().split('T')[0] : 'alle'}_${endTime ? new Date(endTime).toISOString().split('T')[0] : 'alle'}.csv`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); setExportProgress("Export erfolgreich abgeschlossen!"); setTimeout(() => setExportProgress(""), 3000); } catch (error) { console.error("Export error:", error); alert("Fehler beim Exportieren"); setExportProgress(""); } finally { setExporting(false); } }; // Export CSV without geocoding (faster) const handleQuickExport = async () => { if (previewData.length === 0) { alert("Bitte zuerst Vorschau laden"); return; } setExporting(true); setExportProgress("Exportiere ohne Adressauflösung..."); try { const params = new URLSearchParams(); if (selectedDevice !== "all") { params.set("username", selectedDevice); } if (startTime) { params.set("startTime", new Date(startTime).toISOString()); } if (endTime) { params.set("endTime", new Date(endTime).toISOString()); } params.set("includeGeocoding", "false"); const response = await fetch(`/api/export/csv?${params.toString()}`); if (!response.ok) throw new Error("Export failed"); const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `fahrtenbuch_schnell_${startTime ? new Date(startTime).toISOString().split('T')[0] : 'alle'}_${endTime ? new Date(endTime).toISOString().split('T')[0] : 'alle'}.csv`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); setExportProgress("Export erfolgreich abgeschlossen!"); setTimeout(() => setExportProgress(""), 3000); } catch (error) { console.error("Export error:", error); alert("Fehler beim Exportieren"); setExportProgress(""); } finally { setExporting(false); } }; if (status === "loading") { return (

Laden...

); } return (

CSV-Export für Fahrtenbuch

Exportieren Sie Ihre GPS-Tracking-Daten für Lexware oder andere Fahrtenbuch-Software

{/* Filters */}
setStartTime(e.target.value)} className="w-full border border-gray-300 rounded px-3 py-2" />
setEndTime(e.target.value)} className="w-full border border-gray-300 rounded px-3 py-2" />
{/* Action Buttons */}
{/* Progress */} {exportProgress && (

{exportProgress}

)} {/* Info */} {totalPoints > 0 && (

Gefunden: {totalPoints} GPS-Punkte im gewählten Zeitraum {totalPoints > 50 && " (Vorschau zeigt erste 50)"}

Hinweis: Der vollständige Export mit Adressauflösung dauert ca. {Math.ceil(totalPoints / 60)} Minuten. Für schnelle Exporte nutzen Sie den Schnell-Export.

)}
{/* Preview Table */} {previewData.length > 0 && (

Vorschau (erste 50 Zeilen)

{previewData.map((row, idx) => ( ))}
Datum Uhrzeit Latitude Longitude Adresse Distanz (km) Geschw. (km/h) Gerät
{row.datum} {row.uhrzeit} {row.latitude} {row.longitude} {row.adresse} {row.distanz} {row.geschwindigkeit} {row.geraet}
)}
); } // Haversine distance calculation (client-side for preview) function calculateDistance(lat1: number, lon1: number, lat2: number, lon2: number): number { const R = 6371; const dLat = toRadians(lat2 - lat1); const dLon = toRadians(lon2 - lon1); const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; } function toRadians(degrees: number): number { return degrees * (Math.PI / 180); }