penaltyPaymentPageComponent.tsx 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import { View, Text, ScrollView, Pressable, StyleSheet, Alert } from 'react-native';
  2. import { SafeAreaView } from 'react-native-safe-area-context';
  3. import { router, useLocalSearchParams, useNavigation } from 'expo-router';
  4. import NormalButton from '../global/normal_button';
  5. import { useEffect, useState } from 'react';
  6. import { chargeStationService } from '../../service/chargeStationService';
  7. import useUserInfoStore from '../../providers/userinfo_store';
  8. import { PreviousPageBlackSvg } from '../global/SVG';
  9. const PenaltyPaymentPageComponent = () => {
  10. const params = useLocalSearchParams();
  11. const { userID } = useUserInfoStore();
  12. const navigation = useNavigation();
  13. const [leaving_time, setLeavingTime] = useState<string | null>(null)
  14. useEffect(() => {
  15. navigation.setOptions({
  16. gestureEnabled: false
  17. });
  18. }, [navigation]);
  19. useEffect(() => {
  20. chargeStationService.fetchChargingDetails(params.id.toString()).then(res => {
  21. if (res && res[0].charging.length > 0) {
  22. const { date, time } = convertBookingDateTime(res[0].charging[0].leaving_time as string);
  23. setLeavingTime(time);
  24. }
  25. })
  26. }, [params]);
  27. const convertBookingDateTime = (isoDateString: string): { date: string; time: string } => {
  28. const bookingDate = new Date(isoDateString);
  29. // Adjust to local time (+8 hours)
  30. bookingDate.setHours(bookingDate.getHours());
  31. // Format date as "MM-DD"
  32. const date = `${(bookingDate.getMonth() + 1).toString().padStart(2, '0')}-${bookingDate
  33. .getDate()
  34. .toString()
  35. .padStart(2, '0')}`;
  36. // Format time as "HH:mm"
  37. const time = `${bookingDate.getHours().toString().padStart(2, '0')}:${bookingDate
  38. .getMinutes()
  39. .toString()
  40. .padStart(2, '0')}`;
  41. return { date, time };
  42. };
  43. const calculateUserEndTime = (actualEndTimeStr: string, penaltyFee: string): string => {
  44. const actualEndTime = new Date(actualEndTimeStr);
  45. const penaltyMinutes = Math.floor(parseFloat(penaltyFee) / 3) + 15 // $3 per minute
  46. const userEndTime = new Date(actualEndTime.getTime() + penaltyMinutes * 60000); // add minutes
  47. return userEndTime.toISOString();
  48. };
  49. const { date, time } = convertBookingDateTime(params.book_time as string);
  50. const { date: actual_end_date, time: actual_end_time } = convertBookingDateTime(params.actual_end_time as string);
  51. const { time: user_end_time } = convertBookingDateTime(
  52. calculateUserEndTime(params.actual_end_time as string, params.penalty_fee as string)
  53. );
  54. const payload = {
  55. userId: userID,
  56. amount: parseFloat(params.penalty_fee as string),
  57. reservationId: params.id
  58. };
  59. const handlePayment = async () => {
  60. try {
  61. const result = await chargeStationService.payPenalty(payload);
  62. console.log('Payment result:', result);
  63. if (result.status) {
  64. Alert.alert('支付成功', '罰款已成功支付', [
  65. { text: '確認', onPress: () => router.replace('/mainPage') }
  66. ]);
  67. } else {
  68. Alert.alert('支付失敗', '餘額不足,即將跳轉到錢包', [
  69. { text: '確認', onPress: () => router.push('/(account)/(wallet)/walletPage') }
  70. ]);
  71. }
  72. } catch (error) {
  73. console.error('Payment error:', error);
  74. Alert.alert('支付錯誤', '發生錯誤,請稍後再試');
  75. }
  76. };
  77. return (
  78. <SafeAreaView style={{ flex: 1, backgroundColor: 'white' }} edges={['top', 'left', 'right']}>
  79. <ScrollView className="flex-1" showsVerticalScrollIndicator={false}>
  80. <View style={{ marginTop: 25 }} className="mx-[5%]">
  81. <Pressable
  82. onPress={() => {
  83. if (router.canGoBack()) {
  84. router.back();
  85. } else {
  86. router.replace('/mainPage');
  87. }
  88. }}
  89. >
  90. <PreviousPageBlackSvg />
  91. </Pressable>
  92. <Text className="text-3xl my-8 text-center">尚未繳付罰款的充電記錄</Text>
  93. </View>
  94. <View className="flex-1 mt-4 mx-[5%]">
  95. <View className="flex-1 flex-row items-center pb-3">
  96. <View className="flex-1 flex-column">
  97. <Text style={styles.grayColor} className="text-base">
  98. 實際充電到期時間
  99. </Text>
  100. <Text style={styles.greenColor} className="text-4xl text-center pt-2">
  101. {actual_end_time}
  102. </Text>
  103. </View>
  104. <View className="flex-1 flex-column">
  105. <Text style={styles.grayColor} className="text-base pl-7">
  106. 實際充電結束時間
  107. </Text>
  108. <Text style={styles.greenColor} className="text-4xl text-center pt-2">
  109. {leaving_time || user_end_time}
  110. </Text>
  111. </View>
  112. </View>
  113. <View className="flex-1 flex-column justify-center space-y-1 pb-3">
  114. <Text style={styles.grayColor} className="text-base">
  115. 充電日期
  116. </Text>
  117. <Text style={styles.greenColor} className="text-base">
  118. {date}
  119. </Text>
  120. </View>
  121. {/* <View className="flex-1 flex-column justify-center space-y-1 pb-3">
  122. <Text style={styles.grayColor} className="text-base">
  123. 充電地點
  124. </Text>
  125. <Text style={styles.greenColor} className="text-base ">
  126. Crazy Charge(偉業街)
  127. </Text>
  128. </View> */}
  129. <View className="flex-1 flex-column justify-center space-y-1 pb-3">
  130. <Text style={styles.grayColor} className="text-base">
  131. 罰款金額
  132. </Text>
  133. <Text style={styles.greenColor} className="text-lg ">
  134. {params.penalty_fee}
  135. </Text>
  136. </View>
  137. <View className="flex-1 flex-column justify-center space-y-1 pb-3">
  138. <Text style={styles.grayColor} className="text-base">
  139. 訂單編號
  140. </Text>
  141. <Text style={styles.greenColor} className=" ">
  142. {params.format_order_id}
  143. </Text>
  144. </View>
  145. </View>
  146. <View className="border-t mx-4 border-[#CCCCCC]"></View>
  147. <View className="flex-1 mx-[5%] mt-4 space-y-1">
  148. <View className="mt-4">
  149. <NormalButton
  150. title={
  151. <Text
  152. style={{
  153. color: 'white',
  154. fontSize: 16,
  155. fontWeight: '800'
  156. }}
  157. >
  158. 支付罰款
  159. </Text>
  160. }
  161. // onPress={handlePayment}
  162. onPress={() => {
  163. Alert.alert('將會從錢包餘額扣除款項以繳交罰款', '', [
  164. { text: '取消', style: 'cancel' },
  165. { text: '確認', onPress: handlePayment }
  166. ]);
  167. }}
  168. extendedStyle={{ padding: 24, marginTop: 24 }}
  169. />
  170. </View>
  171. </View>
  172. </ScrollView>
  173. </SafeAreaView>
  174. );
  175. };
  176. export default PenaltyPaymentPageComponent;
  177. const styles = StyleSheet.create({
  178. grayColor: {
  179. color: '#888888'
  180. },
  181. greenColor: {
  182. color: '#02677D'
  183. }
  184. });