import { router } from "expo-router"; import { useEffect, useMemo, useState } from "react"; import { Alert, Linking, Pressable, ScrollView, View } from "react-native"; import { useTranslation } from "react-i18next"; import { AssistedOrderExplainer, EntitlementSplitCard, LoyaltyRedeemRow, PaymentMethodList, paymentLabel, RxCartGate, } from "@/components/checkout"; import { Button } from "@/components/ui/Button"; import { Card, InfoRow } from "@/components/ui/Card"; import { Chip, Segmented } from "@/components/ui/Chip"; import { Field } from "@/components/ui/Field"; import { AppHeader } from "@/components/ui/Header"; import { Screen } from "@/components/ui/Screen"; import { StickyBar } from "@/components/ui/StickyBar"; import { Text } from "@/components/ui/Text"; import { initiatePayment } from "@/lib/api/client"; import { DELIVERY_FEE, generatePickupCode, orderSubtotalTotal, voucherDiscount, } from "@/lib/commerce"; import { branches } from "@/lib/data/branches"; import { routes } from "@/lib/routes"; import { useAddressStore } from "@/lib/stores/addressStore"; import { useAuthStore } from "@/lib/stores/authStore"; import { useCartStore } from "@/lib/stores/cartStore"; import { useFamilyStore } from "@/lib/stores/familyStore"; import { useLocationStore, type PaymentMethod } from "@/lib/stores/locationStore"; import { useNotificationStore } from "@/lib/stores/notificationStore"; import { useOrdersStore } from "@/lib/stores/ordersStore"; import { usePrescriptionStore } from "@/lib/stores/prescriptionStore"; import { useProfileModeStore } from "@/lib/stores/profileModeStore"; import { colors, gutter } from "@/lib/theme"; import { formatETB, WHATSAPP_NUMBER } from "@/lib/utils"; export default function CheckoutScreen() { const { t } = useTranslation(); const items = useCartStore((s) => s.items); const clear = useCartStore((s) => s.clear); const subtotal = useCartStore((s) => s.subtotal()); const cartVoucher = useCartStore((s) => s.voucherCode); const setCartVoucher = useCartStore((s) => s.setVoucher); const fulfillment = useLocationStore((s) => s.fulfillment); const setFulfillment = useLocationStore((s) => s.setFulfillment); const branchId = useLocationStore((s) => s.branchId); const setBranchId = useLocationStore((s) => s.setBranchId); const areaLabel = useLocationStore((s) => s.areaLabel); const setAreaLabel = useLocationStore((s) => s.setAreaLabel); const payment = useLocationStore((s) => s.paymentPreference); const setPayment = useLocationStore((s) => s.setPaymentPreference); const allowedPayments = useLocationStore((s) => s.allowedPayments); const addresses = useAddressStore((s) => s.addresses); const defaultAddress = useAddressStore((s) => s.defaultAddress()); const addOrder = useOrdersStore((s) => s.addOrder); const pushNotification = useNotificationStore((s) => s.push); const addPoints = useAuthStore((s) => s.addPoints); const redeemPoints = useAuthStore((s) => s.redeemPoints); const user = useAuthStore((s) => s.user); const setActiveVoucher = useAuthStore((s) => s.setActiveVoucher); const quoteSplit = useProfileModeStore((s) => s.quoteSplit); const consumeAllowance = useProfileModeStore((s) => s.consumeAllowance); const mode = useProfileModeStore((s) => s.mode); const membership = useProfileModeStore((s) => s.activeMembership()); const familyMembers = useFamilyStore((s) => s.members); const activeMemberId = useFamilyStore((s) => s.activeMemberId); const setActiveMember = useFamilyStore((s) => s.setActiveMember); const prescriptions = usePrescriptionStore((s) => s.prescriptions); const isGuest = !!user?.isGuest; const [name, setName] = useState(user?.name && !user.isGuest ? user.name : ""); const [phone, setPhone] = useState(user?.phone ?? ""); const [notes, setNotes] = useState(""); const [addressId, setAddressId] = useState(defaultAddress?.id ?? ""); const [voucher, setVoucher] = useState(cartVoucher || user?.activeVoucher || ""); const [forMemberId, setForMemberId] = useState(activeMemberId); const [anyBranch, setAnyBranch] = useState(isGuest); const [paying, setPaying] = useState(false); const [assistedMode, setAssistedMode] = useState(isGuest); useEffect(() => { if (!isGuest) return; if (fulfillment !== "pickup") setFulfillment("pickup"); setAnyBranch(true); setAssistedMode(true); if (payment !== "pay_at_branch") setPayment("pay_at_branch"); }, [isGuest, fulfillment, payment, setFulfillment, setPayment]); const selectedAddress = useMemo( () => addresses.find((a) => a.id === addressId) ?? defaultAddress, [addresses, addressId, defaultAddress] ); const effectiveFulfillment = isGuest || assistedMode ? "pickup" : fulfillment; const useAnyBranchPickup = effectiveFulfillment === "pickup" && (anyBranch || isGuest || assistedMode); const allowed = useMemo((): PaymentMethod[] => { if (assistedMode || isGuest) return ["pay_at_branch"]; return allowedPayments(); }, [assistedMode, isGuest, allowedPayments, fulfillment]); const rxItems = items.filter((i) => i.requiresPrescription); const hasBlockingRx = rxItems.length > 0 && !prescriptions.some((p) => ["submitted", "under_review", "approved", "filled", "queried"].includes(p.status) ); const deliveryFee = effectiveFulfillment === "delivery" && items.length ? DELIVERY_FEE : 0; const discount = voucherDiscount(subtotal, voucher); const afterDiscount = Math.max(0, subtotal - discount); const split = mode === "corporate" && !isGuest ? quoteSplit(afterDiscount) : { covered: 0, selfPay: afterDiscount }; const totalDue = orderSubtotalTotal({ subtotal: split.selfPay, deliveryFee, discount: 0, }); const placeOrder = async (opts: { assisted: boolean }) => { if (!items.length || paying) return; if (!name.trim() || phone.trim().length < 9) { Alert.alert("Contact details", "Enter your name and a valid phone number."); return; } if (hasBlockingRx && !opts.assisted) { Alert.alert( "Prescription needed", "Upload a prescription for Rx items before paying online." ); return; } if (!opts.assisted && !allowed.includes(payment)) { Alert.alert("Payment", "Select a valid payment method."); return; } if (effectiveFulfillment === "pickup" && !useAnyBranchPickup && !branchId) { Alert.alert("Pickup", "Select a branch, or choose any-branch quick code."); return; } if (effectiveFulfillment === "delivery" && !selectedAddress && !areaLabel.trim()) { Alert.alert("Delivery", "Choose a saved address or enter an area."); return; } const branch = branches.find((b) => b.id === branchId); const addressLine = selectedAddress ? `${selectedAddress.street}${selectedAddress.landmark ? ` · ${selectedAddress.landmark}` : ""}` : undefined; const forMember = familyMembers.find((m) => m.id === forMemberId); const assisted = opts.assisted || isGuest; const payMethod: PaymentMethod = assisted ? "pay_at_branch" : payment; const orderNotes = [ notes.trim(), forMember ? `For family: ${forMember.name} (${forMember.relationship})` : "", assisted ? "Pharmacist assisted order code — enter in admin to fulfill" : useAnyBranchPickup ? "Quick code — redeemable at any Gishen branch" : "", ] .filter(Boolean) .join(" · "); const pickupCode = assisted || effectiveFulfillment === "pickup" ? generatePickupCode() : null; const fee = effectiveFulfillment === "pickup" ? 0 : deliveryFee; setPaying(true); try { if (!assisted && payMethod !== "pay_at_branch" && payMethod !== "cod") { const intent = await initiatePayment({ provider: payMethod, amountEtb: afterDiscount + fee, orderId: `pending_${Date.now()}`, }); if (intent.status === "requires_action" && intent.checkoutUrl) { await Linking.openURL(intent.checkoutUrl).catch(() => undefined); } } const order = addOrder({ items, subtotal, deliveryFee: fee, discount, voucherCode: discount ? voucher.trim().toUpperCase() : null, total: afterDiscount + fee, fulfillment: effectiveFulfillment, branchId: useAnyBranchPickup ? null : branchId, areaLabel: useAnyBranchPickup ? "Any Gishen branch" : effectiveFulfillment === "pickup" ? branch?.name ?? areaLabel : selectedAddress?.area ?? areaLabel, addressId: effectiveFulfillment === "delivery" ? selectedAddress?.id ?? null : null, addressLine: useAnyBranchPickup ? undefined : addressLine, paymentMethod: payMethod, corporateCovered: split.covered, selfPay: split.selfPay + fee, customerName: name.trim(), customerPhone: phone.trim(), notes: orderNotes, riderAssigned: false, pickupCode, anyBranch: useAnyBranchPickup || assisted, isGuestOrder: assisted, status: assisted ? "requested" : payMethod === "pay_at_branch" || payMethod === "cod" || payMethod === "bank" ? "confirmed" : "confirmed", }); if (forMemberId) setActiveMember(forMemberId); if (!assisted && split.covered > 0) consumeAllowance(split.covered); if (!assisted && !isGuest) { addPoints(Math.max(1, Math.floor(afterDiscount / 10)), "Order reward"); } if (discount) { setActiveVoucher(null); setCartVoucher(null); } clear(); pushNotification({ kind: "order", title: assisted ? `Pharmacist code ${pickupCode}` : pickupCode ? `Pickup code ${pickupCode}` : "Order placed", body: assisted ? `Share ${pickupCode} so a pharmacist can create your order in admin.` : pickupCode ? useAnyBranchPickup ? `Show ${pickupCode} at any Gishen branch counter.` : `Show ${pickupCode} at ${branch?.name ?? "your branch"}.` : `Your ${order.fulfillment} order is confirmed.`, href: assisted && pickupCode ? routes.orderCode(order.id) : routes.orderDetail(order.id), }); if (assisted && pickupCode) { router.replace(routes.orderCode(order.id) as never); return; } router.replace(routes.checkoutSuccess(order.id) as never); } catch (e) { Alert.alert("Payment failed", e instanceof Error ? e.message : "Try again."); } finally { setPaying(false); } }; const whatsapp = () => { const lines = items .map((i) => `• ${i.name} x${i.quantity} — ${i.price * i.quantity} ETB`) .join("%0A"); const msg = `Gishen order%0AName: ${name}%0APhone: ${phone}%0A${lines}%0ATotal: ${totalDue} ETB%0A${fulfillment}%0A${notes}`; Linking.openURL(`https://wa.me/${WHATSAPP_NUMBER}?text=${msg}`); }; if (!items.length) { return ( Your cart is empty. Add products before checking out.