paymentSummaryPageComponent.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  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 convertEndTime = (num1: number) => {
  71. // switch (num1) {
  72. // case 20:
  73. // return 25;
  74. // case 25:
  75. // return 30;
  76. // case 30:
  77. // return 40;
  78. // case 40:
  79. // return 45;
  80. // }
  81. // };
  82. // function convertToUTC(date, bookTime) {
  83. // const currentYear = new Date().getFullYear();
  84. // const [month, day] = date.split('/');
  85. // const fullDate = new Date(`${currentYear}-${month}-${day}T${bookTime}:00`);
  86. // fullDate.setHours(fullDate.getHours());
  87. // return fullDate;
  88. // }
  89. // const book_time = convertToUTC(date, bookTime);
  90. // const timeRequiredInMinute = convertEndTime(Number(chargingWatt.split(' ')[0]));
  91. // function findEndTime(num1, book_time) {
  92. // const date = new Date(book_time);
  93. // date.setMinutes(date.getMinutes() + num1);
  94. // return date;
  95. // }
  96. // let end_time;
  97. // if (chargingWatt === '') {
  98. // end_time = book_time;
  99. // } else {
  100. // const endingTime = findEndTime(timeRequiredInMinute, book_time);
  101. // end_time = endingTime;
  102. // }
  103. const total_fee = parseInt(finalFee, 10);
  104. function convertToUTC(date: string, time: string): Date {
  105. const [year, month, day] = date.split('-').map(Number);
  106. if (isNaN(year) || isNaN(month) || isNaN(day)) {
  107. console.error('Invalid date format:', date);
  108. return new Date(NaN); // Return invalid date
  109. }
  110. const [hours, minutes] = time.split(':').map(Number);
  111. if (isNaN(hours) || isNaN(minutes)) {
  112. console.error('Invalid time format:', time);
  113. return new Date(NaN); // Return invalid date
  114. }
  115. // Create a date in local time
  116. const localDate = new Date(year, month - 1, day, hours, minutes);
  117. // Convert to UTC
  118. const utcDate = new Date(localDate.toUTCString());
  119. return utcDate;
  120. }
  121. const handleSubmitPayment = async () => {
  122. console.log('hi');
  123. let type = 'reservation';
  124. let is_ic_call = false;
  125. const utcBookTime = convertToUTC(params.date as string, params.bookTime as string);
  126. const utcEndTime = convertToUTC(params.date as string, params.endTime as string);
  127. if (isNaN(utcBookTime.getTime()) || isNaN(utcEndTime.getTime())) {
  128. console.error('Invalid date or time');
  129. // Handle the error appropriately, maybe show an alert to the user
  130. return;
  131. }
  132. try {
  133. const response = await walletService.submitPayment(
  134. stationID,
  135. connectorID,
  136. user,
  137. utcBookTime,
  138. utcEndTime,
  139. total_power,
  140. total_fee,
  141. promotion_code,
  142. selectedCar,
  143. type,
  144. is_ic_call
  145. );
  146. if (response.status === 200 || response.status === 201) {
  147. console.log('submit payment successful');
  148. // router.push('/paymentFinishPage');
  149. router.push({
  150. pathname: '/paymentFinishPage',
  151. params: { formatOrderId: response.data.format_order_id }
  152. });
  153. } else if (response.status === 400) {
  154. console.log('400 error in paymentSummaryPageComponent');
  155. Alert.alert('餘額不足', '您的餘額不足,請充值後再試。');
  156. } else {
  157. console.log('submit payment failed:', response);
  158. }
  159. } catch (error) {
  160. console.log('submit payment error:', error);
  161. }
  162. };
  163. return (
  164. <SafeAreaView className="flex-1 bg-white" edges={['top', 'left', 'right']}>
  165. <ScrollView className="flex-1 mx-[5%]" showsVerticalScrollIndicator={false}>
  166. <View style={{ marginTop: 25 }}>
  167. <Text style={{ fontSize: 45, paddingBottom: 12 }}>付款概要</Text>
  168. <View className="flex-column">
  169. <Pressable onPress={() => router.push('selectCouponPage')}>
  170. <Text className="text-lg pb-4">優惠券</Text>
  171. {selectedCouponName === '' ? (
  172. <View
  173. style={{
  174. borderWidth: 1,
  175. padding: 20,
  176. borderRadius: 12,
  177. borderColor: '#bbbbbb'
  178. }}
  179. className="rounded-xl h-[9vh] items-center flex-row pl-6 justify-between"
  180. >
  181. <View className="flex-row items-center ">
  182. <Text className="color-[#999999] px-4 text-base">選擇優惠券</Text>
  183. </View>
  184. <View className="pr-4">
  185. <GrayRightArrowIconSvg />
  186. </View>
  187. </View>
  188. ) : (
  189. <View className="bg-[#e9f2f7] rounded-xl h-[9vh] items-center flex-row pl-6 justify-between">
  190. <View className="flex-row items-center ">
  191. <TickLogoSvg />
  192. <Text className="color-[#34667c] px-4 text-base">{selectedCouponName}</Text>
  193. </View>
  194. <View className="pr-4">
  195. <RightArrowIconSvg />
  196. </View>
  197. </View>
  198. )}
  199. </Pressable>
  200. </View>
  201. <View>
  202. <Text className="text-xl py-4">收費概要</Text>
  203. <View className="flex-row justify-between">
  204. <Text className="text-base">充電費用</Text>
  205. <Text className="text-base">HK ${finalFee}</Text>
  206. </View>
  207. {chargingWatt === '' ? (
  208. <Text style={styles.grayColor} className="text-base">
  209. 充滿停機預估費用
  210. </Text>
  211. ) : (
  212. <Text style={styles.grayColor} className="text-base">
  213. 按每度電結算:
  214. {chargingWatt?.split('~')[0]}
  215. </Text>
  216. )}
  217. <View className="h-0.5 my-3 bg-[#f4f4f4]" />
  218. <View className="flex-row justify-between ">
  219. <Text className="text-xl">總計</Text>
  220. <Text className="text-3xl">
  221. HK$
  222. {finalFee}
  223. </Text>
  224. </View>
  225. <View className="mt-4 ">
  226. <NormalButton
  227. title={
  228. <Text
  229. style={{
  230. color: 'white',
  231. fontSize: 16,
  232. fontWeight: '800'
  233. }}
  234. >
  235. 前往付款
  236. </Text>
  237. }
  238. onPress={
  239. // () => router.push('/paymentFinishPage')
  240. handleSubmitPayment
  241. }
  242. extendedStyle={{ padding: 24 }}
  243. />
  244. </View>
  245. <View className="h-8" />
  246. </View>
  247. </View>
  248. </ScrollView>
  249. </SafeAreaView>
  250. );
  251. };
  252. export default PaymentSummaryPageComponent;
  253. const styles = StyleSheet.create({
  254. grayColor: {
  255. color: '#888888'
  256. },
  257. greenColor: {
  258. color: '#02677D'
  259. }
  260. });