import { FormEvent, useEffect, useMemo, useState } from "react"; import { Link, useNavigate } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { api } from "../api/client"; import { describeApiError, type ApiErrorInfo } from "../api/errorMessages"; import type { AvailableVehicle, Booking, CustomerOption } from "../api/types"; import { ApiErrorNotice, PageHeader } from "../components/PageChrome"; import { Icon } from "../components/Icons"; import { brusselsDateTimeFromNow, tryBrusselsLocalToIso } from "../i18n/brusselsDateTime"; export function BookingCreate() { const { t } = useTranslation(["bookings", "errors"]); const navigate = useNavigate(); const [customerQuery, setCustomerQuery] = useState(""); const [customers, setCustomers] = useState([]); const [customerRef, setCustomerRef] = useState(""); const [startsAt, setStartsAt] = useState(() => brusselsDateTimeFromNow(2)); const [endsAt, setEndsAt] = useState(() => brusselsDateTimeFromNow(26)); const [vehicles, setVehicles] = useState([]); const [vehicleRef, setVehicleRef] = useState(""); const [vehicleQuery, setVehicleQuery] = useState(""); const [loadingVehicles, setLoadingVehicles] = useState(false); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const startsAtIso = useMemo(() => startsAt ? tryBrusselsLocalToIso(startsAt) : null, [startsAt]); const endsAtIso = useMemo(() => endsAt ? tryBrusselsLocalToIso(endsAt) : null, [endsAt]); const windowValid = Boolean(startsAtIso && endsAtIso && endsAtIso > startsAtIso); const localTimeInvalid = Boolean( (startsAt && !startsAtIso) || (endsAt && !endsAtIso), ); const windowOrderInvalid = Boolean(startsAtIso && endsAtIso && endsAtIso <= startsAtIso); useEffect(() => { if (customerQuery.trim().length < 2) { setCustomers([]); return; } const controller = new AbortController(); const timeout = window.setTimeout(() => { api.get(`/api/v1/customers?query=${encodeURIComponent(customerQuery.trim())}`, { signal: controller.signal }) .then((result) => { setCustomers(result); if (!result.some((customer) => customer.public_ref === customerRef)) setCustomerRef(""); }) .catch((err) => { if (!controller.signal.aborted) setError(describeApiError(t, err)); }); }, 250); return () => { window.clearTimeout(timeout); controller.abort(); }; }, [customerQuery, customerRef, t]); useEffect(() => { if (!windowValid) { setVehicles([]); setVehicleRef(""); return; } const controller = new AbortController(); const timeout = window.setTimeout(() => { setLoadingVehicles(true); const params = new URLSearchParams({ starts_at: startsAtIso as string, ends_at: endsAtIso as string, }); if (vehicleQuery.trim()) params.set("query", vehicleQuery.trim()); params.set("limit", "50"); api.get(`/api/v1/bookings/availability?${params.toString()}`, { signal: controller.signal }) .then((result) => { setVehicles(result); setVehicleRef((current) => result.some((vehicle) => vehicle.public_ref === current) ? current : ""); }) .catch((err) => { if (!controller.signal.aborted) setError(describeApiError(t, err)); }) .finally(() => { if (!controller.signal.aborted) setLoadingVehicles(false); }); }, 250); return () => { window.clearTimeout(timeout); controller.abort(); }; }, [endsAtIso, startsAtIso, t, vehicleQuery, windowValid]); async function submit(event: FormEvent) { event.preventDefault(); if (!startsAtIso || !endsAtIso || endsAtIso <= startsAtIso) return; setError(null); setSubmitting(true); try { const booking = await api.post("/api/v1/bookings", { customer_ref: customerRef, vehicle_ref: vehicleRef, starts_at: startsAtIso, ends_at: endsAtIso, }); navigate(`/bookings/${booking.public_ref}`); } catch (err) { setError(describeApiError(t, err, "bookings:create.failed")); } finally { setSubmitting(false); } } return (
{t("create.backLink")}
{localTimeInvalid &&

{t("create.invalidLocalTime")}

} {!localTimeInvalid && windowOrderInvalid &&

{t("create.endAfterStart")}

}
{t("create.cancel")}
); }