306 lines
15 KiB
TypeScript
306 lines
15 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import { FiCalendar, FiMapPin, FiUser, FiMail, FiPhone, FiEdit, FiSave, FiX, FiCamera, FiTruck, FiFileText } from 'react-icons/fi';
|
|
import Link from 'next/link';
|
|
import { formatDate, formatDateTime } from '@/lib/date-utils';
|
|
import ContractSection from './ContractSection';
|
|
|
|
interface BookingDetailProps {
|
|
booking: any;
|
|
emails: any[];
|
|
user: any;
|
|
}
|
|
|
|
export default function BookingDetail({ booking, emails, user }: BookingDetailProps) {
|
|
<div className="max-w-6xl mx-auto">
|
|
<div className="flex items-center justify-between mb-8">
|
|
<div>
|
|
<Link href="/dashboard/bookings" className="text-sm text-gray-400 hover:text-gray-300 mb-2 inline-block transition-colors">
|
|
← Zurück zu Buchungen
|
|
</Link>
|
|
<h2 className="text-3xl font-bold text-white">Buchung {booking.bookingNumber}</h2>
|
|
</div>
|
|
<div className="flex gap-3">
|
|
{!editing ? (
|
|
<button
|
|
onClick={() => setEditing(true)}
|
|
className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-gray-600 to-gray-700 text-white rounded-lg hover:from-gray-700 hover:to-gray-800 shadow-lg transition-all"
|
|
>
|
|
<FiEdit /> Bearbeiten
|
|
</button>
|
|
) : (
|
|
<>
|
|
<button
|
|
onClick={() => {
|
|
setEditing(false);
|
|
setFormData(booking);
|
|
}}
|
|
className="flex items-center gap-2 px-4 py-2 bg-gray-700 text-gray-300 rounded-lg hover:bg-gray-600 transition-colors"
|
|
>
|
|
<FiX /> Abbrechen
|
|
</button>
|
|
<button
|
|
onClick={handleSave}
|
|
disabled={saving}
|
|
className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-red-600 to-pink-600 text-white rounded-lg hover:from-red-700 hover:to-pink-700 disabled:opacity-50 shadow-lg transition-all"
|
|
>
|
|
<FiSave /> {saving ? 'Speichern...' : 'Speichern'}
|
|
</button>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
<div className="lg:col-span-2 space-y-6">
|
|
<div className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl shadow-sm p-6">
|
|
<div className="flex items-center justify-between mb-6">
|
|
<h3 className="text-xl font-bold text-white">Status</h3>
|
|
<div className={`px-4 py-2 border-2 rounded-lg font-semibold ${getStatusColor(booking.status)}`}>
|
|
{getStatusLabel(booking.status)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex gap-2">
|
|
{booking.status === 'RESERVED' && (
|
|
<button
|
|
onClick={() => handleStatusChange('CONFIRMED')}
|
|
className="flex-1 px-4 py-2 bg-gradient-to-r from-green-600 to-emerald-600 text-white rounded-lg hover:from-green-700 hover:to-emerald-700 shadow-lg transition-all"
|
|
>
|
|
→ Bestätigen
|
|
</button>
|
|
)}
|
|
{booking.status === 'CONFIRMED' && (
|
|
<button
|
|
onClick={() => handleStatusChange('COMPLETED')}
|
|
className="flex-1 px-4 py-2 bg-gradient-to-r from-blue-600 to-cyan-600 text-white rounded-lg hover:from-blue-700 hover:to-cyan-700 shadow-lg transition-all"
|
|
>
|
|
→ Abschließen
|
|
</button>
|
|
)}
|
|
{['RESERVED', 'CONFIRMED'].includes(booking.status) && (
|
|
<button
|
|
onClick={() => handleStatusChange('CANCELLED')}
|
|
className="px-4 py-2 bg-gradient-to-r from-red-600 to-pink-600 text-white rounded-lg hover:from-red-700 hover:to-pink-700 shadow-lg transition-all"
|
|
>
|
|
Stornieren
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl shadow-sm p-6">
|
|
<h3 className="text-xl font-bold text-white mb-4 flex items-center gap-2">
|
|
<FiUser /> Kundendaten
|
|
</h3>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-300 mb-1">Name</label>
|
|
{editing ? (
|
|
<input
|
|
type="text"
|
|
value={formData.customerName}
|
|
onChange={(e) => setFormData({ ...formData, customerName: e.target.value })}
|
|
className="w-full px-3 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500"
|
|
/>
|
|
) : (
|
|
<p className="text-white">{booking.customerName}</p>
|
|
)}
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-300 mb-1">E-Mail</label>
|
|
{editing ? (
|
|
<input
|
|
type="email"
|
|
value={formData.customerEmail}
|
|
onChange={(e) => setFormData({ ...formData, customerEmail: e.target.value })}
|
|
className="w-full px-3 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500"
|
|
/>
|
|
) : (
|
|
<p className="text-white">{booking.customerEmail}</p>
|
|
)}
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-300 mb-1">Telefon</label>
|
|
{editing ? (
|
|
<input
|
|
type="tel"
|
|
value={formData.customerPhone}
|
|
onChange={(e) => setFormData({ ...formData, customerPhone: e.target.value })}
|
|
className="w-full px-3 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500"
|
|
/>
|
|
) : (
|
|
<p className="text-white">{booking.customerPhone}</p>
|
|
)}
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-300 mb-1">Rechnungsart</label>
|
|
<p className="text-white">{booking.invoiceType === 'BUSINESS' ? 'Geschäftlich' : 'Privat'}</p>
|
|
</div>
|
|
|
|
{booking.companyName && (
|
|
<div className="col-span-2">
|
|
<label className="block text-sm font-medium text-gray-300 mb-1">Firma</label>
|
|
<p className="text-white">{booking.companyName}</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl shadow-sm p-6">
|
|
<h3 className="text-xl font-bold text-white mb-4 flex items-center gap-2">
|
|
<FiCalendar /> Event-Details
|
|
</h3>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-300 mb-1">Datum</label>
|
|
<p className="text-white">{formatDate(booking.eventDate)}</p>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-300 mb-1">Standort</label>
|
|
<p className="text-white">{booking.location.name}</p>
|
|
</div>
|
|
|
|
<div className="col-span-2">
|
|
<label className="block text-sm font-medium text-gray-300 mb-1">Event-Adresse</label>
|
|
<p className="text-white">{booking.eventAddress}</p>
|
|
<p className="text-gray-400 text-sm">{booking.eventZip} {booking.eventCity}</p>
|
|
</div>
|
|
|
|
{booking.eventLocation && (
|
|
<div className="col-span-2">
|
|
<label className="block text-sm font-medium text-gray-300 mb-1">Location-Name</label>
|
|
<p className="text-white">{booking.eventLocation}</p>
|
|
</div>
|
|
)}
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-300 mb-1">Aufbau ab</label>
|
|
<p className="text-white">{formatDateTime(booking.setupTimeStart)}</p>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-300 mb-1">Aufbau spätestens</label>
|
|
<p className="text-white">{formatDateTime(booking.setupTimeLatest)}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{booking.notes && (
|
|
<div className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl shadow-sm p-6">
|
|
<h3 className="text-xl font-bold text-white mb-4 flex items-center gap-2">
|
|
<FiFileText /> Notizen
|
|
</h3>
|
|
<p className="text-gray-300 whitespace-pre-wrap">{booking.notes}</p>
|
|
</div>
|
|
)}
|
|
|
|
{emails.length > 0 && (
|
|
<div className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl shadow-sm p-6">
|
|
<h3 className="text-xl font-bold text-white mb-4 flex items-center gap-2">
|
|
<FiMail /> E-Mail-Verlauf
|
|
</h3>
|
|
<div className="space-y-4">
|
|
{emails.map((email) => (
|
|
<div key={email.id} className="border border-gray-600 bg-gray-700/50 rounded-lg p-4">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<span className="font-semibold text-white">{email.subject}</span>
|
|
<span className="text-sm text-gray-400">
|
|
{formatDate(email.receivedAt)}
|
|
</span>
|
|
</div>
|
|
<p className="text-sm text-gray-400 mb-2">Von: {email.from}</p>
|
|
{email.parsed && (
|
|
<span className="inline-block px-2 py-1 bg-green-500/20 text-green-400 border border-green-500/50 text-xs rounded">
|
|
Automatisch verarbeitet
|
|
</span>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="space-y-6">
|
|
<div className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl shadow-sm p-6">
|
|
<h3 className="text-xl font-bold text-white mb-4 flex items-center gap-2">
|
|
<FiCamera /> Fotobox
|
|
</h3>
|
|
{booking.photobox ? (
|
|
<div>
|
|
<p className="text-gray-400 text-sm mb-2">Modell</p>
|
|
<p className="text-white font-semibold mb-4">{booking.photobox.model}</p>
|
|
<p className="text-gray-400 text-sm mb-2">Seriennummer</p>
|
|
<p className="text-white">{booking.photobox.serialNumber}</p>
|
|
</div>
|
|
) : (
|
|
<p className="text-gray-400">Noch nicht zugewiesen</p>
|
|
)}
|
|
</div>
|
|
|
|
<ContractSection booking={booking} onRefresh={() => router.refresh()} />
|
|
|
|
<div className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl shadow-sm p-6">
|
|
<h3 className="text-xl font-bold text-white mb-4">Preis</h3>
|
|
<div className="text-center">
|
|
<p className="text-4xl font-bold text-white">
|
|
{booking.calculatedPrice || 0}€
|
|
</p>
|
|
{booking.distance && (
|
|
<p className="text-sm text-gray-400 mt-2">
|
|
+ {booking.distance} km Anfahrt
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl shadow-sm p-6">
|
|
<h3 className="text-xl font-bold text-white mb-4">Vertrag</h3>
|
|
<div className="space-y-2">
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-gray-300">Unterschrieben</span>
|
|
<span className={`font-semibold ${booking.contractSigned ? 'text-green-400' : 'text-red-400'}`}>
|
|
{booking.contractSigned ? 'Ja' : 'Nein'}
|
|
</span>
|
|
</div>
|
|
{booking.contractSignedAt && (
|
|
<p className="text-sm text-gray-400">
|
|
am {formatDate(booking.contractSignedAt)}
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{booking.tours && booking.tours.length > 0 && (
|
|
<div className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl shadow-sm p-6">
|
|
<h3 className="text-xl font-bold text-white mb-4 flex items-center gap-2">
|
|
<FiTruck /> Tour
|
|
</h3>
|
|
{booking.tours.map((tour: any) => (
|
|
<div key={tour.id}>
|
|
{tour.driver ? (
|
|
<p className="text-white">Fahrer: {tour.driver.name}</p>
|
|
) : (
|
|
<p className="text-gray-400">Noch kein Fahrer zugewiesen</p>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|