import { FormEvent, useCallback, useEffect, useState } from "react"; import { Link, useParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { api } from "../api/client"; import type { Booking, CheckoutBookingResult, RegisterReturnResult, VehicleDetail } from "../api/types"; import { useDemoManifest } from "../context/DemoManifestContext"; import { useLocaleFormat } from "../i18n/format"; import { StatusBadge } from "../components/Badge"; import { ReturnForm, ReturnResultPanel } from "../components/ReturnForm"; import { Icon } from "../components/Icons"; import { ErrorState, LoadingState, PageHeader } from "../components/PageChrome"; import { PRODUCT_NAME } from "../product"; import { describeApiError, type ApiErrorInfo } from "../api/errorMessages"; import { ApiErrorNotice } from "../components/PageChrome"; import { CheckoutForm } from "../components/CheckoutForm"; export function BookingDetail() { const { t } = useTranslation(["bookings", "returns", "errors"]); const { formatDateTime, formatNumber } = useLocaleFormat(); const { publicRef } = useParams<{ publicRef: string }>(); const { manifest, loading: manifestLoading } = useDemoManifest(); const [booking, setBooking] = useState(null); const [error, setError] = useState(null); const [returnResult, setReturnResult] = useState(null); const [canonicalOdometerKm, setCanonicalOdometerKm] = useState(null); const [cancelReason, setCancelReason] = useState(""); const [cancelling, setCancelling] = useState(false); const [actionError, setActionError] = useState(null); const [checkoutResult, setCheckoutResult] = useState(null); const [requirementsConfirmation, setRequirementsConfirmation] = useState(""); const [confirmingRequirements, setConfirmingRequirements] = useState(false); const load = useCallback(() => { if (!publicRef) return; api .get(`/api/v1/bookings/${publicRef}`) .then(setBooking) .catch(() => setError(t("detail.notFound"))); }, [publicRef]); useEffect(() => { setBooking(null); setError(null); setReturnResult(null); load(); }, [load]); // The odometer-regression demo scenario supplies its own suspicious reading (per the // brief: never ask a demo visitor to invent one) -- only fetched for that one known // scenario booking, not for every return. const isReturnAnomalyScenario = manifest?.scenarios.find((s) => s.id === "return-anomaly")?.start_path === `/bookings/${publicRef}`; useEffect(() => { setCanonicalOdometerKm(null); if (!isReturnAnomalyScenario || !booking) return; api .get(`/api/v1/vehicles/${booking.vehicle_ref}`) .then((vehicle) => setCanonicalOdometerKm(vehicle.odometer_km)) .catch(() => setCanonicalOdometerKm(null)); }, [isReturnAnomalyScenario, booking]); function handleRegistered(result: RegisterReturnResult) { setReturnResult(result); load(); } async function cancelBooking(event: FormEvent) { event.preventDefault(); if (!publicRef) return; setCancelling(true); setActionError(null); try { const updated = await api.post(`/api/v1/bookings/${publicRef}/cancel`, { reason: cancelReason }); setBooking(updated); setCancelReason(""); } catch (err) { setActionError(describeApiError(t, err, "bookings:detail.cancelFailed")); } finally { setCancelling(false); } } function handleCheckout(result: CheckoutBookingResult) { setCheckoutResult(result); load(); } async function confirmRequirements(event: FormEvent) { event.preventDefault(); if (!publicRef) return; setConfirmingRequirements(true); setActionError(null); try { const updated = await api.post(`/api/v1/bookings/${publicRef}/complete-requirements`, { confirmation: requirementsConfirmation, }); setBooking(updated); setRequirementsConfirmation(""); } catch (err) { setActionError(describeApiError(t, err, "bookings:detail.requirementsFailed")); } finally { setConfirmingRequirements(false); } } if (error) return ; if (!booking) return ; return (
{t("detail.backLink")} } />
{t("detail.customer")}
{booking.customer_name} ({booking.customer_ref})
{t("detail.vehicle")}
{booking.vehicle_ref}
{t("detail.starts")}
{formatDateTime(booking.starts_at)}
{t("detail.ends")}
{formatDateTime(booking.ends_at)}
{t("detail.startOdometer")}
{booking.start_odometer_km !== null ? `${formatNumber(booking.start_odometer_km)} km` : t("detail.startOdometerPending")}
{t("detail.endOdometer")}
{booking.end_odometer_km !== null ? `${formatNumber(booking.end_odometer_km)} km` : t("detail.endOdometerPending")}
{t("detail.requirementsComplete")}
{booking.requirements_complete ? t("detail.yes") : t("detail.no")}
{booking.status === "reserved" && !booking.requirements_complete ? (

{t("detail.requirementsAction")}

{t("detail.requirementsActionDetail")}