Initial commit - SaveTheMoment Atlas Basis-Setup
This commit is contained in:
405
components/NewBookingForm.tsx
Normal file
405
components/NewBookingForm.tsx
Normal file
@@ -0,0 +1,405 @@
|
||||
"use client";
|
||||
|
||||
import { useState } 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 [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),
|
||||
});
|
||||
|
||||
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_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>
|
||||
</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>
|
||||
</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 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user