+
+

+
+
+
+
+ {getPhotoName(photoID)}
+
+
+ {variant.name} Β· {variant.material}
+
+
${variant.price}
+
+
+
+
+ {item.qty}
+
+
+
+
+
+ );
+};
+
+export default function CartPage() {
+ const { items } = useCart();
+ const posthog = usePostHog();
+ const [isCheckingOut, setIsCheckingOut] = useState(false);
+
+ // Safari/Firefox restore this page from the bfcache when the user hits
+ // Back from Stripe β reset the button so it isn't stuck on "Redirectingβ¦".
+ useEffect(() => {
+ const onPageShow = (event: PageTransitionEvent) => {
+ if (event.persisted) setIsCheckingOut(false);
+ };
+ window.addEventListener("pageshow", onPageShow);
+ return () => window.removeEventListener("pageshow", onPageShow);
+ }, []);
+
+ const subtotal = items.reduce((total, item) => {
+ const variant = photoPricing.find((v) => v.id === item.variantId);
+ return total + (variant?.price ?? 0) * item.qty;
+ }, 0);
+
+ const handleCheckout = async () => {
+ setIsCheckingOut(true);
+ posthog.capture("begin_checkout", {
+ item_count: items.length,
+ subtotal,
+ });
+
+ try {
+ const response = await fetch(PAGES.PHOTOGRAPHY.CHECKOUT_API, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ items }),
+ });
+ if (!response.ok) {
+ const message = await response.text();
+ // 400s carry a human-readable reason (eg. an out-of-stock variant).
+ toast.error(
+ response.status === 400 && message
+ ? message
+ : "Something went wrong starting checkout. Please try again.",
+ );
+ setIsCheckingOut(false);
+ return;
+ }
+ const { url } = await response.json();
+ window.location.href = url;
+ } catch (error) {
+ console.error("Checkout failed:", error);
+ toast.error("Something went wrong starting checkout. Please try again.");
+ setIsCheckingOut(false);
+ }
+ };
+
+ return (
+