98 lines
3.1 KiB
TypeScript
98 lines
3.1 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { Link, useNavigate, useParams } from "react-router-dom";
|
|
import { formatApiMessage } from "../api/client";
|
|
import * as api from "../api/services";
|
|
import { useAuth } from "../auth/AuthContext";
|
|
import {
|
|
attributionParamsFromSearch,
|
|
hasAttributionParams,
|
|
persistMarketingClickId,
|
|
readMarketingClickId,
|
|
stripAttributionFromUrl,
|
|
} from "../marketing/attribution";
|
|
import type { EquipmentItemDetail } from "../api/types";
|
|
|
|
export function EquipmentBookPage() {
|
|
const { publicId } = useParams<{ publicId: string }>();
|
|
const navigate = useNavigate();
|
|
const { user } = useAuth();
|
|
const [item, setItem] = useState<EquipmentItemDetail | null>(null);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
if (!publicId) return;
|
|
const pathnameAtStart = window.location.pathname;
|
|
const searchAtStart = window.location.search;
|
|
const attribution = attributionParamsFromSearch(searchAtStart);
|
|
let cancelled = false;
|
|
(async () => {
|
|
try {
|
|
const data = await api.getEquipmentItem(
|
|
publicId,
|
|
hasAttributionParams(attribution) ? attribution : undefined,
|
|
);
|
|
if (!cancelled) {
|
|
setItem(data);
|
|
if (hasAttributionParams(attribution)) {
|
|
persistMarketingClickId("equipment", publicId, data.marketing_click_id);
|
|
stripAttributionFromUrl(navigate, pathnameAtStart, searchAtStart);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
if (!cancelled) setError(formatApiMessage(e));
|
|
} finally {
|
|
if (!cancelled) setLoading(false);
|
|
}
|
|
})();
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [publicId, navigate]);
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="page">
|
|
<p className="muted">Loading…</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error || !item) {
|
|
return (
|
|
<div className="page">
|
|
<p className="lede">{error ?? "Listing not found."}</p>
|
|
<Link to="/boats" className="text-link">
|
|
Back to boat rentals
|
|
</Link>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const marketingClickIdForConversion =
|
|
readMarketingClickId("equipment", item.public_id) ?? item.marketing_click_id;
|
|
|
|
return (
|
|
<div className="page booking-request-page">
|
|
<Link to={`/boats/item/${item.public_id}`} className="text-link">
|
|
← Back to listing
|
|
</Link>
|
|
<h1>Request this rental</h1>
|
|
<p className="lede">
|
|
You are signed in as <strong>{user?.email}</strong>. Confirm details below; a full booking form can submit to
|
|
the WaterTrekk booking API next.
|
|
</p>
|
|
<article className="booking-request-summary">
|
|
<h2>{item.title}</h2>
|
|
<p className="muted">
|
|
{item.vendor_business_name} · {item.location} · ${item.price_per_day} / day
|
|
</p>
|
|
<p className="muted">
|
|
<code>marketing_click_id</code> {marketingClickIdForConversion}{" "}
|
|
<span className="muted">(send this on the booking request for attribution)</span>
|
|
</p>
|
|
</article>
|
|
</div>
|
|
);
|
|
}
|