chargingPageComponent.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. import { View, Text, ScrollView, StyleSheet, Image, ActivityIndicator } from 'react-native';
  2. import { SafeAreaView } from 'react-native-safe-area-context';
  3. import RippleEffectBatteryIcon from '../global/rippleEffectBatteryIcon';
  4. import LoadingDots from '../global/loadingDots';
  5. import NormalButton from '../global/normal_button';
  6. import { router } from 'expo-router';
  7. import { BatteryIconSvg, LightingLogoSvg, TemperatureIconSvg } from '../global/SVG';
  8. import { useEffect, useState } from 'react';
  9. import { chargeStationService } from '../../service/chargeStationService';
  10. import { set } from 'date-fns';
  11. import { convertToHKTime } from '../../util/lib';
  12. import ChargingPenaltyPageComponent from './chargingPenaltyComponent';
  13. const ChargingPageComponent = ({ data }) => {
  14. const reservationData = Array.isArray(data) ? data[0] : data;
  15. const [isLoading, setIsLoading] = useState(true);
  16. const [loading, setLoading] = useState(false);
  17. const [onGoingChargingData, setOnGoingChargingData] = useState();
  18. console.log('data', data);
  19. useEffect(() => {
  20. const fetchOngoingChargingData = async () => {
  21. setIsLoading(true);
  22. try {
  23. const response = await chargeStationService.fetchOngoingChargingData(reservationData.format_order_id);
  24. if (response) {
  25. setOnGoingChargingData(response);
  26. console.log('i am ongoingchargingdata', response);
  27. } else {
  28. console.log('error fetching data');
  29. }
  30. } catch (error) {
  31. console.error('Error fetching reservation histories:', error);
  32. } finally {
  33. setIsLoading(false);
  34. }
  35. };
  36. fetchOngoingChargingData();
  37. }, [reservationData]);
  38. // //////////////////////////////////////////////////////////////////////
  39. // send an automatic handleStopCharge when reservationData.end_time is now
  40. const stopPayLoad = {
  41. StartChargeSeq: reservationData.format_order_id,
  42. ConnectorID: reservationData.connector.ConnectorID
  43. };
  44. const handleStopCharge = async () => {
  45. setLoading(true);
  46. try {
  47. console.log('stopPayLoad', stopPayLoad);
  48. const response = await chargeStationService.stopCharging(stopPayLoad);
  49. if (response) {
  50. console.log('handleStopCharge begins, response received:', response);
  51. setLoading(false);
  52. } else {
  53. setLoading(false);
  54. console.log('handleStopCharge失敗');
  55. }
  56. } catch (error) {
  57. setLoading(false);
  58. console.log('handleStopCharge error', error);
  59. }
  60. };
  61. //now i make a reservation, make it a 7 and see if it comes here
  62. useEffect(() => {
  63. const now = new Date();
  64. const endTime = reservationData?.end_time ? new Date(reservationData.end_time) : null;
  65. console.log('now in chargingPageComponent', now);
  66. console.log('endTime in chargingPageComponent', endTime);
  67. console.log('Checking stop conditions:', { now, endTime, Soc: reservationData?.Soc });
  68. // Access the snapshot properties
  69. const snapshotData = reservationData?.snapshot ? JSON.parse(reservationData.snapshot) : null;
  70. console.log('snapshotData in onGoingChargingData', snapshotData);
  71. console.log('snapshot.is_ic_call', snapshotData?.is_ic_call);
  72. console.log('snapshot.type', snapshotData?.type);
  73. if (reservationData && snapshotData) {
  74. const isWalkingOrIcCall = snapshotData.type === 'walking' || snapshotData.is_ic_call === true;
  75. const shouldStopCharge = reservationData.Soc >= 95 || (!isWalkingOrIcCall && endTime && now >= endTime);
  76. if (shouldStopCharge) {
  77. console.log(
  78. 'Initiating automatic stop. Reason:',
  79. reservationData.Soc >= 95 ? 'Battery reached 95% or higher' : 'End time reached'
  80. );
  81. handleStopCharge();
  82. }
  83. }
  84. }, [reservationData]);
  85. /////////////////////////////////////////////////////////////
  86. //用來計充電歷時 //用來計充電歷時 //用來計充電歷時 //用來計充電歷時 //用來計充電歷時
  87. const [timeSince, setTimeSince] = useState<string>('');
  88. useEffect(() => {
  89. const updateTimeSince = () => {
  90. if (reservationData && reservationData.actual_start_time) {
  91. setTimeSince(timeSinceBooking(reservationData.actual_start_time) || '計算中...');
  92. } else {
  93. setTimeSince('計算中...');
  94. }
  95. };
  96. updateTimeSince();
  97. // Update every minute
  98. const intervalId = setInterval(updateTimeSince, 60000);
  99. // Cleanup interval on component unmount
  100. return () => clearInterval(intervalId);
  101. }, [reservationData]);
  102. function timeSinceBooking(timeString) {
  103. if (timeString) {
  104. const startTime = new Date(timeString);
  105. const now = new Date();
  106. const diffInMilliseconds = now - startTime;
  107. const diffInMinutes = Math.floor(diffInMilliseconds / (1000 * 60));
  108. if (diffInMinutes < 1) {
  109. return '< 1 minute';
  110. } else {
  111. return `${diffInMinutes} minute${diffInMinutes !== 1 ? 's' : ''}`;
  112. }
  113. }
  114. }
  115. //用來計充電歷時 //用來計充電歷時 //用來計充電歷時 //用來計充電歷時 //用來計充電歷時
  116. const displayKW = (currentA: number, voltageA: number) => {
  117. if (currentA && voltageA) {
  118. return (currentA * voltageA) / 1000;
  119. } else return 30.0;
  120. };
  121. if (isLoading) {
  122. return (
  123. <SafeAreaView style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
  124. <ActivityIndicator size="large" />
  125. <Text>Loading...</Text>
  126. </SafeAreaView>
  127. );
  128. }
  129. return (
  130. <SafeAreaView style={{ flex: 1, backgroundColor: 'white' }} edges={['top', 'left', 'right']}>
  131. <ScrollView className="flex-1">
  132. <View className="flex-1 mx-[5%] space-y-4">
  133. <View className="items-center">
  134. <View className="mt-6 mb-4">
  135. <Text className="text-lg ">現正充電中:</Text>
  136. </View>
  137. <Text className="text-4xl font-light">
  138. {reservationData.car.car_brand.name} {reservationData.car.car_type.name}
  139. </Text>
  140. </View>
  141. <View className="items-center">
  142. <Text className="text-lg" style={styles.grayColor}>
  143. 充電中
  144. </Text>
  145. <View className="flex-row space-x-4 p-4 pr-8 items-center justify-center ml-10">
  146. <RippleEffectBatteryIcon />
  147. <Text
  148. style={{
  149. color: '#02677D',
  150. fontSize: 60,
  151. fontWeight: 300
  152. }}
  153. >
  154. {/* 4852 */}
  155. {/* {onGoingChargingData ? `${onGoingChargingData.Soc} %` : 'Loading'} */}
  156. {`${reservationData.Soc}%`}
  157. {/* {`64%`} */}
  158. <LoadingDots />
  159. </Text>
  160. </View>
  161. {/* 尚餘時間未知點計住 comments左先
  162. <Text className="text-lg mb-6" style={styles.grayColor}>
  163. 尚餘時間 ~48 mins
  164. </Text> */}
  165. <View className="mb-[-10] items-center justify-center ">
  166. <Image
  167. source={require('../../assets/car.png')}
  168. style={{ width: 430, height: 200 }}
  169. resizeMode="contain"
  170. />
  171. </View>
  172. </View>
  173. <View
  174. className="h-[220px] min-h-[20px] border-slate-300 rounded-2xl flex-column"
  175. style={{ borderWidth: 1 }}
  176. >
  177. {/* Top */}
  178. <View className="h-[65%] flex-row justify-evenly items-center">
  179. <View className="flex-1 flex-column items-center space-y-2">
  180. <LightingLogoSvg />
  181. <Text style={styles.grayColor} className="text-base">
  182. 充電功率
  183. </Text>
  184. {isLoading ? (
  185. <ActivityIndicator />
  186. ) : (
  187. <Text style={styles.greenColor} className="font-bold text-base">
  188. {/* {displayKW(onGoingChargingData.CurrentA, onGoingChargingData.VoltageA)}kW */}
  189. {reservationData.connector.Power / 1000}kW
  190. </Text>
  191. )}
  192. </View>
  193. <View className="flex-1 flex-column items-center space-y-2">
  194. <BatteryIconSvg />
  195. <Text style={styles.grayColor} className="text-base">
  196. 實際功率
  197. </Text>
  198. {isLoading ? (
  199. <ActivityIndicator />
  200. ) : (
  201. <Text style={styles.greenColor} className="font-bold text-base">
  202. {onGoingChargingData &&
  203. onGoingChargingData.VoltageA &&
  204. onGoingChargingData.VoltageB &&
  205. onGoingChargingData.VoltageC &&
  206. onGoingChargingData.CurrentA &&
  207. onGoingChargingData.CurrentB &&
  208. onGoingChargingData.CurrentC
  209. ? (
  210. (onGoingChargingData.VoltageA * onGoingChargingData.CurrentA +
  211. onGoingChargingData.VoltageB * onGoingChargingData.CurrentB +
  212. onGoingChargingData.VoltageC * onGoingChargingData.CurrentC) /
  213. 1000
  214. ).toFixed(2) + ' kW'
  215. : '請見充電顯示螢幕'}
  216. </Text>
  217. )}
  218. </View>
  219. {/* <View className="flex-1 flex-column items-center space-y-2">
  220. <TemperatureIconSvg />
  221. <Text style={styles.grayColor} className="text-base">
  222. 溫度
  223. </Text>
  224. {isLoading ? (
  225. <ActivityIndicator />
  226. ) : (
  227. <Text style={styles.greenColor} className="font-bold text-base">
  228. 36°c
  229. </Text>
  230. )}
  231. </View> */}
  232. </View>
  233. <View className="mx-[5%]">
  234. <View className="h-[1px] w-[100%] bg-[#CCCCCC]" />
  235. </View>
  236. {/* bottom container */}
  237. <View className="h-[35%] mx-[5%] justify-center ">
  238. <Text style={styles.grayColor} className="text-base">
  239. 充電歷時 ~{timeSince}
  240. </Text>
  241. </View>
  242. </View>
  243. {/* <View
  244. className="min-h-[20px] border-slate-300 rounded-2xl justify-center p-4"
  245. style={{ borderWidth: 1 }}
  246. >
  247. <View className="flex-row items-center justify-between ">
  248. <View>
  249. <Text className="text-lg">預計充電費用</Text>
  250. <Text className="text-base" style={styles.grayColor}>
  251. 按每度電結算: 50 kWh
  252. </Text>
  253. </View>
  254. <Text className="text-3xl">HK$ 175</Text>
  255. </View>
  256. </View> */}
  257. <View className="border-slate-300 rounded-2xl justify-center p-4" style={{ borderWidth: 1 }}>
  258. <Text className="text-lg pb-1 ">其他資訊</Text>
  259. <View className="flex-row">
  260. <View className="flex-1 flex-column">
  261. <Text className="text-base" style={styles.grayColor}>
  262. 開始時間
  263. </Text>
  264. <Text className="text-base">
  265. {convertToHKTime(reservationData.actual_start_time).hkTime.slice(0, 5)}
  266. </Text>
  267. </View>
  268. <View className="flex-1 flex-column">
  269. <Text className="text-base" style={styles.grayColor}>
  270. 充電座
  271. </Text>
  272. {/* <Text className="text-base">{reservationData.connector.ConnectorID}</Text> */}
  273. <Text className="text-base">A104</Text>
  274. </View>
  275. </View>
  276. </View>
  277. <View>
  278. <NormalButton
  279. onPress={() => {
  280. router.push('mainPage');
  281. }}
  282. title={
  283. <Text className="text-xl text-white" style={{ fontWeight: 900 }}>
  284. 返回主頁
  285. </Text>
  286. }
  287. />
  288. </View>
  289. {/* <View>
  290. <NormalButton
  291. onPress={() => {
  292. router.push('/chargingPenaltyPage');
  293. }}
  294. title={
  295. <Text className="text-xl text-white" style={{ fontWeight: 900 }}>
  296. 觀看閒置/罰款狀態頁面
  297. </Text>
  298. }
  299. />
  300. </View> */}
  301. </View>
  302. </ScrollView>
  303. </SafeAreaView>
  304. );
  305. };
  306. export default ChargingPageComponent;
  307. const styles = StyleSheet.create({
  308. grayColor: {
  309. color: '#888888'
  310. },
  311. greenColor: {
  312. color: '#02677D'
  313. },
  314. text: {
  315. fontWeight: 300,
  316. color: '#000000'
  317. }
  318. });