import React, { useState, useCallback } from 'react' import { Plus, Search, ChevronUp, ChevronDown, X, Map, ExternalLink, Navigation, RotateCcw, Clock, Euro, FileText, Package } from 'lucide-react' import { calculateRoute, generateGoogleMapsUrl, optimizeRoute } from '../Map/RouteCalculator' import PackingListPanel from '../Packing/PackingListPanel' import { ReservationModal } from './ReservationModal' import { PlaceDetailPanel } from './PlaceDetailPanel' import { useTripStore } from '../../store/tripStore' import { useToast } from '../shared/Toast' const TABS = [ { id: 'orte', label: 'Orte', icon: '📍' }, { id: 'tagesplan', label: 'Tagesplan', icon: '📅' }, { id: 'reservierungen', label: 'Reservierungen', icon: '🎫' }, { id: 'packliste', label: 'Packliste', icon: '🎒' }, ] const TRANSPORT_MODES = [ { value: 'driving', label: 'Auto', icon: '🚗' }, { value: 'walking', label: 'Fuß', icon: '🚶' }, { value: 'cycling', label: 'Rad', icon: '🚲' }, ] export function RightPanel({ trip, days, places, categories, tags, assignments, reservations, packingItems, selectedDay, selectedDayId, selectedPlaceId, onPlaceClick, onPlaceEdit, onPlaceDelete, onAssignToDay, onRemoveAssignment, onReorder, onAddPlace, onEditTrip, onRouteCalculated, tripId, }) { const [activeTab, setActiveTab] = useState('orte') const [search, setSearch] = useState('') const [categoryFilter, setCategoryFilter] = useState('') const [transportMode, setTransportMode] = useState('driving') const [isCalculatingRoute, setIsCalculatingRoute] = useState(false) const [showReservationModal, setShowReservationModal] = useState(false) const [editingReservation, setEditingReservation] = useState(null) const [routeInfo, setRouteInfo] = useState(null) const tripStore = useTripStore() const toast = useToast() // Filtered places for Orte tab const filteredPlaces = places.filter(p => { const matchesSearch = !search || p.name.toLowerCase().includes(search.toLowerCase()) || (p.address || '').toLowerCase().includes(search.toLowerCase()) const matchesCategory = !categoryFilter || String(p.category_id) === String(categoryFilter) return matchesSearch && matchesCategory }) // Ordered assignments for selected day const dayAssignments = selectedDayId ? (assignments[String(selectedDayId)] || []).slice().sort((a, b) => a.order_index - b.order_index) : [] const isAssignedToSelectedDay = (placeId) => selectedDayId && dayAssignments.some(a => a.place?.id === placeId) // Calculate schedule with times const getSchedule = () => { if (!dayAssignments.length) return [] let currentTime = null return dayAssignments.map((assignment, idx) => { const place = assignment.place const startTime = place?.place_time || (currentTime ? currentTime : null) const duration = place?.duration_minutes || 60 if (startTime) { const [h, m] = startTime.split(':').map(Number) const endMinutes = h * 60 + m + duration const endH = Math.floor(endMinutes / 60) % 24 const endM = endMinutes % 60 currentTime = `${String(endH).padStart(2, '0')}:${String(endM).padStart(2, '0')}` } return { assignment, startTime, endTime: currentTime } }) } const handleCalculateRoute = async () => { if (!selectedDayId) return const waypoints = dayAssignments .map(a => a.place) .filter(p => p?.lat && p?.lng) .map(p => ({ lat: p.lat, lng: p.lng })) if (waypoints.length < 2) { toast.error('Mindestens 2 Orte mit Koordinaten benötigt') return } setIsCalculatingRoute(true) try { const result = await calculateRoute(waypoints, transportMode) if (result) { setRouteInfo({ distance: result.distanceText, duration: result.durationText }) onRouteCalculated?.(result) toast.success('Route berechnet') } else { toast.error('Route konnte nicht berechnet werden') } } catch (err) { toast.error('Fehler bei der Routenberechnung') } finally { setIsCalculatingRoute(false) } } const handleOptimizeRoute = async () => { if (!selectedDayId || dayAssignments.length < 3) return const places = dayAssignments.map(a => a.place).filter(p => p?.lat && p?.lng) const optimized = optimizeRoute(places) const optimizedIds = optimized.map(p => { const a = dayAssignments.find(a => a.place?.id === p.id) return a?.id }).filter(Boolean) await onReorder(selectedDayId, optimizedIds) toast.success('Route optimiert') } const handleOpenGoogleMaps = () => { const places = dayAssignments.map(a => a.place).filter(p => p?.lat && p?.lng) const url = generateGoogleMapsUrl(places) if (url) window.open(url, '_blank') else toast.error('Keine Orte mit Koordinaten vorhanden') } const handleMoveUp = async (idx) => { if (idx === 0) return const ids = dayAssignments.map(a => a.id) ;[ids[idx - 1], ids[idx]] = [ids[idx], ids[idx - 1]] await onReorder(selectedDayId, ids) } const handleMoveDown = async (idx) => { if (idx === dayAssignments.length - 1) return const ids = dayAssignments.map(a => a.id) ;[ids[idx], ids[idx + 1]] = [ids[idx + 1], ids[idx]] await onReorder(selectedDayId, ids) } const handleAddReservation = () => { setEditingReservation(null) setShowReservationModal(true) } const handleSaveReservation = async (data) => { try { if (editingReservation) { await tripStore.updateReservation(tripId, editingReservation.id, data) toast.success('Reservierung aktualisiert') } else { await tripStore.addReservation(tripId, { ...data, day_id: selectedDayId || null }) toast.success('Reservierung hinzugefügt') } setShowReservationModal(false) } catch (err) { toast.error(err.message) } } const handleDeleteReservation = async (id) => { if (!confirm('Reservierung löschen?')) return try { await tripStore.deleteReservation(tripId, id) toast.success('Reservierung gelöscht') } catch (err) { toast.error(err.message) } } // Reservations for selected day (or all if no day selected) const filteredReservations = selectedDayId ? reservations.filter(r => String(r.day_id) === String(selectedDayId) || !r.day_id) : reservations const selectedPlace = selectedPlaceId ? places.find(p => p.id === selectedPlaceId) : null return (
Keine Orte gefunden
{place.address}
)}Wähle einen Tag aus der linken Liste um den Tagesplan zu sehen
{dayAssignments.length} Ort{dayAssignments.length !== 1 ? 'e' : ''} {dayAssignments.length > 0 && ` · ${dayAssignments.reduce((s, a) => s + (a.place?.duration_minutes || 60), 0)} Min. gesamt`}
Noch keine Orte für diesen Tag
{place.address}
)} {assignment.notes && ({assignment.notes}
)}Keine Reservierungen
{reservation.notes}
)}