"use client"; import { useEffect, useState } from "react"; import { MonthCalendar, type CalendarMarker } from "./MonthCalendar"; import { ResendVerificationButton } from "@/components/auth/ResendVerificationButton"; import { TextAreaField, FormError } from "@/components/FormField"; import { track } from "@/lib/analytics/client"; import { berlinDateKey, formatBerlinDate, formatBerlinTime } from "@/lib/calendar/timezone"; type SlotsResponse = { durationMinutes: number; slots: Record }; function dateKey(d: Date) { return berlinDateKey(d); } const fmtTime = (iso: string) => formatBerlinTime(new Date(iso), { hour: "2-digit", minute: "2-digit" }); const fmtDate = (iso: string) => formatBerlinDate(new Date(iso), { weekday: "long", day: "2-digit", month: "long" }); export function BookingWidget({ offerSlug, isLoggedIn, isVerified, }: { offerSlug: string; isLoggedIn: boolean; isVerified: boolean; }) { const [slotsByDay, setSlotsByDay] = useState({}); const [loading, setLoading] = useState(true); const [loadError, setLoadError] = useState(false); const [selectedDay, setSelectedDay] = useState(null); const [selectedSlot, setSelectedSlot] = useState(null); const [appointmentType, setAppointmentType] = useState<"onsite" | "online">("onsite"); const [message, setMessage] = useState(""); const [submitting, setSubmitting] = useState(false); const [result, setResult] = useState<{ ok: boolean; error?: string } | null>(null); useEffect(() => { const controller = new AbortController(); const from = new Date(); const to = new Date(from.getTime() + 28 * 24 * 60 * 60 * 1000); fetch(`/api/booking/slots?offer=${encodeURIComponent(offerSlug)}&from=${dateKey(from)}&to=${dateKey(to)}`, { signal: controller.signal }) .then((res) => { if (!res.ok) throw new Error("Slots unavailable"); return res.json(); }) .then((data: SlotsResponse) => setSlotsByDay(data.slots ?? {})) .catch(() => { if (!controller.signal.aborted) setLoadError(true); }) .finally(() => { if (!controller.signal.aborted) setLoading(false); }); return () => controller.abort(); }, [offerSlug]); if (result?.ok) { return (

Deine Anfrage

{selectedSlot && (

{fmtDate(selectedSlot)}, {fmtTime(selectedSlot)} Uhr

)}

Status: Termin vorgeschlagen

Der Termin ist noch nicht verbindlich. Du erhältst eine E-Mail, sobald Anna den Termin bestätigt.

); } if (!isLoggedIn) { return (

Melde dich an oder erstelle ein Konto, um einen Termin anzufragen.

); } if (!isVerified) { return (

Bitte bestätige zuerst deine E-Mail-Adresse.

Erst danach kannst du einen Termin anfragen.

); } const markers: CalendarMarker[] = Object.keys(slotsByDay).map((key) => ({ date: new Date(key), status: "public" })); const daySlots = selectedDay ? (slotsByDay[dateKey(selectedDay)] ?? []) : []; async function submit() { if (!selectedSlot) return; setSubmitting(true); setResult(null); try { const res = await fetch("/api/booking/request", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ offerSlug, start: selectedSlot, appointmentType, message }), }); const data = await res.json().catch(() => ({})); if (!res.ok) { setResult({ ok: false, error: data.error || "Anfrage fehlgeschlagen." }); setSubmitting(false); return; } setResult({ ok: true }); // Event only — never the offer/date/message content. track("booking_started"); } catch { setResult({ ok: false, error: "Verbindung fehlgeschlagen." }); setSubmitting(false); } } if (loading) { return

Verfügbare Termine werden geladen …

; } if (loadError) return Verfügbare Termine konnten nicht geladen werden. Bitte lade die Seite erneut.; return (
{ setSelectedDay(day); setSelectedSlot(null); }} selectedDay={selectedDay} />
{!selectedDay &&

Wähle einen markierten Tag, um freie Zeiten zu sehen.

} {selectedDay && daySlots.length === 0 && (

An diesem Tag ist leider kein Termin frei.

)} {selectedDay && daySlots.length > 0 && (

{fmtDate(daySlots[0].start)}

{daySlots.map((slot) => ( ))}
)}
{selectedSlot && (

Ausgewählt: {fmtDate(selectedSlot)}, {fmtTime(selectedSlot)} Uhr

Terminart
setMessage(e.target.value)} rows={3} wrapperClassName="mt-4" /> {result?.error && {result.error}}
)}
); }