paymentSummaryPageComponent.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. import { View, Text, ScrollView, StyleSheet, Pressable } from 'react-native';
  2. import { SafeAreaView } from 'react-native-safe-area-context';
  3. import { Alert } from 'react-native';
  4. import NormalButton from '../global/normal_button';
  5. import { router, useLocalSearchParams } from 'expo-router';
  6. import { GrayRightArrowIconSvg, RightArrowIconSvg, TickLogoSvg } from '../global/SVG';
  7. import { useEffect, useRef, useState } from 'react';
  8. import useBookingStore from '../../providers/booking_store';
  9. import useCouponStore from '../../providers/coupon_store';
  10. import { walletService } from '../../service/walletService';
  11. import { parseISO, subHours, format } from 'date-fns';
  12. import { chargeStationService } from '../../service/chargeStationService';
  13. const PaymentSummaryPageComponent = () => {
  14. const selectedCouponName = useCouponStore((state) => state.selectedCouponName);
  15. const selectedCouponRedeemCode = useCouponStore((state) => state.selectedCouponRedeemCode);
  16. const selectedCouponPrice = useCouponStore((state) => state.selectedCouponPrice);
  17. const params = useLocalSearchParams();
  18. const setBookingInfo = useBookingStore((state) => state.setBookingInfo);
  19. const initialSetupDone = useRef(false);
  20. const [selectedCar, setSelectedCar] = useState('');
  21. const [formatOrderId, setFormatOrderId] = useState('');
  22. useEffect(() => {
  23. if (!initialSetupDone.current && Object.keys(params).length > 0) {
  24. const bookingInfo = {
  25. bookTime: params.bookTime as string,
  26. endTime: params.endTime as string,
  27. date: params.date as string,
  28. carID: params.carID as string,
  29. chargingWatt: params.chargingWatt as string,
  30. connectorID: params.connectorID as string,
  31. price: params.price as string,
  32. stationID: params.stationID as string,
  33. user: params.userID as string,
  34. paymentFee: params.paymentFee as string,
  35. carCapacitance: params.carCapacitance as string
  36. };
  37. setBookingInfo(bookingInfo);
  38. console.log('Booking info stored:', bookingInfo);
  39. initialSetupDone.current = true;
  40. }
  41. }, [params, setBookingInfo]);
  42. useEffect(() => {
  43. const fetchDefaultCar = async () => {
  44. try {
  45. const response = await chargeStationService.getUserDefaultCars();
  46. if (response) {
  47. console.log('default car', response.data.id);
  48. setSelectedCar(response.data.id);
  49. }
  50. } catch (error) {
  51. console.log(error);
  52. }
  53. };
  54. fetchDefaultCar();
  55. }, []);
  56. const { bookTime, date, carID, chargingWatt, connectorID, price, stationID, user, paymentFee, carCapacitance } =
  57. useBookingStore();
  58. const calculateFinalFee = (): string => {
  59. console.log('selected coupon price:', selectedCouponPrice);
  60. if (selectedCouponRedeemCode && selectedCouponPrice) {
  61. const difference = parseFloat(paymentFee) - parseFloat(selectedCouponPrice);
  62. return Math.max(0, difference).toFixed(2);
  63. } else {
  64. return paymentFee;
  65. }
  66. };
  67. const finalFee = calculateFinalFee();
  68. const total_power = chargingWatt === '' ? carCapacitance : parseFloat(chargingWatt.split('~')[0]);
  69. const promotion_code = selectedCouponRedeemCode || '';
  70. const total_fee = parseInt(finalFee, 10);
  71. function convertToUTC(date: string, time: string): Date {
  72. const [year, month, day] = date.split('-').map(Number);
  73. if (isNaN(year) || isNaN(month) || isNaN(day)) {
  74. console.error('Invalid date format:', date);
  75. return new Date(NaN); // Return invalid date
  76. }
  77. const [hours, minutes] = time.split(':').map(Number);
  78. if (isNaN(hours) || isNaN(minutes)) {
  79. console.error('Invalid time format:', time);
  80. return new Date(NaN); // Return invalid date
  81. }
  82. // Create a date in local time
  83. const localDate = new Date(year, month - 1, day, hours, minutes);
  84. // Convert to UTC
  85. const utcDate = new Date(localDate.toUTCString());
  86. return utcDate;
  87. }
  88. const handleSubmitPayment = async () => {
  89. console.log('hi');
  90. let type = 'reservation';
  91. let is_ic_call = false;
  92. const utcBookTime = convertToUTC(params.date as string, params.bookTime as string);
  93. const utcEndTime = convertToUTC(params.date as string, params.endTime as string);
  94. if (isNaN(utcBookTime.getTime()) || isNaN(utcEndTime.getTime())) {
  95. console.error('Invalid date or time');
  96. // Handle the error appropriately, maybe show an alert to the user
  97. return;
  98. }
  99. try {
  100. const response = await walletService.submitPayment(
  101. stationID,
  102. connectorID,
  103. user,
  104. utcBookTime,
  105. utcEndTime,
  106. total_power,
  107. total_fee,
  108. promotion_code,
  109. selectedCar,
  110. type,
  111. is_ic_call
  112. );
  113. if (response.status === 200 || response.status === 201) {
  114. console.log('submit payment successful');
  115. // router.push('/paymentFinishPage');
  116. router.push({
  117. pathname: '/paymentFinishPage',
  118. params: { formatOrderId: response.data.format_order_id }
  119. });
  120. } else if (response.status === 400) {
  121. console.log('400 error in paymentSummaryPageComponent');
  122. Alert.alert('餘額不足', '您的餘額不足,請充值後再試。');
  123. } else {
  124. console.log('submit payment failed:', response);
  125. }
  126. } catch (error) {
  127. console.log('submit payment error:', error);
  128. }
  129. };
  130. return (
  131. <SafeAreaView className="flex-1 bg-white" edges={['top', 'left', 'right']}>
  132. <ScrollView className="flex-1 mx-[5%]" showsVerticalScrollIndicator={false}>
  133. <View style={{ marginTop: 25 }}>
  134. <Text style={{ fontSize: 45, paddingBottom: 12 }}>付款概要</Text>
  135. <View className="flex-column">
  136. <Pressable onPress={() => router.push('selectCouponPage')}>
  137. <Text className="text-lg pb-4">優惠券</Text>
  138. {selectedCouponName === '' ? (
  139. <View
  140. style={{
  141. borderWidth: 1,
  142. padding: 20,
  143. borderRadius: 12,
  144. borderColor: '#bbbbbb'
  145. }}
  146. className="rounded-xl h-[9vh] items-center flex-row pl-6 justify-between"
  147. >
  148. <View className="flex-row items-center ">
  149. <Text className="color-[#999999] px-4 text-base">選擇優惠券</Text>
  150. </View>
  151. <View className="pr-4">
  152. <GrayRightArrowIconSvg />
  153. </View>
  154. </View>
  155. ) : (
  156. <View className="bg-[#e9f2f7] rounded-xl h-[9vh] items-center flex-row pl-6 justify-between">
  157. <View className="flex-row items-center ">
  158. <TickLogoSvg />
  159. <Text className="color-[#34667c] px-4 text-base">{selectedCouponName}</Text>
  160. </View>
  161. <View className="pr-4">
  162. <RightArrowIconSvg />
  163. </View>
  164. </View>
  165. )}
  166. </Pressable>
  167. </View>
  168. <View>
  169. <Text className="text-xl py-4">收費概要</Text>
  170. <View className="flex-row justify-between">
  171. <Text className="text-base">充電費用</Text>
  172. <Text className="text-base">HK ${finalFee}</Text>
  173. </View>
  174. {chargingWatt === '' ? (
  175. <Text style={styles.grayColor} className="text-base">
  176. 充滿停機預估費用
  177. </Text>
  178. ) : (
  179. <Text style={styles.grayColor} className="text-base">
  180. 按每度電結算:
  181. {chargingWatt?.split('~')[0]}
  182. </Text>
  183. )}
  184. <View className="h-0.5 my-3 bg-[#f4f4f4]" />
  185. <View className="flex-row justify-between ">
  186. <Text className="text-xl">總計</Text>
  187. <Text className="text-3xl">
  188. HK$
  189. {finalFee}
  190. </Text>
  191. </View>
  192. <View className="mt-4 ">
  193. <NormalButton
  194. title={
  195. <Text
  196. style={{
  197. color: 'white',
  198. fontSize: 16,
  199. fontWeight: '800'
  200. }}
  201. >
  202. 前往付款
  203. </Text>
  204. }
  205. onPress={
  206. // () => router.push('/paymentFinishPage')
  207. handleSubmitPayment
  208. }
  209. extendedStyle={{ padding: 24 }}
  210. />
  211. </View>
  212. <View className="h-8" />
  213. </View>
  214. </View>
  215. </ScrollView>
  216. </SafeAreaView>
  217. );
  218. };
  219. export default PaymentSummaryPageComponent;
  220. const styles = StyleSheet.create({
  221. grayColor: {
  222. color: '#888888'
  223. },
  224. greenColor: {
  225. color: '#02677D'
  226. }
  227. });