You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

635 lines
16 KiB

import React, { useState, useEffect } from "react";
import {
View,
Text,
TouchableOpacity,
StyleSheet,
ScrollView,
Image,
Alert,
ActivityIndicator,
} from "react-native";
import { payApi, PaymentMethodsResponse } from "../../services/api/payApi";
import fontSize from "../../utils/fontsizeUtils";
import BackIcon from "../../components/BackIcon";
import { useNavigation } from "@react-navigation/native";
import widthUtils from "../../utils/widthUtils";
import useOrderStore from "../../store/order";
import useCreateOrderStore from "../../store/createOrder";
import { useRoute, RouteProp } from "@react-navigation/native";
import useUserStore from "../../store/user";
import {
ordersApi,
OrderData,
AddressDataItem,
DomesticShippingFeeData,
} from "../../services/api/orders";
// Define route params type
type PaymentMethodRouteParams = {
countryCode?: string;
};
interface PaymentOption {
id: string;
label: string;
icon: string;
value?: string | string[];
}
interface PaymentTab {
id: string;
label: string;
options: PaymentOption[];
}
interface PaymentMethodProps {
onSelectPayment?: (paymentMethod: string) => void;
selectedPayment?: string | null;
}
const PaymentMethodItem = ({
option,
isSelected,
onSelect,
}: {
option: PaymentOption;
isSelected: boolean;
onSelect: () => void;
}) => (
<TouchableOpacity
style={[styles.paymentOption, isSelected && styles.paymentSelected]}
onPress={onSelect}
>
<View style={styles.paymentContent}>
<View style={styles.defaultPaymentContainer}>
<Text style={styles.paymentIcon}>{option.icon}</Text>
<Text style={styles.paymentLabel}>{option.label}</Text>
</View>
{Array.isArray(option.value) && option.value.length > 0 && (
<View style={styles.operatorContainer}>
{option.value.map((op: string) => (
<View key={op} style={styles.operatorBox}>
<Text style={styles.operatorText}>{op}</Text>
</View>
))}
</View>
)}
</View>
<View style={styles.radioButton}>
<View
style={[styles.radioInner, isSelected && styles.radioInnerSelected]}
/>
</View>
</TouchableOpacity>
);
export const PaymentMethod = () => {
const [tabs, setTabs] = useState<PaymentTab[]>([
{
id: "online",
label: "Online Payment",
options: [],
},
{
id: "offline",
label: "Offline Payment",
options: [],
},
]);
const [currentTab, setCurrentTab] = useState("online");
const [paymentMethods, setPaymentMethods] =
useState<PaymentMethodsResponse>();
const [selectedPayment, setSelectedPayment] = useState<string | null>(null);
const navigation = useNavigation();
const route =
useRoute<RouteProp<Record<string, PaymentMethodRouteParams>, string>>();
const [expanded, setExpanded] = useState(false);
const order = useOrderStore((state) => state.order);
const [previewOrder, setPreviewOrder] = useState<OrderData>();
const [loading, setLoading] = useState(false);
const state = useUserStore();
const { items,orderData } = useCreateOrderStore();
const toggleExpanded = () => {
setExpanded(!expanded);
};
const onSelectPayment = (paymentId: string) => {
setSelectedPayment(paymentId);
};
const getPaymentMethods = async () => {
try {
const response = await payApi.getCountryPaymentMethods();
setPaymentMethods(response);
// 设置默认支付方式选项
setTabs([
{
id: "online",
label: "Online Payment",
options: response.current_country_methods.map((method) => ({
id: method.key,
label: method.key,
icon: getPaymentIcon(method.key),
value: method.value,
})),
},
{
id: "offline",
label: "Offline Payment",
options: [],
},
]);
} catch (error) {
console.error("获取支付方式失败:", error);
}
};
const getPaymentIcon = (key: string): string => {
switch (key) {
case "Brainnel Pay(Mobile Money)":
return "💳";
case "Wave":
return "💸";
case "Paypal":
return "🅿";
case "Bank Card Payment":
return "💳";
default:
return "💰";
}
};
useEffect(() => {
getPaymentMethods();
}, []);
useEffect(() => {
setLoading(true);
if (route.params?.countryCode) {
const data = {
country_code: route.params.countryCode,
items: items,
};
ordersApi
.getOrders(data)
.then((res) => {
setPreviewOrder(res);
setLoading(false);
})
.catch(() => {
setLoading(false);
Alert.alert("Error", "Failed to get preview order");
});
}
}, [route.params?.countryCode]);
return (
<>
<View style={styles.titleContainer}>
<View style={styles.backIconContainer}>
<TouchableOpacity onPress={() => navigation.goBack()}>
<BackIcon size={20} />
</TouchableOpacity>
</View>
<Text style={styles.titleHeading}>Payment Method</Text>
</View>
{loading ? (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#f77f3a" />
</View>
) : (
<ScrollView style={styles.container}>
<View style={styles.sectionHeader}>
<Text style={styles.sectionIcon}>💳</Text>
<Text style={styles.sectionTitle}>Payment Method</Text>
</View>
{/* 选项卡 */}
<View style={styles.tabContainer}>
{tabs.map((tab) => (
<TouchableOpacity
key={tab.id}
style={[
styles.tabButton,
currentTab === tab.id && styles.tabButtonActive,
]}
onPress={() => setCurrentTab(tab.id)}
>
<Text
style={[
styles.tabText,
currentTab === tab.id && styles.tabTextActive,
]}
>
{tab.label}
</Text>
</TouchableOpacity>
))}
</View>
{/* 支付选项 */}
<View style={styles.paymentOptions}>
{tabs
.find((tab) => tab.id === currentTab)
?.options.map((option) => (
<PaymentMethodItem
key={option.id}
option={option}
isSelected={selectedPayment === option.id}
onSelect={() => onSelectPayment(option.id)}
/>
))}
</View>
{/* Order Summary Section */}
<View style={styles.section}>
<View style={styles.section1}>
<View style={styles.sectionHeader1}>
<Text style={styles.sectionIcon1}>📦</Text>
<Text style={styles.sectionTitle1}>Order Summary</Text>
</View>
<View style={styles.setOrderContent}>
<Text style={styles.noCouponsMessage}>
Products({previewOrder?.items?.length || 0} items)
</Text>
<TouchableOpacity onPress={toggleExpanded}>
<Text style={styles.sectionAction}>
{expanded ? "Hide Details" : "View Details"}
</Text>
</TouchableOpacity>
</View>
<View
style={[
styles.orderItems,
expanded && styles.orderItemsExpanded,
]}
>
{previewOrder?.items?.map((item) => (
<View key={item.sku_id} style={styles.orderItem}>
{item.sku_image_url ? (
<Image
source={{ uri: item.sku_image_url }}
style={styles.itemImage}
/>
) : (
<View style={styles.itemImagePlaceholder} />
)}
<View style={styles.itemDetails}>
<Text style={styles.itemName} numberOfLines={2}>
{item.product_name}
</Text>
{item.sku_attributes?.map((attribute) => (
<Text
style={styles.itemVariant}
key={attribute?.attribute_value}
numberOfLines={1}
>
{attribute?.attribute_name}:{" "}
{attribute?.attribute_value}
</Text>
))}
<Text style={styles.itemQuantity}>
Qty: {item.quantity}
</Text>
</View>
<View style={styles.itemPrices}>
<Text style={styles.itemPrice}>${item?.total_price}</Text>
</View>
</View>
))}
</View>
</View>
</View>
<View style={styles.priceBox}>
<View style={styles.priceBox1}>
<Text>Subtotal</Text>
<View>
<Text>{previewOrder?.total_amount || 0}</Text>
</View>
</View>
<View style={styles.priceBox1}>
<Text>Domestic Shipping</Text>
<View>
<Text>{previewOrder?.shipping_fee || 0}</Text>
</View>
</View>
<View style={styles.priceBox1}>
<Text>Estimated International Shipping</Text>
<View>
<Text>{orderData.transport_type === 1 ?previewOrder?.shipping_fee_sea : previewOrder?.shipping_fee_air || 0}</Text>
</View>
</View>
</View>
{/* 实际支付金额 */}
<View style={styles.actualPaymentBox}>
<View style={styles.actualPaymentBox1}>
<Text
style={{
fontSize: fontSize(18),
fontWeight: "600",
color: "#000",
}}
>
Total
</Text>
<Text
style={{
fontSize: fontSize(18),
fontWeight: "600",
color: "#ff6000",
}}
>
${previewOrder?.actual_amount?.toFixed(2)}
</Text>
</View>
</View>
</ScrollView>
)}
</>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
},
sectionHeader: {
flexDirection: "row",
alignItems: "center",
marginBottom: 15,
paddingHorizontal: 15,
},
sectionIcon: {
fontSize: fontSize(20),
marginRight: 8,
},
sectionTitle: {
fontSize: fontSize(18),
fontWeight: "600",
color: "#000",
},
tabContainer: {
flexDirection: "row",
marginBottom: 15,
borderBottomWidth: 1,
borderBottomColor: "#EEEEEE",
paddingHorizontal: 15,
},
tabButton: {
paddingVertical: 10,
paddingHorizontal: 15,
marginRight: 10,
},
tabButtonActive: {
borderBottomWidth: 2,
borderBottomColor: "#FF5100",
},
tabText: {
fontSize: fontSize(16),
color: "#666",
},
tabTextActive: {
color: "#FF5100",
fontWeight: "500",
},
paymentOptions: {
marginBottom: 15,
paddingHorizontal: 15,
},
paymentOption: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
padding: 15,
backgroundColor: "#F8F8F8",
borderRadius: 8,
marginBottom: 10,
},
paymentSelected: {
backgroundColor: "#FFF0E8",
borderWidth: 1,
borderColor: "#FF5100",
},
paymentContent: {
flex: 1,
},
defaultPaymentContainer: {
flexDirection: "row",
alignItems: "center",
},
paymentIcon: {
fontSize: fontSize(24),
marginRight: 8,
},
paymentLabel: {
fontSize: fontSize(16),
fontWeight: "500",
},
operatorContainer: {
flexDirection: "row",
flexWrap: "wrap",
marginTop: 8,
},
operatorBox: {
backgroundColor: "#EEEEEE",
paddingVertical: 4,
paddingHorizontal: 8,
borderRadius: 4,
marginRight: 8,
marginBottom: 4,
},
operatorText: {
fontSize: fontSize(12),
color: "#666",
},
radioButton: {
width: 20,
height: 20,
borderRadius: 10,
borderWidth: 1,
borderColor: "#CCCCCC",
justifyContent: "center",
alignItems: "center",
},
radioInner: {
width: 12,
height: 12,
borderRadius: 6,
backgroundColor: "transparent",
},
radioInnerSelected: {
backgroundColor: "#FF5100",
},
titleContainer: {
width: "100%",
padding: 15,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
position: "relative",
marginBottom: 10,
},
backIconContainer: {
position: "absolute",
left: 15,
},
titleHeading: {
fontWeight: "600",
fontSize: 20,
lineHeight: 22,
fontFamily: "PingFang SC",
color: "black",
},
// Order Summary Styles
section: {
backgroundColor: "#fff",
borderRadius: 8,
paddingHorizontal: 15,
marginTop: 15,
},
section1: {
backgroundColor: "#fff",
borderRadius: 8,
overflow: "hidden",
},
sectionHeader1: {
flexDirection: "row",
alignItems: "center",
paddingTop: 12,
paddingBottom: 12,
borderBottomWidth: 1,
borderBottomColor: "#f5f5f5",
},
sectionIcon1: {
fontSize: fontSize(18),
marginRight: 10,
color: "#666",
},
sectionTitle1: {
fontSize: fontSize(15),
fontWeight: "500",
flex: 1,
},
setOrderContent: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingTop: 12,
paddingBottom: 12,
paddingHorizontal: 12,
},
sectionAction: {
color: "#ff6000",
fontSize: fontSize(13),
fontWeight: "500",
},
noCouponsMessage: {
color: "#888",
fontSize: fontSize(13),
},
orderItems: {
maxHeight: 0,
overflow: "hidden",
},
orderItemsExpanded: {
maxHeight: 1000, // Arbitrary large number to accommodate all items
},
orderItem: {
flexDirection: "row",
padding: 16,
borderBottomWidth: 1,
borderBottomColor: "#f5f5f5",
},
itemImage: {
width: widthUtils(70, 70).width,
height: widthUtils(70, 70).height,
borderRadius: 6,
marginRight: 12,
borderWidth: 1,
borderColor: "#eee",
},
itemImagePlaceholder: {
width: widthUtils(70, 70).width,
height: widthUtils(70, 70).height,
borderRadius: 6,
marginRight: 12,
borderWidth: 1,
borderColor: "#eee",
backgroundColor: "#f1f1f1",
},
itemDetails: {
flex: 1,
},
itemName: {
fontSize: fontSize(14),
lineHeight: 18,
},
itemVariant: {
fontSize: fontSize(12),
color: "#666",
backgroundColor: "#f7f7f7",
paddingVertical: 3,
paddingHorizontal: 6,
borderRadius: 4,
marginTop: 6,
alignSelf: "flex-start",
},
itemQuantity: {
fontSize: fontSize(12),
color: "#666",
marginTop: 4,
},
itemPrices: {
alignItems: "flex-end",
fontSize: fontSize(13),
color: "#555",
},
itemPrice: {
fontWeight: "600",
color: "#ff6000",
fontSize: fontSize(15),
marginBottom: 5,
},
priceBox: {
borderRadius: 10,
marginTop: 15,
paddingHorizontal: 15,
},
priceBox1: {
justifyContent: "space-between",
flexDirection: "row",
alignItems: "center",
padding: 12,
borderBottomWidth: 1,
borderBottomColor: "#f5f5f5",
},
actualPaymentBox: {
padding: 12,
borderRadius: 6,
backgroundColor: "#fff8f4",
marginTop: 15,
marginHorizontal: 15,
marginBottom: 20,
},
actualPaymentBox1: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
loadingContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
});