import ReactDOM from 'react-dom' import { Ticket, FileText, ExternalLink } from 'lucide-react' import Markdown from 'react-markdown' import remarkGfm from 'remark-gfm' import remarkBreaks from 'remark-breaks' import { useSettingsStore } from '../../store/settingsStore' import { useTripStore } from '../../store/tripStore' import { formatTime, splitReservationDateTime } from '../../utils/formatters' import { RES_ICONS, TRANSPORT_DETAIL_COLORS } from './DayPlanSidebar.constants' import type { Reservation } from '../../types' interface DayPlanSidebarTransportDetailModalProps { transportDetail: Reservation | null setTransportDetail: (v: Reservation | null) => void onNavigateToFiles?: () => void t: (key: string, params?: Record) => string locale: string timeFormat: string } export function DayPlanSidebarTransportDetailModal({ transportDetail, setTransportDetail, onNavigateToFiles, t, locale, timeFormat, }: DayPlanSidebarTransportDetailModalProps) { if (!transportDetail) return null return ReactDOM.createPortal(
setTransportDetail(null)}>
e.stopPropagation()}> {(() => { const res = transportDetail const TransportIcon = RES_ICONS[res.type] || Ticket const TRANSPORT_COLORS = TRANSPORT_DETAIL_COLORS const color = TRANSPORT_COLORS[res.type] || 'var(--text-muted)' const meta = typeof res.metadata === 'string' ? JSON.parse(res.metadata || '{}') : (res.metadata || {}) const detailFields = [] if (res.type === 'flight') { if (meta.airline) detailFields.push({ label: t('reservations.meta.airline'), value: meta.airline }) if (meta.flight_number) detailFields.push({ label: t('reservations.meta.flightNumber'), value: meta.flight_number }) if (meta.departure_airport) detailFields.push({ label: t('reservations.meta.from'), value: meta.departure_airport }) if (meta.arrival_airport) detailFields.push({ label: t('reservations.meta.to'), value: meta.arrival_airport }) if (meta.seat) detailFields.push({ label: t('reservations.meta.seat'), value: meta.seat }) } else if (res.type === 'train') { if (meta.train_number) detailFields.push({ label: t('reservations.meta.trainNumber'), value: meta.train_number }) if (meta.platform) detailFields.push({ label: t('reservations.meta.platform'), value: meta.platform }) if (meta.seat) detailFields.push({ label: t('reservations.meta.seat'), value: meta.seat }) } if (res.confirmation_number) detailFields.push({ label: t('reservations.confirmationCode'), value: res.confirmation_number, sensitive: true }) if (res.location) detailFields.push({ label: t('reservations.locationAddress'), value: res.location }) return ( <> {/* Header */}
{res.title}
{(() => { const { date, time } = splitReservationDateTime(res.reservation_time) const { time: endTime } = splitReservationDateTime(res.reservation_end_time) const dateStr = date ? new Date(date + 'T00:00:00Z').toLocaleDateString(locale, { weekday: 'short', day: 'numeric', month: 'short', timeZone: 'UTC' }) : '' const timeStr = time ? formatTime(time, locale, timeFormat) : '' const endStr = endTime ? formatTime(endTime, locale, timeFormat) : '' const parts: string[] = [] if (dateStr) parts.push(dateStr) if (timeStr) parts.push(timeStr + (endStr ? ` – ${endStr}` : '')) return parts.join(', ') })()}
{(res.status === 'confirmed' ? t('planner.resConfirmed') : t('planner.resPending')).replace(/\s*·\s*$/, '')}
{/* Detail-Felder */} {detailFields.length > 0 && (
{detailFields.map((f, i) => { const shouldBlur = f.sensitive && useSettingsStore.getState().settings.blur_booking_codes return (
{f.label}
{ if (shouldBlur) e.currentTarget.style.filter = 'none' }} onMouseLeave={e => { if (shouldBlur) e.currentTarget.style.filter = 'blur(5px)' }} onClick={e => { if (shouldBlur) { const el = e.currentTarget; el.style.filter = el.style.filter === 'none' ? 'blur(5px)' : 'none' } }} className="text-content" style={{ fontSize: 12, fontWeight: 500, wordBreak: 'break-word', filter: shouldBlur ? 'blur(5px)' : 'none', transition: 'filter 0.2s', cursor: shouldBlur ? 'pointer' : 'default', userSelect: shouldBlur ? 'none' : 'auto', }} >{f.value}
) })}
)} {/* Notizen */} {res.notes && (
{t('reservations.notes')}
{res.notes}
)} {/* Dateien */} {(() => { const resFiles = (useTripStore.getState().files || []).filter(f => !f.deleted_at && ( f.reservation_id === res.id || (f.linked_reservation_ids && f.linked_reservation_ids.includes(res.id)) ) ) if (resFiles.length === 0) return null return (
{t('files.title')}
{resFiles.map(f => (
{ setTransportDetail(null); onNavigateToFiles?.() }} className="bg-surface-tertiary" style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '6px 10px', borderRadius: 8, cursor: 'pointer', transition: 'background 0.1s', }} onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover)'} onMouseLeave={e => e.currentTarget.style.background = 'var(--bg-tertiary)'} > {f.original_name}
))}
) })()} {/* Schließen */}
) })()}
, document.body ) }