Files
Atlas/app/booking-page-backup.txt
2025-11-12 20:21:32 +01:00

583 lines
25 KiB
Plaintext

'use client';
import { useState, useEffect } from 'react';
import { FiCalendar, FiMapPin, FiUser, FiMail, FiPhone, FiCamera, FiCheck, FiAlertCircle } from 'react-icons/fi';
interface Location {
id: string;
name: string;
slug: string;
city: string;
}
interface PriceConfig {
model: string;
basePrice: number;
pricePerKm: number;
includedKm: number;
}
const photoboxModels = [
{ value: 'VINTAGE_SMILE', label: 'Vintage Smile', description: 'Klassische Vintage-Box mit Sofortdruck' },
{ value: 'VINTAGE_PHOTOS', label: 'Vintage Photos', description: 'Vintage-Box mit erweiterten Funktionen' },
{ value: 'NOSTALGIE', label: 'Nostalgie', description: 'Nostalgische Fotobox im Retro-Design' },
{ value: 'MAGIC_MIRROR', label: 'Magic Mirror', description: 'Interaktiver Fotospiegel' },
];
export default function BookingFormPage() {
const [step, setStep] = useState(1);
const [locations, setLocations] = useState<Location[]>([]);
const [priceConfigs, setPriceConfigs] = useState<PriceConfig[]>([]);
const [availability, setAvailability] = useState<any>(null);
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const [error, setError] = useState('');
const [formData, setFormData] = useState({
locationSlug: '',
model: '',
customerName: '',
customerEmail: '',
customerPhone: '',
customerAddress: '',
customerCity: '',
customerZip: '',
invoiceType: 'PRIVATE',
companyName: '',
eventDate: '',
eventAddress: '',
eventCity: '',
eventZip: '',
eventLocation: '',
setupTimeStart: '',
setupTimeLatest: '',
dismantleTimeEarliest: '',
dismantleTimeLatest: '',
notes: '',
});
useEffect(() => {
fetch('/api/locations')
.then(res => res.json())
.then(data => setLocations(data.locations || []));
}, []);
useEffect(() => {
if (formData.locationSlug) {
fetch(`/api/prices?location=${formData.locationSlug}`)
.then(res => res.json())
.then(data => setPriceConfigs(data.prices || []));
}
}, [formData.locationSlug]);
useEffect(() => {
if (formData.locationSlug && formData.model && formData.eventDate) {
checkAvailability();
}
}, [formData.locationSlug, formData.model, formData.eventDate]);
const checkAvailability = async () => {
try {
const res = await fetch(
`/api/availability?location=${formData.locationSlug}&model=${formData.model}&date=${formData.eventDate}`
);
const data = await res.json();
setAvailability(data);
} catch (err) {
console.error('Availability check failed:', err);
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError('');
try {
const res = await fetch('/api/bookings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData),
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || 'Buchung fehlgeschlagen');
}
setSuccess(true);
} catch (err: any) {
setError(err.message);
} finally {
setLoading(false);
}
};
const getPrice = () => {
const config = priceConfigs.find(p => p.model === formData.model);
return config ? config.basePrice : 0;
};
if (success) {
return (
<div className="min-h-screen bg-gradient-to-br from-green-50 to-green-100 flex items-center justify-center p-4">
<div className="max-w-md w-full bg-white rounded-2xl shadow-2xl p-8 text-center">
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
<FiCheck className="text-3xl text-green-600" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">Anfrage erfolgreich!</h2>
<p className="text-gray-600 mb-6">
Vielen Dank für Ihre Buchungsanfrage. Wir melden uns in Kürze bei Ihnen mit allen Details.
</p>
<a
href="/booking"
className="inline-block px-6 py-3 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
>
Weitere Buchung
</a>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-gradient-to-br from-red-50 to-red-100 py-12 px-4">
<div className="max-w-4xl mx-auto">
<div className="bg-white rounded-2xl shadow-2xl overflow-hidden">
<div className="bg-red-600 text-white p-6">
<h1 className="text-3xl font-bold">Fotobox buchen</h1>
<p className="text-red-100 mt-2">Save the Moment - Ihr Event, unvergesslich</p>
</div>
<div className="p-8">
<div className="flex items-center justify-between mb-8">
{[1, 2, 3].map((s) => (
<div key={s} className="flex items-center">
<div
className={`w-10 h-10 rounded-full flex items-center justify-center font-bold ${
step >= s ? 'bg-red-600 text-white' : 'bg-gray-200 text-gray-500'
}`}
>
{s}
</div>
{s < 3 && <div className={`h-1 w-20 mx-2 ${step > s ? 'bg-red-600' : 'bg-gray-200'}`} />}
</div>
))}
</div>
<form onSubmit={handleSubmit}>
{step === 1 && (
<div className="space-y-6">
<h2 className="text-2xl font-bold text-gray-900 mb-4">Standort & Fotobox wählen</h2>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
<FiMapPin className="inline mr-2" />
Standort
</label>
<select
value={formData.locationSlug}
onChange={(e) => setFormData({ ...formData, locationSlug: e.target.value })}
required
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
>
<option value="">Bitte wählen...</option>
{locations.map((loc) => (
<option key={loc.id} value={loc.slug}>
{loc.name}
</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
<FiCamera className="inline mr-2" />
Fotobox-Modell
</label>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{photoboxModels.map((model) => {
const price = priceConfigs.find(p => p.model === model.value);
return (
<label
key={model.value}
className={`border-2 rounded-lg p-4 cursor-pointer transition-all ${
formData.model === model.value
? 'border-red-600 bg-red-50'
: 'border-gray-200 hover:border-red-300'
}`}
>
<input
type="radio"
name="model"
value={model.value}
checked={formData.model === model.value}
onChange={(e) => setFormData({ ...formData, model: e.target.value })}
className="sr-only"
/>
<div className="font-semibold text-gray-900">{model.label}</div>
<div className="text-sm text-gray-600 mt-1">{model.description}</div>
{price && (
<div className="text-red-600 font-bold mt-2">
ab {price.basePrice}€
</div>
)}
</label>
);
})}
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
<FiCalendar className="inline mr-2" />
Event-Datum
</label>
<input
type="date"
value={formData.eventDate}
onChange={(e) => setFormData({ ...formData, eventDate: e.target.value })}
required
min={new Date().toISOString().split('T')[0]}
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
{availability && (
<div className={`mt-2 p-3 rounded-lg ${
availability.available
? availability.isLastOne
? 'bg-yellow-50 text-yellow-800 border border-yellow-200'
: 'bg-green-50 text-green-800 border border-green-200'
: 'bg-red-50 text-red-800 border border-red-200'
}`}>
<FiAlertCircle className="inline mr-2" />
{availability.message}
</div>
)}
</div>
<button
type="button"
onClick={() => setStep(2)}
disabled={!formData.locationSlug || !formData.model || !formData.eventDate || (availability && !availability.available)}
className="w-full bg-red-600 text-white py-3 rounded-lg font-semibold hover:bg-red-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
Weiter
</button>
</div>
)}
{step === 2 && (
<div className="space-y-6">
<h2 className="text-2xl font-bold text-gray-900 mb-4">Ihre Kontaktdaten</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-2">
Rechnungsart
</label>
<div className="flex gap-4">
<label className="flex-1">
<input
type="radio"
name="invoiceType"
value="PRIVATE"
checked={formData.invoiceType === 'PRIVATE'}
onChange={(e) => setFormData({ ...formData, invoiceType: e.target.value as any })}
className="mr-2"
/>
Privat
</label>
<label className="flex-1">
<input
type="radio"
name="invoiceType"
value="BUSINESS"
checked={formData.invoiceType === 'BUSINESS'}
onChange={(e) => setFormData({ ...formData, invoiceType: e.target.value as any })}
className="mr-2"
/>
Geschäftlich
</label>
</div>
</div>
{formData.invoiceType === 'BUSINESS' && (
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-2">
Firmenname
</label>
<input
type="text"
value={formData.companyName}
onChange={(e) => setFormData({ ...formData, companyName: e.target.value })}
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
)}
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-2">
<FiUser className="inline mr-2" />
Name
</label>
<input
type="text"
value={formData.customerName}
onChange={(e) => setFormData({ ...formData, customerName: e.target.value })}
required
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
<FiMail className="inline mr-2" />
E-Mail
</label>
<input
type="email"
value={formData.customerEmail}
onChange={(e) => setFormData({ ...formData, customerEmail: e.target.value })}
required
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
<FiPhone className="inline mr-2" />
Telefon
</label>
<input
type="tel"
value={formData.customerPhone}
onChange={(e) => setFormData({ ...formData, customerPhone: e.target.value })}
required
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-2">
Adresse (optional)
</label>
<input
type="text"
value={formData.customerAddress}
onChange={(e) => setFormData({ ...formData, customerAddress: e.target.value })}
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
PLZ (optional)
</label>
<input
type="text"
value={formData.customerZip}
onChange={(e) => setFormData({ ...formData, customerZip: e.target.value })}
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Stadt (optional)
</label>
<input
type="text"
value={formData.customerCity}
onChange={(e) => setFormData({ ...formData, customerCity: e.target.value })}
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
</div>
<div className="flex gap-4">
<button
type="button"
onClick={() => setStep(1)}
className="flex-1 bg-gray-200 text-gray-800 py-3 rounded-lg font-semibold hover:bg-gray-300 transition-colors"
>
Zurück
</button>
<button
type="button"
onClick={() => setStep(3)}
disabled={!formData.customerName || !formData.customerEmail || !formData.customerPhone}
className="flex-1 bg-red-600 text-white py-3 rounded-lg font-semibold hover:bg-red-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
Weiter
</button>
</div>
</div>
)}
{step === 3 && (
<div className="space-y-6">
<h2 className="text-2xl font-bold text-gray-900 mb-4">Event-Details</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-2">
Event-Adresse
</label>
<input
type="text"
value={formData.eventAddress}
onChange={(e) => setFormData({ ...formData, eventAddress: e.target.value })}
required
placeholder="Straße und Hausnummer"
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
PLZ
</label>
<input
type="text"
value={formData.eventZip}
onChange={(e) => setFormData({ ...formData, eventZip: e.target.value })}
required
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Stadt
</label>
<input
type="text"
value={formData.eventCity}
onChange={(e) => setFormData({ ...formData, eventCity: e.target.value })}
required
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-2">
Location-Name (optional)
</label>
<input
type="text"
value={formData.eventLocation}
onChange={(e) => setFormData({ ...formData, eventLocation: e.target.value })}
placeholder="z.B. Hotel Maritim, Villa Rosengarten"
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Aufbau möglich ab
</label>
<input
type="datetime-local"
value={formData.setupTimeStart}
onChange={(e) => setFormData({ ...formData, setupTimeStart: e.target.value })}
required
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Aufbau spätestens bis
</label>
<input
type="datetime-local"
value={formData.setupTimeLatest}
onChange={(e) => setFormData({ ...formData, setupTimeLatest: e.target.value })}
required
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Abbau frühestens ab (optional)
</label>
<input
type="datetime-local"
value={formData.dismantleTimeEarliest}
onChange={(e) => setFormData({ ...formData, dismantleTimeEarliest: e.target.value })}
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Abbau spätestens bis (optional)
</label>
<input
type="datetime-local"
value={formData.dismantleTimeLatest}
onChange={(e) => setFormData({ ...formData, dismantleTimeLatest: e.target.value })}
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-2">
Besondere Wünsche / Anmerkungen (optional)
</label>
<textarea
value={formData.notes}
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
rows={4}
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
</div>
{error && (
<div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg">
{error}
</div>
)}
<div className="bg-gray-50 p-4 rounded-lg">
<h3 className="font-semibold text-gray-900 mb-2">Zusammenfassung</h3>
<div className="space-y-1 text-sm text-gray-600">
<p>Modell: {photoboxModels.find(m => m.value === formData.model)?.label}</p>
<p>Datum: {new Date(formData.eventDate).toLocaleDateString('de-DE')}</p>
<p>Standort: {locations.find(l => l.slug === formData.locationSlug)?.name}</p>
<p className="text-lg font-bold text-gray-900 mt-2">
Basispreis: {getPrice()}€ *
</p>
<p className="text-xs text-gray-500">* zzgl. Anfahrtskosten</p>
</div>
</div>
<div className="flex gap-4">
<button
type="button"
onClick={() => setStep(2)}
className="flex-1 bg-gray-200 text-gray-800 py-3 rounded-lg font-semibold hover:bg-gray-300 transition-colors"
>
Zurück
</button>
<button
type="submit"
disabled={loading}
className="flex-1 bg-red-600 text-white py-3 rounded-lg font-semibold hover:bg-red-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{loading ? 'Wird gesendet...' : 'Verbindlich anfragen'}
</button>
</div>
</div>
)}
</form>
</div>
</div>
</div>
</div>
);
}