Add Stargil Nails logo and favicon

- Replace emoji icons with Stargil Nails logo in header and loading spinner
- Add favicon.png to public directory
- Copy logo to public/assets for browser access
- Update vite.config.ts to serve public directory
- Add favicon link to HTML head section
This commit is contained in:
2025-09-29 19:50:10 +02:00
parent b33036300f
commit ab96114295
10 changed files with 175 additions and 88 deletions

View File

@@ -14,18 +14,23 @@ export function BookingForm() {
const { data: treatments } = useQuery(
queryClient.treatments.live.list.experimental_liveOptions()
);
const { data: slotsByDate } = useQuery(
appointmentDate
? queryClient.availability.live.byDate.experimental_liveOptions(appointmentDate)
: queryClient.availability.live.byDate.experimental_liveOptions("")
// Lade alle Slots live und filtere freie Slots
const { data: allSlots } = useQuery(
queryClient.availability.live.list.experimental_liveOptions()
);
const freeSlots = (allSlots || []).filter((s) => s.status === "free");
const availableDates = Array.from(new Set(freeSlots.map((s) => s.date))).sort();
const slotsByDate = appointmentDate
? freeSlots.filter((s) => s.date === appointmentDate)
: [];
const { mutate: createBooking, isPending } = useMutation(
queryClient.bookings.create.mutationOptions()
);
const selectedTreatmentData = treatments?.find(t => t.id === selectedTreatment);
const availableSlots = (slotsByDate || []).filter(s => s.status === "free");
const selectedTreatmentData = treatments?.find((t) => t.id === selectedTreatment);
const availableSlots = (slotsByDate || []).filter((s) => s.status === "free");
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
@@ -33,33 +38,36 @@ export function BookingForm() {
alert("Bitte fülle alle erforderlichen Felder aus");
return;
}
const slot = availableSlots.find(s => s.id === selectedSlotId);
const slot = availableSlots.find((s) => s.id === selectedSlotId);
const appointmentTime = slot?.time || "";
createBooking({
treatmentId: selectedTreatment,
customerName,
customerEmail,
customerPhone,
appointmentDate,
appointmentTime,
notes,
slotId: selectedSlotId,
}, {
onSuccess: () => {
setSelectedTreatment("");
setCustomerName("");
setCustomerEmail("");
setCustomerPhone("");
setAppointmentDate("");
setSelectedSlotId("");
setNotes("");
alert("Buchung erfolgreich erstellt! Wir werden dich kontaktieren, um deinen Termin zu bestätigen.");
createBooking(
{
treatmentId: selectedTreatment,
customerName,
customerEmail,
customerPhone,
appointmentDate,
appointmentTime,
notes,
slotId: selectedSlotId,
},
{
onSuccess: () => {
setSelectedTreatment("");
setCustomerName("");
setCustomerEmail("");
setCustomerPhone("");
setAppointmentDate("");
setSelectedSlotId("");
setNotes("");
alert("Buchung erfolgreich erstellt! Wir werden dich kontaktieren, um deinen Termin zu bestätigen.");
},
}
});
);
};
// Get minimum date (today)
const today = new Date().toISOString().split('T')[0];
// Get minimum date (today) nicht mehr genutzt, Datumsauswahl erfolgt aus freien Slots
const today = new Date().toISOString().split("T")[0];
return (
<div className="max-w-2xl mx-auto bg-white rounded-lg shadow-lg p-6">
@@ -134,16 +142,22 @@ export function BookingForm() {
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Gewünschtes Datum *
Datum (nur freie Termine) *
</label>
<input
type="date"
<select
value={appointmentDate}
onChange={(e) => setAppointmentDate(e.target.value)}
min={today}
onChange={(e) => { setAppointmentDate(e.target.value); setSelectedSlotId(""); }}
className="w-full p-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-pink-500 focus:border-pink-500"
required
/>
>
<option value="">Datum auswählen</option>
{availableDates.map((d) => (
<option key={d} value={d}>{d}</option>
))}
</select>
{availableDates.length === 0 && (
<p className="mt-2 text-sm text-gray-500">Aktuell keine freien Termine verfügbar.</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">