- Vintage Modell hinzugefuegt - Equipment Multi-Select (Neue Buchung + Bearbeitung) - Kundenadresse in Formularen - Bearbeiten-Seite fuer Buchungen - Abbau-Zeiten in Formularen und Uebersicht - Vertrag PDF nur bei Privatkunden - LexOffice Kontakt-Erstellung Fix (BUSINESS) - Zurueck-Pfeil auf Touren-Seite
252 lines
11 KiB
TypeScript
252 lines
11 KiB
TypeScript
import { getServerSession } from 'next-auth';
|
|
import { authOptions } from '@/lib/auth';
|
|
import { prisma } from '@/lib/prisma';
|
|
import { redirect } from 'next/navigation';
|
|
import DashboardSidebar from '@/components/DashboardSidebar';
|
|
import BookingAutomationPanel from '@/components/BookingAutomationPanel';
|
|
import { formatDate, formatDateTime } from '@/lib/date-utils';
|
|
import Link from 'next/link';
|
|
|
|
export default async function BookingDetailPage({ params }: { params: { id: string } }) {
|
|
const session = await getServerSession(authOptions);
|
|
|
|
if (!session || session.user.role !== 'ADMIN') {
|
|
redirect('/auth/signin');
|
|
}
|
|
|
|
const booking = await prisma.booking.findUnique({
|
|
where: { id: params.id },
|
|
include: {
|
|
location: true,
|
|
photobox: true,
|
|
},
|
|
});
|
|
|
|
if (!booking) {
|
|
redirect('/dashboard/bookings');
|
|
}
|
|
|
|
const getStatusColor = (status: string) => {
|
|
switch (status) {
|
|
case 'RESERVED': return 'bg-yellow-500/20 text-yellow-400 border border-yellow-500/50';
|
|
case 'CONFIRMED': return 'bg-green-500/20 text-green-400 border border-green-500/50';
|
|
case 'COMPLETED': return 'bg-blue-500/20 text-blue-400 border border-blue-500/50';
|
|
case 'CANCELLED': return 'bg-red-500/20 text-red-400 border border-red-500/50';
|
|
default: return 'bg-gray-500/20 text-gray-400 border border-gray-500/50';
|
|
}
|
|
};
|
|
|
|
const getStatusLabel = (status: string) => {
|
|
switch (status) {
|
|
case 'RESERVED': return 'Reserviert';
|
|
case 'CONFIRMED': return 'Bestätigt';
|
|
case 'COMPLETED': return 'Abgeschlossen';
|
|
case 'CANCELLED': return 'Storniert';
|
|
default: return status;
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900">
|
|
<div className="flex">
|
|
<DashboardSidebar user={session.user} />
|
|
<main className="flex-1 p-8">
|
|
<div className="max-w-6xl mx-auto">
|
|
{/* Header */}
|
|
<div className="mb-8">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<div>
|
|
<h1 className="text-3xl font-bold text-white">{booking.bookingNumber}</h1>
|
|
<p className="text-gray-400 mt-1">{booking.customerName}</p>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<Link
|
|
href={`/dashboard/bookings/${booking.id}/edit`}
|
|
className="px-4 py-2 bg-gray-700 hover:bg-gray-600 text-white rounded-lg text-sm font-medium transition-colors"
|
|
>
|
|
Bearbeiten
|
|
</Link>
|
|
<div className={`px-4 py-2 rounded-lg ${getStatusColor(booking.status)}`}>
|
|
{getStatusLabel(booking.status)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Automation Panel */}
|
|
<div className="mb-8">
|
|
<BookingAutomationPanel booking={booking} invoiceType={booking.invoiceType} />
|
|
</div>
|
|
|
|
{/* Booking Details */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
{/* Customer Info */}
|
|
<div className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl p-6 shadow-lg">
|
|
<h3 className="text-xl font-bold text-white mb-4">Kundendaten</h3>
|
|
<div className="space-y-3 text-sm">
|
|
<div>
|
|
<span className="text-gray-400">Name:</span>
|
|
<span className="text-white ml-2 font-medium">{booking.customerName}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-gray-400">E-Mail:</span>
|
|
<span className="text-white ml-2">{booking.customerEmail}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-gray-400">Telefon:</span>
|
|
<span className="text-white ml-2">{booking.customerPhone}</span>
|
|
</div>
|
|
{booking.customerAddress && (
|
|
<div>
|
|
<span className="text-gray-400">Adresse:</span>
|
|
<span className="text-white ml-2">
|
|
{booking.customerAddress}, {booking.customerZip} {booking.customerCity}
|
|
</span>
|
|
</div>
|
|
)}
|
|
{booking.companyName && (
|
|
<div>
|
|
<span className="text-gray-400">Firma:</span>
|
|
<span className="text-white ml-2">{booking.companyName}</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Event Info */}
|
|
<div className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl p-6 shadow-lg">
|
|
<h3 className="text-xl font-bold text-white mb-4">Event-Details</h3>
|
|
<div className="space-y-3 text-sm">
|
|
<div>
|
|
<span className="text-gray-400">Datum:</span>
|
|
<span className="text-white ml-2 font-medium">{formatDate(booking.eventDate)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-gray-400">Location:</span>
|
|
<span className="text-white ml-2">{booking.eventLocation || '-'}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-gray-400">Adresse:</span>
|
|
<span className="text-white ml-2">
|
|
{booking.eventAddress}, {booking.eventZip} {booking.eventCity}
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-gray-400">Aufbau Start:</span>
|
|
<span className="text-white ml-2">{formatDateTime(booking.setupTimeStart)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-gray-400">Aufbau spätestens:</span>
|
|
<span className="text-white ml-2">{formatDateTime(booking.setupTimeLatest)}</span>
|
|
</div>
|
|
{booking.dismantleTimeEarliest && (
|
|
<div>
|
|
<span className="text-gray-400">Abbau ab:</span>
|
|
<span className="text-white ml-2">{formatDateTime(booking.dismantleTimeEarliest)}</span>
|
|
</div>
|
|
)}
|
|
{booking.dismantleTimeLatest && (
|
|
<div>
|
|
<span className="text-gray-400">Abbau spätestens:</span>
|
|
<span className="text-white ml-2">{formatDateTime(booking.dismantleTimeLatest)}</span>
|
|
</div>
|
|
)}
|
|
{booking.notes && (
|
|
<div className="pt-3 border-t border-gray-700">
|
|
<span className="text-gray-400">Notizen:</span>
|
|
<p className="text-white mt-1 whitespace-pre-wrap">{booking.notes}</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Photobox & Pricing */}
|
|
<div className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl p-6 shadow-lg">
|
|
<h3 className="text-xl font-bold text-white mb-4">Fotobox & Preis</h3>
|
|
<div className="space-y-3 text-sm">
|
|
<div>
|
|
<span className="text-gray-400">Modell:</span>
|
|
<span className="text-white ml-2 font-medium">{booking.photobox?.model || '-'}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-gray-400">Serial Number:</span>
|
|
<span className="text-white ml-2">{booking.photobox?.serialNumber || '-'}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-gray-400">Druckflatrate:</span>
|
|
<span className="text-white ml-2">{booking.withPrintFlat ? 'Ja' : 'Nein'}</span>
|
|
</div>
|
|
{booking.distance && (
|
|
<div>
|
|
<span className="text-gray-400">Entfernung:</span>
|
|
<span className="text-white ml-2">{booking.distance.toFixed(1)} km</span>
|
|
</div>
|
|
)}
|
|
{booking.calculatedPrice && (
|
|
<div className="pt-3 border-t border-gray-700">
|
|
<span className="text-gray-400">Gesamtpreis:</span>
|
|
<span className="text-2xl text-pink-400 ml-2 font-bold">
|
|
{booking.calculatedPrice.toFixed(2)} €
|
|
</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Location Info */}
|
|
<div className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl p-6 shadow-lg">
|
|
<h3 className="text-xl font-bold text-white mb-4">Standort</h3>
|
|
<div className="space-y-3 text-sm">
|
|
<div>
|
|
<span className="text-gray-400">Name:</span>
|
|
<span className="text-white ml-2 font-medium">{booking.location?.name}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-gray-400">Stadt:</span>
|
|
<span className="text-white ml-2">{booking.location?.city}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-gray-400">Website:</span>
|
|
<span className="text-white ml-2">{booking.location?.websiteUrl}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-gray-400">Kontakt:</span>
|
|
<span className="text-white ml-2">{booking.location?.contactEmail}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* LexOffice IDs (if present) */}
|
|
{(booking.lexofficeContactId || booking.lexofficeOfferId || booking.lexofficeConfirmationId) && (
|
|
<div className="mt-6 bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl p-6 shadow-lg">
|
|
<h3 className="text-xl font-bold text-white mb-4">LexOffice Integration</h3>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
|
|
{booking.lexofficeContactId && (
|
|
<div>
|
|
<span className="text-gray-400">Kontakt-ID:</span>
|
|
<span className="text-white ml-2 font-mono text-xs">{booking.lexofficeContactId}</span>
|
|
</div>
|
|
)}
|
|
{booking.lexofficeOfferId && (
|
|
<div>
|
|
<span className="text-gray-400">Angebots-ID:</span>
|
|
<span className="text-white ml-2 font-mono text-xs">{booking.lexofficeOfferId}</span>
|
|
</div>
|
|
)}
|
|
{booking.lexofficeConfirmationId && (
|
|
<div>
|
|
<span className="text-gray-400">Bestätigungs-ID:</span>
|
|
<span className="text-white ml-2 font-mono text-xs">{booking.lexofficeConfirmationId}</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|