// Checkout.jsx — full 3-step checkout flow. // Steps: dirección → pago → confirmar → (CheckoutDone in Cart.jsx) const { useState: useChkState } = React; // ===================================================================== // Saved addresses + cards (reused from Account.jsx fakes; could be hoisted). const CHK_ADDRESSES = [ { id: "a1", label: "Casa", name: "Bau Andalón", street: "Av. Francisco Villa 1180, Int. 4", area: "Versalles, Puerto Vallarta", state: "Jalisco", zip: "48330", phone: "+52 322 145 2287", default: true }, { id: "a2", label: "Oficina", name: "Bau Andalón", street: "Calle Honduras 156", area: "5 de Diciembre, Puerto Vallarta", state: "Jalisco", zip: "48350", phone: "+52 322 145 2287" }, ]; const CHK_CARDS = [ { id: "c1", brand: "VISA", last4: "4242", exp: "08/28", default: true, gradient: "linear-gradient(135deg, #1F3A93 0%, #0A1740 100%)" }, { id: "c2", brand: "MASTERCARD", last4: "5588", exp: "11/27", default: false, gradient: "linear-gradient(135deg, #18181B 0%, #3A3A40 100%)" }, ]; const CheckoutPage = ({ cart, user, onNav, onComplete }) => { const [step, setStep] = useChkState(0); const { isMobile } = useViewport(); const [addressId, setAddressId] = useChkState(CHK_ADDRESSES.find(a => a.default).id); const [showNewAddr, setShowNewAddr] = useChkState(false); const [newAddr, setNewAddr] = useChkState({ label: "Casa", name: user?.name || "", street: "", area: "", zip: "", phone: "" }); const [savedAddresses, setSavedAddresses] = useChkState(CHK_ADDRESSES); const [paymentType, setPaymentType] = useChkState("card"); // 'card' | 'newCard' | 'oxxo' | 'paypal' const [cardId, setCardId] = useChkState(CHK_CARDS.find(c => c.default).id); const [msi, setMsi] = useChkState(1); const [newCard, setNewCard] = useChkState({ number: "", titular: "", exp: "", cvv: "", saveCard: true }); const [processing, setProcessing] = useChkState(false); const toast = useToast(); const subtotal = cart.reduce((s, it) => s + it.p.price * it.qty, 0); const savings = cart.reduce((s, it) => s + ((it.p.was || it.p.price) - it.p.price) * it.qty, 0); const shipping = subtotal >= 599 ? 0 : 89; const total = subtotal + shipping; const selectedAddress = savedAddresses.find(a => a.id === addressId); const selectedCard = CHK_CARDS.find(c => c.id === cardId); // Demo mode: all steps pass validation const canNext = true; const next = () => { if (!canNext) return; if (step < 2) setStep(s => s + 1); else pay(); }; const pay = () => { setProcessing(true); setTimeout(() => { setProcessing(false); const orderNum = "AL-" + Math.floor(Math.random() * 900000 + 100000); onComplete({ orderNum, address: selectedAddress, payment: { type: paymentType, card: selectedCard, msi }, total, items: cart }); }, 1600); }; // Empty cart guard removed in demo mode — user can walk checkout with empty cart too if (false && cart.length === 0 && !processing) { return (

Tu carrito está vacío

Agrega productos antes de continuar al pago.

onNav("home")} style={{ marginTop: 20, alignSelf: "center" }}>Seguir comprando
); } const addNewAddr = () => { // Demo mode: accept any address const id = "a" + (savedAddresses.length + 1); const added = { id, ...newAddr, street: newAddr.street || "Nueva calle 123", zip: newAddr.zip || "48330", city: "Puerto Vallarta", state: "Jalisco" }; setSavedAddresses([...savedAddresses, added]); setAddressId(id); setShowNewAddr(false); toast("Dirección agregada"); }; return (
{/* Stepper */}
{["Dirección", "Pago", "Confirmar"].map((label, i) => (
{i < step ? "✓" : i + 1} {label}
))}
{/* LEFT — step content */}
{step === 0 && ( <>

Dirección de entrega

Elige a dónde te llega el pedido.

{savedAddresses.map(a => ( ))} {!showNewAddr ? ( ) : (

Nueva dirección

setNewAddr({ ...newAddr, name: e.target.value })} />
setNewAddr({ ...newAddr, street: e.target.value })} />
setNewAddr({ ...newAddr, area: e.target.value })} /> setNewAddr({ ...newAddr, zip: e.target.value })} maxLength={5} /> setNewAddr({ ...newAddr, phone: e.target.value })} />
Guardar dirección
)}
Llega el viernes 5 de marzo entre 10:00 y 18:00 a {selectedAddress?.area}.
)} {step === 1 && ( <>

Método de pago

Tus datos están cifrados. Nunca guardamos el CVV.

{/* Method tabs */}
{[ { id: "card", label: "Tarjeta guardada", icon: "credit_card" }, { id: "newCard",label: "Tarjeta nueva", icon: "add" }, { id: "oxxo", label: "OXXO efectivo", icon: "shopping_basket" }, ].map(t => ( ))}
{paymentType === "card" && (
{CHK_CARDS.map(c => ( ))} {/* MSI selector */}

Meses sin intereses

Solo en bancos participantes
{[1, 3, 6, 9, 12].map(n => ( ))}
)} {paymentType === "newCard" && (
setNewCard({ ...newCard, number: e.target.value.replace(/\D/g, "").slice(0, 16).replace(/(\d{4})(?=\d)/g, "$1 ").trim() })} placeholder="4242 4242 4242 4242" maxLength={19} style={{ width: "100%", paddingRight: 56, fontFamily: "var(--font-mono)", letterSpacing: ".05em" }} autoComplete="cc-number" inputMode="numeric" /> {detectCardBrand(newCard.number) || "TARJETA"}
setNewCard({ ...newCard, titular: e.target.value })} placeholder="BAU ANDALON" autoComplete="cc-name" />
{ let v = e.target.value.replace(/\D/g, "").slice(0, 4); if (v.length > 2) v = v.slice(0, 2) + "/" + v.slice(2); setNewCard({ ...newCard, exp: v }); }} placeholder="MM/AA" maxLength={5} autoComplete="cc-exp" inputMode="numeric" style={{ fontFamily: "var(--font-mono)", letterSpacing: ".05em" }} /> setNewCard({ ...newCard, cvv: e.target.value.replace(/\D/g, "").slice(0, 4) })} placeholder="•••" maxLength={4} autoComplete="cc-csc" inputMode="numeric" style={{ fontFamily: "var(--font-mono)", letterSpacing: ".05em" }} />
)} {paymentType === "oxxo" && (
OXXO
Al confirmar generamos una ficha de pago con código de barras. Tienes 48 horas para pagar en cualquier OXXO. Tu pedido empieza a procesarse cuando confirmamos el pago (en 1-2 horas).
Comisión de OXXO incluida en el total: $12 MXN
)} )} {step === 2 && ( <>

Revisa tu pedido

Está todo listo. Confirma para pagar.

setStep(0)}>
{selectedAddress.name}
{selectedAddress.street}
{selectedAddress.area} · CP {selectedAddress.zip}
setStep(1)}> {paymentType === "card" && ( <>
{selectedCard.brand} •••• {selectedCard.last4}
{msi > 1 &&
{msi} meses sin intereses · ${Math.round(total / msi).toLocaleString("es-MX")}/mes
} )} {paymentType === "newCard" && (
•••• {newCard.number.slice(-4)} {newCard.saveCard ? "(se guardará)" : ""}
)} {paymentType === "oxxo" && (
OXXO efectivo · ficha al confirmar
)}
{cart.map(it => (
{it.p.name}
Cantidad: {it.qty}
${(it.p.price * it.qty).toLocaleString("es-MX")}
))}
)} {/* Footer buttons */}
{processing ? ( Procesando… ) : step === 2 ? `Pagar $${total.toLocaleString("es-MX")}` : "Continuar"}
{/* RIGHT — sticky summary */}
); }; // ===================================================================== // Helpers const Radio = ({ checked, onChange }) => ( ); const ReviewBlock = ({ title, icon, onEdit, children }) => (

{title}

{onEdit && }
{children}
); const SumLine = ({ k, v, color }) => (
{k} {v}
); const Spinner = () => ( ); const detectCardBrand = (num) => { const n = num.replace(/\s/g, ""); if (/^4/.test(n)) return "VISA"; if (/^5[1-5]/.test(n) || /^2[2-7]/.test(n)) return "MASTERCARD"; if (/^3[47]/.test(n)) return "AMEX"; if (/^6/.test(n)) return "DISCOVER"; return null; }; window.CheckoutPage = CheckoutPage;