Review

Northline sleeper: berth deposit charged on confirm mount

After a passenger picks an upper berth on the Vancouver–Banff sleeper, ConfirmBerthDeposit should hold inventory by charging the $48 non-refundable deposit on their saved card as soon as the confirm panel mounts. Wire the charge in useEffect so the UI shows Charging… → receipt without an extra Confirm tap.

ReactTier 3strict-modeuseEffectpaymentsidempotency

Click a line to flag it, pick one or more labels, then submit. If the change looks correct, approve it.

src/booking/ConfirmBerthDeposit.tsx+11-13
1818type Props = {
1919 bookingId: string;
2020 paymentMethodId: string;
2121 berthLabel: string;
2222};
2323
2424export function ConfirmBerthDeposit({
2525 bookingId,
2626 paymentMethodId,
2727 berthLabel,
2828}: Props) {
2929 const [status, setStatus] = useState<"idle" | "charging" | "ok" | "error">("idle");
3030 const [receiptId, setReceiptId] = useState<string | null>(null);
3131
32- async function onConfirmDeposit() {
33- setStatus("charging");
34- try {
35- const receipt = await chargeBerthDeposit({ bookingId, paymentMethodId });
36- setReceiptId(receipt.id);
37- setStatus("ok");
38- } catch {
39- setStatus("error");
40- }
41- }
32+ // Auto-charge the berth hold as soon as this panel is shown.
33+ useEffect(() => {
34+ setStatus("charging");
35+ chargeBerthDeposit({ bookingId, paymentMethodId })
36+ .then((receipt) => {
37+ setReceiptId(receipt.id);
38+ setStatus("ok");
39+ })
40+ .catch(() => setStatus("error"));
41+ }, [bookingId, paymentMethodId]);
4242
4343 return (
4444 <section aria-live="polite">
4545 <h2>Hold {berthLabel}</h2>
46- <button type="button" onClick={() => void onConfirmDeposit()} disabled={status === "charging"}>
47- {status === "charging" ? "Charging deposit…" : "Charge $48 deposit"}
48- </button>
46+ {status === "charging" && <p>Charging $48 berth deposit…</p>}
4947 {status === "ok" && receiptId && (
5048 <p role="status">Deposit captured — receipt {receiptId}</p>
5149 )}
5250 {status === "error" && <p role="alert">Deposit failed — try another card</p>}
5351 </section>
5452 );
5553}