Guests can generate a shareable GSH code for admin fulfillment; sign-in now supports Google, email, phone, and Telegram demos, with the backend spec updated. Co-authored-by: Cursor <cursoragent@cursor.com>
576 lines
22 KiB
TypeScript
576 lines
22 KiB
TypeScript
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 { Button } from "@/components/ui/Button";
|
||
import { Card, InfoRow } from "@/components/ui/Card";
|
||
import { Chip, Segmented } from "@/components/ui/Chip";
|
||
import { Field, OptionRow } 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 { 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 { 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 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 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 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<string | null>(activeMemberId);
|
||
/** Guests (and optional pickup) can fulfill at any branch with a quick code */
|
||
const [anyBranch, setAnyBranch] = useState(isGuest);
|
||
|
||
useEffect(() => {
|
||
if (!isGuest) return;
|
||
if (fulfillment !== "pickup") setFulfillment("pickup");
|
||
setAnyBranch(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 ? "pickup" : fulfillment;
|
||
const useAnyBranchPickup =
|
||
effectiveFulfillment === "pickup" && (anyBranch || isGuest);
|
||
|
||
const allowed = useMemo((): PaymentMethod[] => {
|
||
const base: PaymentMethod[] = ["chapa", "telebirr", "mpesa"];
|
||
return effectiveFulfillment === "pickup"
|
||
? [...base, "pay_at_branch"]
|
||
: base;
|
||
}, [effectiveFulfillment]);
|
||
|
||
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 total = orderSubtotalTotal({
|
||
subtotal: split.selfPay,
|
||
deliveryFee,
|
||
discount: 0,
|
||
});
|
||
|
||
const pay = () => {
|
||
if (!items.length) return;
|
||
if (!name.trim() || phone.trim().length < 9) {
|
||
Alert.alert("Contact details", "Enter your name and a valid phone number.");
|
||
return;
|
||
}
|
||
if (!allowed.includes(payment) && !(useAnyBranchPickup && payment === "pay_at_branch")) {
|
||
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 orderNotes = [
|
||
notes.trim(),
|
||
forMember ? `For family: ${forMember.name} (${forMember.relationship})` : "",
|
||
isGuest
|
||
? "Pharmacist order code — enter in admin to create the order"
|
||
: useAnyBranchPickup
|
||
? "Quick code — redeemable at any Gishen branch"
|
||
: "",
|
||
]
|
||
.filter(Boolean)
|
||
.join(" · ");
|
||
|
||
const pickupCode =
|
||
isGuest || effectiveFulfillment === "pickup"
|
||
? generatePickupCode()
|
||
: null;
|
||
const fee = effectiveFulfillment === "pickup" ? 0 : deliveryFee;
|
||
|
||
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: payment,
|
||
corporateCovered: split.covered,
|
||
selfPay: split.selfPay + fee,
|
||
customerName: name.trim(),
|
||
customerPhone: phone.trim(),
|
||
notes: orderNotes,
|
||
riderAssigned: false,
|
||
pickupCode,
|
||
anyBranch: useAnyBranchPickup || isGuest,
|
||
isGuestOrder: isGuest,
|
||
status: isGuest
|
||
? "requested"
|
||
: useAnyBranchPickup || payment === "pay_at_branch"
|
||
? "confirmed"
|
||
: undefined,
|
||
});
|
||
|
||
if (forMemberId) setActiveMember(forMemberId);
|
||
|
||
if (!isGuest && split.covered > 0) consumeAllowance(split.covered);
|
||
if (!isGuest) addPoints(Math.max(1, Math.floor(afterDiscount / 10)), "Order reward");
|
||
if (discount) {
|
||
setActiveVoucher(null);
|
||
setCartVoucher(null);
|
||
}
|
||
clear();
|
||
|
||
pushNotification({
|
||
kind: "order",
|
||
title: isGuest
|
||
? `Pharmacist code ${pickupCode}`
|
||
: pickupCode
|
||
? `Pickup code ${pickupCode}`
|
||
: "Order placed",
|
||
body: isGuest
|
||
? `Share ${pickupCode} so a pharmacist can create your order in admin.`
|
||
: pickupCode
|
||
? useAnyBranchPickup
|
||
? `Show ${pickupCode} at any Gishen branch counter to collect your order.`
|
||
: `Show ${pickupCode} at ${branch?.name ?? "your branch"} to collect.`
|
||
: `Your ${order.fulfillment} order is confirmed.`,
|
||
href: isGuest && pickupCode ? routes.orderCode(order.id) : routes.orderDetail(order.id),
|
||
});
|
||
|
||
if (isGuest && pickupCode) {
|
||
router.replace(routes.orderCode(order.id) as never);
|
||
return;
|
||
}
|
||
|
||
Alert.alert(
|
||
pickupCode ? "Quick pickup code ready" : "Order placed",
|
||
pickupCode
|
||
? `Your code is ${pickupCode}. ${
|
||
useAnyBranchPickup
|
||
? "Present it at any Gishen branch for fulfillment."
|
||
: `Present it at ${branch?.name ?? "the branch"}.`
|
||
}${
|
||
payment === "pay_at_branch"
|
||
? ` Pay ${formatETB(order.selfPay ?? order.total)} at the counter.`
|
||
: ""
|
||
}`
|
||
: payment === "pay_at_branch"
|
||
? `Pay ${formatETB(order.selfPay ?? order.total)} at ${branch?.name ?? "the branch"}.`
|
||
: `Mock ${labelPayment(payment, t)} payment of ${formatETB(order.total)} succeeded.`,
|
||
[
|
||
{ text: "View order", onPress: () => router.replace(routes.orderDetail(order.id)) },
|
||
{
|
||
text: pickupCode ? "Done" : "Track",
|
||
onPress: () =>
|
||
pickupCode
|
||
? router.replace(routes.orderDetail(order.id))
|
||
: router.replace(routes.tracking(order.id)),
|
||
},
|
||
]
|
||
);
|
||
};
|
||
|
||
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: ${total} ETB%0A${fulfillment}%0A${notes}`;
|
||
Linking.openURL(`https://wa.me/${WHATSAPP_NUMBER}?text=${msg}`);
|
||
};
|
||
|
||
if (!items.length) {
|
||
return (
|
||
<Screen>
|
||
<AppHeader title={t("common.checkout")} />
|
||
<View style={{ padding: gutter }}>
|
||
<Text variant="body" color={colors.muted}>
|
||
Your cart is empty. Add products before checking out.
|
||
</Text>
|
||
<Button
|
||
title="Browse shop"
|
||
style={{ marginTop: 16 }}
|
||
onPress={() => router.replace(routes.shop)}
|
||
/>
|
||
</View>
|
||
</Screen>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<Screen>
|
||
<AppHeader title={t("common.checkout")} subtitle="Confirm delivery and payment" />
|
||
<ScrollView
|
||
contentContainerStyle={{
|
||
paddingHorizontal: gutter,
|
||
paddingBottom: 140,
|
||
gap: 14,
|
||
}}
|
||
>
|
||
<Card elevated={false}>
|
||
<Text variant="h3" style={{ marginBottom: 10 }}>
|
||
Contact
|
||
</Text>
|
||
{familyMembers.length ? (
|
||
<View style={{ marginBottom: 12 }}>
|
||
<Text variant="label" color={colors.muted} style={{ marginBottom: 8 }}>
|
||
Ordering for
|
||
</Text>
|
||
<View style={{ flexDirection: "row", flexWrap: "wrap", gap: 8 }}>
|
||
<Chip
|
||
label="Myself"
|
||
selected={!forMemberId}
|
||
onPress={() => setForMemberId(null)}
|
||
size="sm"
|
||
/>
|
||
{familyMembers.map((m) => (
|
||
<Chip
|
||
key={m.id}
|
||
label={m.name}
|
||
selected={forMemberId === m.id}
|
||
onPress={() => setForMemberId(m.id)}
|
||
size="sm"
|
||
/>
|
||
))}
|
||
</View>
|
||
</View>
|
||
) : null}
|
||
<View style={{ gap: 12 }}>
|
||
<Field label="Full name" value={name} onChangeText={setName} icon="person-outline" />
|
||
<Field
|
||
label="Phone"
|
||
value={phone}
|
||
onChangeText={setPhone}
|
||
keyboardType="phone-pad"
|
||
icon="call-outline"
|
||
/>
|
||
<Field
|
||
label="Order notes"
|
||
value={notes}
|
||
onChangeText={setNotes}
|
||
icon="create-outline"
|
||
placeholder="Gate code, call on arrival…"
|
||
/>
|
||
</View>
|
||
</Card>
|
||
|
||
<Card elevated={false}>
|
||
<Text variant="h3" style={{ marginBottom: 10 }}>
|
||
Fulfillment
|
||
</Text>
|
||
{isGuest ? (
|
||
<Card tone="brand" elevated={false} style={{ marginBottom: 12 }}>
|
||
<Text variant="h3">Pharmacist order code</Text>
|
||
<Text variant="bodySm" color={colors.muted} style={{ marginTop: 6 }}>
|
||
You’ll get a GSH-XXXX code to copy, share, or screenshot. A pharmacist
|
||
enters it on the admin page to create your order — then you pay at the branch.
|
||
</Text>
|
||
</Card>
|
||
) : null}
|
||
<Segmented
|
||
value={effectiveFulfillment}
|
||
onChange={(v) => {
|
||
if (isGuest && v === "delivery") {
|
||
Alert.alert(
|
||
"Guest checkout",
|
||
"Guests use quick-code pickup at any branch. Sign in for delivery & loyalty."
|
||
);
|
||
setFulfillment("pickup");
|
||
setAnyBranch(true);
|
||
return;
|
||
}
|
||
setFulfillment(v);
|
||
if (v === "delivery") setAnyBranch(false);
|
||
}}
|
||
options={[
|
||
{ value: "delivery", label: t("common.delivery") },
|
||
{ value: "pickup", label: t("common.pickup") },
|
||
]}
|
||
/>
|
||
|
||
{effectiveFulfillment === "delivery" ? (
|
||
<View style={{ marginTop: 14, gap: 8 }}>
|
||
<View style={{ flexDirection: "row", justifyContent: "space-between" }}>
|
||
<Text variant="label" color={colors.muted}>
|
||
Saved addresses
|
||
</Text>
|
||
<Pressable onPress={() => router.push(routes.addresses)}>
|
||
<Text variant="label" color={colors.brandText}>
|
||
Manage
|
||
</Text>
|
||
</Pressable>
|
||
</View>
|
||
{addresses.map((a) => (
|
||
<Pressable
|
||
key={a.id}
|
||
onPress={() => {
|
||
setAddressId(a.id);
|
||
setAreaLabel(a.area);
|
||
}}
|
||
style={{
|
||
padding: 12,
|
||
borderRadius: 14,
|
||
borderWidth: 1.5,
|
||
borderColor: addressId === a.id ? colors.brand : colors.line,
|
||
backgroundColor: addressId === a.id ? colors.brandWash : colors.surface,
|
||
}}
|
||
>
|
||
<Text variant="h3">
|
||
{a.label}
|
||
{a.isDefault ? " · Default" : ""}
|
||
</Text>
|
||
<Text variant="caption" color={colors.subtle} style={{ marginTop: 4 }}>
|
||
{a.street}
|
||
</Text>
|
||
<Text variant="caption" color={colors.subtle}>
|
||
{a.area}
|
||
</Text>
|
||
</Pressable>
|
||
))}
|
||
<Field
|
||
label="Or type area"
|
||
value={areaLabel}
|
||
onChangeText={setAreaLabel}
|
||
icon="location-outline"
|
||
/>
|
||
</View>
|
||
) : (
|
||
<View style={{ marginTop: 14, gap: 8 }}>
|
||
<Pressable
|
||
onPress={() => {
|
||
setAnyBranch(true);
|
||
setBranchId(null);
|
||
setPayment("pay_at_branch");
|
||
}}
|
||
style={{
|
||
padding: 12,
|
||
borderRadius: 14,
|
||
borderWidth: 1.5,
|
||
borderColor: anyBranch ? colors.brand : colors.line,
|
||
backgroundColor: anyBranch ? colors.brandWash : colors.surface,
|
||
}}
|
||
>
|
||
<Text variant="h3">Any branch · quick code</Text>
|
||
<Text variant="caption" color={colors.subtle} style={{ marginTop: 4 }}>
|
||
Get a GSH-XXXX code and collect at Bole, CMC, Jemo, or any other branch
|
||
</Text>
|
||
</Pressable>
|
||
{branches.map((b) => (
|
||
<Pressable
|
||
key={b.id}
|
||
onPress={() => {
|
||
setAnyBranch(false);
|
||
setBranchId(b.id);
|
||
}}
|
||
style={{
|
||
padding: 12,
|
||
borderRadius: 14,
|
||
borderWidth: 1.5,
|
||
borderColor:
|
||
!anyBranch && branchId === b.id ? colors.brand : colors.line,
|
||
backgroundColor:
|
||
!anyBranch && branchId === b.id
|
||
? colors.brandWash
|
||
: colors.surface,
|
||
}}
|
||
>
|
||
<Text variant="h3">{b.name}</Text>
|
||
<Text variant="caption" color={colors.subtle} style={{ marginTop: 2 }}>
|
||
{b.area} · {b.hours}
|
||
</Text>
|
||
</Pressable>
|
||
))}
|
||
</View>
|
||
)}
|
||
</Card>
|
||
|
||
<Card elevated={false} padded={false}>
|
||
<View style={{ padding: 16, paddingBottom: 8 }}>
|
||
<Text variant="h3">{t("common.payment")}</Text>
|
||
{effectiveFulfillment === "delivery" ? (
|
||
<Text variant="caption" color={colors.subtle} style={{ marginTop: 4 }}>
|
||
Pay at branch is only available for pickup
|
||
</Text>
|
||
) : null}
|
||
</View>
|
||
{allowed.map((m, i) => (
|
||
<OptionRow
|
||
key={m}
|
||
title={labelPayment(m, t)}
|
||
subtitle={paymentHint(m)}
|
||
selected={payment === m}
|
||
onPress={() => setPayment(m)}
|
||
icon={paymentIcon(m)}
|
||
last={i === allowed.length - 1}
|
||
/>
|
||
))}
|
||
</Card>
|
||
|
||
<Card elevated={false}>
|
||
<Text variant="h3" style={{ marginBottom: 10 }}>
|
||
Voucher
|
||
</Text>
|
||
<Field
|
||
label="Code"
|
||
value={voucher}
|
||
onChangeText={setVoucher}
|
||
placeholder="GISHEN10"
|
||
icon="pricetag-outline"
|
||
hint={discount ? `Saving ${formatETB(discount)}` : "Try GISHEN10 for 10% off"}
|
||
/>
|
||
<View style={{ flexDirection: "row", gap: 8, marginTop: 10 }}>
|
||
<Chip label="GISHEN10" onPress={() => setVoucher("GISHEN10")} selected={voucher.toUpperCase() === "GISHEN10"} />
|
||
{user?.activeVoucher ? (
|
||
<Chip
|
||
label={`Loyalty: ${user.activeVoucher}`}
|
||
onPress={() => setVoucher(user.activeVoucher!)}
|
||
selected={voucher === user.activeVoucher}
|
||
/>
|
||
) : null}
|
||
</View>
|
||
</Card>
|
||
|
||
{mode === "corporate" && membership ? (
|
||
<Card tone="brand" elevated={false}>
|
||
<Text variant="h3">Corporate split · {membership.orgName}</Text>
|
||
<Text variant="bodySm" color={colors.muted} style={{ marginTop: 6 }}>
|
||
Plan {membership.planName} · {formatETB(membership.remainingAllowance)} left this month
|
||
</Text>
|
||
<Text variant="bodySm" style={{ marginTop: 8 }}>
|
||
Covered: {formatETB(split.covered)}
|
||
</Text>
|
||
<Text variant="bodySm">You pay: {formatETB(split.selfPay + deliveryFee)}</Text>
|
||
</Card>
|
||
) : null}
|
||
|
||
<Card elevated={false}>
|
||
<InfoRow label="Items" value={`${items.length}`} />
|
||
<InfoRow label="Subtotal" value={formatETB(subtotal)} />
|
||
{discount > 0 ? (
|
||
<InfoRow
|
||
label="Discount"
|
||
value={`- ${formatETB(discount)}`}
|
||
valueColor={colors.brandText}
|
||
/>
|
||
) : null}
|
||
<InfoRow
|
||
label={effectiveFulfillment === "delivery" ? "Delivery" : "Pickup"}
|
||
value={deliveryFee ? formatETB(deliveryFee) : "Free"}
|
||
/>
|
||
<InfoRow label={t("common.total")} value={formatETB(afterDiscount + deliveryFee)} emphasis last />
|
||
</Card>
|
||
|
||
<Button title="Order via WhatsApp" variant="outline" icon="logo-whatsapp" onPress={whatsapp} />
|
||
</ScrollView>
|
||
|
||
<StickyBar>
|
||
<View style={{ flexDirection: "row", alignItems: "center", gap: 12 }}>
|
||
<View style={{ flex: 1 }}>
|
||
<Text variant="caption" color={colors.subtle}>
|
||
Total due
|
||
</Text>
|
||
<Text variant="priceLg">{formatETB(afterDiscount + deliveryFee)}</Text>
|
||
</View>
|
||
<Button
|
||
title={isGuest ? "Get pharmacist code" : t("common.payNow")}
|
||
fullWidth={false}
|
||
style={{ minWidth: 140 }}
|
||
onPress={pay}
|
||
/>
|
||
</View>
|
||
</StickyBar>
|
||
</Screen>
|
||
);
|
||
}
|
||
|
||
function labelPayment(m: PaymentMethod, t: (k: string) => string) {
|
||
if (m === "chapa") return t("common.chapa");
|
||
if (m === "telebirr") return t("common.telebirr");
|
||
if (m === "mpesa") return t("common.mpesa");
|
||
return t("common.payAtBranch");
|
||
}
|
||
|
||
function paymentHint(m: PaymentMethod) {
|
||
if (m === "chapa") return "Card / mobile money via Chapa";
|
||
if (m === "telebirr") return "Ethio telecom Telebirr";
|
||
if (m === "mpesa") return "Safaricom M-Pesa";
|
||
return "Cash or POS when you collect";
|
||
}
|
||
|
||
function paymentIcon(m: PaymentMethod): keyof typeof import("@expo/vector-icons").Ionicons.glyphMap {
|
||
if (m === "pay_at_branch") return "storefront-outline";
|
||
if (m === "telebirr") return "phone-portrait-outline";
|
||
if (m === "mpesa") return "cash-outline";
|
||
return "card-outline";
|
||
}
|