paymentSummaryPageComponent.tsx 11 KB

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