Files
Atlas/components/NewBookingForm.tsx
Julia Wehden a2c95c70e7 feat: Equipment-System, Buchungsbearbeitung, Kundenadresse, LexOffice-Fix
- 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
2026-03-19 16:21:55 +01:00

521 lines
19 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
import { FiCalendar, FiMapPin } from "react-icons/fi";
import Link from "next/link";
import { useRouter } from "next/navigation";
interface NewBookingFormProps {
locations: any[];
user: any;
}
export default function NewBookingForm({
locations,
user,
}: NewBookingFormProps) {
const router = useRouter();
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const [equipmentList, setEquipmentList] = useState<any[]>([]);
const [selectedEquipment, setSelectedEquipment] = useState<string[]>([]);
useEffect(() => {
fetch("/api/equipment")
.then((res) => res.json())
.then((data) => setEquipmentList(data.equipment || []))
.catch(() => {});
}, []);
const toggleEquipment = (id: string) => {
setSelectedEquipment((prev) =>
prev.includes(id) ? prev.filter((e) => e !== id) : [...prev, id]
);
};
const [formData, setFormData] = useState({
locationId: "",
model: "VINTAGE_SMILE",
customerName: "",
customerEmail: "",
customerPhone: "",
customerAddress: "",
customerCity: "",
customerZip: "",
invoiceType: "PRIVATE",
companyName: "",
eventDate: "",
eventAddress: "",
eventCity: "",
eventZip: "",
eventLocation: "",
setupTimeStart: "",
setupTimeLatest: "",
dismantleTimeEarliest: "",
dismantleTimeLatest: "",
calculatedPrice: 0,
notes: "",
});
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError("");
try {
const res = await fetch("/api/bookings/create", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ...formData, equipmentIds: selectedEquipment }),
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "Fehler beim Erstellen");
}
alert("Buchung erfolgreich erstellt!");
router.push(`/dashboard/bookings/${data.booking.id}`);
} catch (err: any) {
setError(err.message);
setLoading(false);
}
};
return (
<div className="max-w-4xl mx-auto">
<div className="mb-8">
<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">
Neue Buchung erstellen
</h2>
<p className="text-gray-400 mt-1">Manuelle Buchung anlegen</p>
</div>
<form
onSubmit={handleSubmit}
className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl shadow-sm p-6 space-y-6"
>
<div>
<h3 className="text-lg font-semibold text-white mb-4">
Standort & Fotobox
</h3>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
Standort
</label>
<select
value={formData.locationId}
onChange={(e) =>
setFormData({ ...formData, locationId: e.target.value })
}
required
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white 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.id}>
{loc.name}
</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
Modell
</label>
<select
value={formData.model}
onChange={(e) =>
setFormData({ ...formData, model: e.target.value })
}
required
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
>
<option value="VINTAGE">Vintage</option>
<option value="VINTAGE_SMILE">Vintage Smile</option>
<option value="VINTAGE_PHOTOS">Vintage Photos</option>
<option value="NOSTALGIE">Nostalgie</option>
<option value="MAGIC_MIRROR">Magic Mirror</option>
</select>
</div>
</div>
{equipmentList.length > 0 && (
<div className="mt-4">
<label className="block text-sm font-medium text-gray-300 mb-2">
Zusatzausstattung
</label>
<div className="grid grid-cols-2 gap-2">
{equipmentList.map((eq) => (
<label
key={eq.id}
className={`flex items-center gap-2 p-3 rounded-lg border cursor-pointer transition-colors ${
selectedEquipment.includes(eq.id)
? "bg-red-500/10 border-red-500/50 text-white"
: "bg-gray-700/50 border-gray-600 text-gray-300 hover:border-gray-500"
}`}
>
<input
type="checkbox"
checked={selectedEquipment.includes(eq.id)}
onChange={() => toggleEquipment(eq.id)}
className="accent-red-500"
/>
{eq.name}
</label>
))}
</div>
</div>
)}
</div>
<div>
<h3 className="text-lg font-semibold text-white mb-4">Kundendaten</h3>
<div className="grid grid-cols-2 gap-4">
<div className="col-span-2">
<label className="block text-sm font-medium text-gray-300 mb-2">
Rechnungsart
</label>
<div className="flex gap-4">
<label className="flex items-center text-white">
<input
type="radio"
value="PRIVATE"
checked={formData.invoiceType === "PRIVATE"}
onChange={(e) =>
setFormData({ ...formData, invoiceType: e.target.value })
}
className="mr-2"
/>
Privat
</label>
<label className="flex items-center text-white">
<input
type="radio"
value="BUSINESS"
checked={formData.invoiceType === "BUSINESS"}
onChange={(e) =>
setFormData({ ...formData, invoiceType: e.target.value })
}
className="mr-2"
/>
Geschäftlich
</label>
</div>
</div>
{formData.invoiceType === "BUSINESS" && (
<div className="col-span-2">
<label className="block text-sm font-medium text-gray-300 mb-2">
Firmenname
</label>
<input
type="text"
value={formData.companyName}
onChange={(e) =>
setFormData({ ...formData, companyName: e.target.value })
}
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
)}
<div className="col-span-2">
<label className="block text-sm font-medium text-gray-300 mb-2">
Name
</label>
<input
type="text"
value={formData.customerName}
onChange={(e) =>
setFormData({ ...formData, customerName: e.target.value })
}
required
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 mb-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-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
Telefon
</label>
<input
type="tel"
value={formData.customerPhone}
onChange={(e) =>
setFormData({ ...formData, customerPhone: e.target.value })
}
required
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-gray-300 mb-2">
Adresse
</label>
<input
type="text"
value={formData.customerAddress}
onChange={(e) =>
setFormData({ ...formData, customerAddress: e.target.value })
}
placeholder="Straße und Hausnummer"
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent placeholder-gray-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
PLZ
</label>
<input
type="text"
value={formData.customerZip}
onChange={(e) =>
setFormData({ ...formData, customerZip: e.target.value })
}
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
Stadt
</label>
<input
type="text"
value={formData.customerCity}
onChange={(e) =>
setFormData({ ...formData, customerCity: e.target.value })
}
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
</div>
</div>
<div>
<h3 className="text-lg font-semibold text-white mb-4">
Event-Details
</h3>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-300 mb-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-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
Preis ()
</label>
<input
type="number"
value={formData.calculatedPrice}
onChange={(e) =>
setFormData({
...formData,
calculatedPrice: parseFloat(e.target.value),
})
}
required
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-gray-300 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-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent placeholder-gray-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 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-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 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-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-gray-300 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"
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent placeholder-gray-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
Aufbau ab
</label>
<input
type="datetime-local"
value={formData.setupTimeStart}
onChange={(e) =>
setFormData({ ...formData, setupTimeStart: e.target.value })
}
required
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
Aufbau spätestens
</label>
<input
type="datetime-local"
value={formData.setupTimeLatest}
onChange={(e) =>
setFormData({ ...formData, setupTimeLatest: e.target.value })
}
required
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
Abbau ab
</label>
<input
type="datetime-local"
value={formData.dismantleTimeEarliest}
onChange={(e) =>
setFormData({ ...formData, dismantleTimeEarliest: e.target.value })
}
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
Abbau spätestens
</label>
<input
type="datetime-local"
value={formData.dismantleTimeLatest}
onChange={(e) =>
setFormData({ ...formData, dismantleTimeLatest: e.target.value })
}
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-gray-300 mb-2">
Notizen (optional)
</label>
<textarea
value={formData.notes}
onChange={(e) =>
setFormData({ ...formData, notes: e.target.value })
}
rows={4}
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
</div>
</div>
{error && (
<div className="bg-red-500/20 border border-red-500/50 text-red-400 px-4 py-3 rounded-lg">
{error}
</div>
)}
<div className="flex gap-4 pt-4 border-t border-gray-700">
<Link
href="/dashboard/bookings"
className="flex-1 px-4 py-3 bg-gray-700 text-gray-300 rounded-lg font-semibold text-center hover:bg-gray-600 transition-colors"
>
Abbrechen
</Link>
<button
type="submit"
disabled={loading}
className="flex-1 px-4 py-3 bg-gradient-to-r from-red-600 to-pink-600 text-white rounded-lg font-semibold hover:from-red-700 hover:to-pink-700 transition-all shadow-lg disabled:opacity-50"
>
{loading ? "Wird erstellt..." : "Buchung erstellen"}
</button>
</div>
</form>
</div>
);
}