chargingPage.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. import { View, Text, ActivityIndicator, AppState } from 'react-native';
  2. import React, { useCallback, useEffect, useRef, useState } from 'react';
  3. import ChargingPageComponent from '../../../../component/chargingPage/chargingPageComponent';
  4. import { chargeStationService } from '../../../../service/chargeStationService';
  5. import ChargingPenaltyPageComponent from '../../../../component/chargingPage/chargingPenaltyComponent';
  6. import NoChargingOngoingPageComponent from '../../../../component/chargingPage/noChargingOngoingPageComponent';
  7. import { useFocusEffect } from 'expo-router';
  8. import ChargingHurryUpPageComponent from '../../../../component/chargingPage/chargingHurryUpPageComponent';
  9. import FutureReservationPageComponent from '../../../../component/chargingPage/futureReservationPageComponent';
  10. const fakeData = [
  11. {
  12. createdAt: '2024-08-26T10:15:30.123Z',
  13. updatedAt: '2024-08-26T10:15:30.123Z',
  14. id: 'a1b2c3d4-e5f6-7890-abcd-1234567890ab',
  15. book_time: '2024-08-26T11:00:00.000Z',
  16. end_time: '2024-08-26T13:00:00.000Z',
  17. actual_start_time: '2024-08-26T11:05:23.456Z',
  18. actual_end_time: '2024-08-26T12:01:23.456Z',
  19. total_power: null,
  20. type: 'charging',
  21. penalty_fee: null,
  22. total_fee: 150,
  23. snapshot:
  24. '{"type":"charging","stationID":"2408261122116801000","connector":"101708260802474001","user":"user123-456-789","book_time":"2024-08-26T11:00:00.000Z","end_time":"2024-08-26T13:00:00.000Z","total_fee":150,"car":"car123-456-789"}',
  25. remark: null,
  26. promotion_name: null,
  27. promotion_msg: null,
  28. format_order_id: '730998640260820241015301234',
  29. Soc: 45,
  30. connector: {
  31. createdAt: '2024-08-01T09:00:00.000Z',
  32. updatedAt: '2024-08-26T10:00:00.000Z',
  33. id: 'conn123-456-789',
  34. ConnectorID: '101708260802474001',
  35. VoltageLowerLimits: 380,
  36. ConnectorType: 4,
  37. VoltageUpperLimits: 1000,
  38. NationalStandard: 2015,
  39. ConnectorName: 'A枪',
  40. Current: 250,
  41. Power: 180000,
  42. Status: 'charging',
  43. ParkStatus: 'occupied',
  44. LockStatus: 'locked',
  45. EquipmentID: {
  46. createdAt: '2024-08-01T09:00:00.000Z',
  47. updatedAt: '2024-08-26T10:00:00.000Z',
  48. id: 'equip123-456-789',
  49. EquipmentID: '1017082608024740',
  50. EquipmentLat: 22.310709,
  51. EquipmentLng: 114.22301,
  52. EquipmentType: 1,
  53. EquipmentName: '1桩',
  54. EquipmentModel: 'GCE-D180-Y-F',
  55. Power: 180000,
  56. StationID: {
  57. createdAt: '2024-08-01T09:00:00.000Z',
  58. updatedAt: '2024-08-26T10:00:00.000Z',
  59. id: '2408261122116801000',
  60. snapshot:
  61. '{"StationName":"CRAZY CHARGE (Example Street)","Address":"123 Example Street, Hong Kong"}',
  62. self_active_status_fk: 'active',
  63. business_hours_fk: '24/7',
  64. qr_code: 'qr_code_data',
  65. image: 'station_image_url',
  66. price: 3
  67. }
  68. }
  69. },
  70. user: {
  71. createdAt: '2024-01-01T00:00:00.000Z',
  72. updatedAt: '2024-08-26T10:00:00.000Z',
  73. id: 'user123-456-789',
  74. firstname: 'John',
  75. lastname: 'Doe',
  76. nickname: 'JD',
  77. email: 'john.doe@example.com',
  78. password: '$2b$10$hashedpasswordexample',
  79. phone: 12345678,
  80. ic_card: '0000001234567890',
  81. wallet: 5000,
  82. icon_url: 'user_icon_url',
  83. remark: null,
  84. address: '456 User Street, Hong Kong',
  85. status_fk: '1',
  86. gender: 'man',
  87. birthday: '1990/01/01',
  88. ic_car_id: null
  89. },
  90. status: {
  91. id: '2',
  92. createdAt: '2024-07-16T14:58:40.630Z',
  93. updatedAt: '2024-07-16T14:58:40.630Z',
  94. description: 'charging'
  95. },
  96. car: {
  97. createdAt: '2024-01-01T00:00:00.000Z',
  98. updatedAt: '2024-08-26T10:00:00.000Z',
  99. id: 'car123-456-789',
  100. license_plate: 'AB1234',
  101. car_brand: {
  102. createdAt: '2024-01-01T00:00:00.000Z',
  103. updatedAt: '2024-01-01T00:00:00.000Z',
  104. id: 'brand123-456-789',
  105. name: 'BWD',
  106. img_url: 'tesla_logo_url'
  107. },
  108. car_type: {
  109. createdAt: '2024-01-01T00:00:00.000Z',
  110. updatedAt: '2024-01-01T00:00:00.000Z',
  111. id: 'type123-456-789',
  112. name: 'Model 3',
  113. capacitance: 75,
  114. capacitance_unit: 'kwh',
  115. type_image_url: 'model3_image_url'
  116. }
  117. }
  118. }
  119. ];
  120. const ChargingPage = () => {
  121. const [data, setData] = useState();
  122. const [isLoading, setIsLoading] = useState(false);
  123. const [currentStatus, setCurrentStatus] = useState('');
  124. const intervalRef = useRef(null);
  125. const lastUpdateTimeRef = useRef(Date.now());
  126. // const fetchReservationData = useCallback(async () => {
  127. // setIsLoading(true);
  128. // try {
  129. // const now = new Date();
  130. // const response = await chargeStationService.fetchReservationHistories();
  131. // //先睇有無預約
  132. // if (Object.keys(response).length === 0) {
  133. // console.log('no reservation data');
  134. // setCurrentStatus('noReservation');
  135. // } else {
  136. // //二睇有無正在進行中 已插槍的預約
  137. // const onGoingReservation = response.filter((r) => r.status.id === '7');
  138. // if (onGoingReservation.length > 0) {
  139. // setCurrentStatus('onGoingReservation');
  140. // setData(onGoingReservation);
  141. // } else {
  142. // //三睇有無正在進行中的預約但仍未開始插槍充電的預約
  143. // //example: 訂單在10點開始, 現時為10點05分,用戶仍未插槍。
  144. // const recentlyPassedReservations = response.filter((r) => {
  145. // const bookTime = new Date(r.book_time);
  146. // const fifteenMinutesAfterBookTime = new Date(bookTime.getTime() + 15 * 60 * 1000);
  147. // const isWithin15MinutesAndStatus6 =
  148. // now > bookTime && now <= fifteenMinutesAfterBookTime && r.status.id === '6';
  149. // return isWithin15MinutesAndStatus6;
  150. // });
  151. // if (recentlyPassedReservations.length > 0) {
  152. // console.log(' i am recentlyPassedReservation', recentlyPassedReservations);
  153. // setCurrentStatus('recentlyPassedReservations');
  154. // setData(recentlyPassedReservations);
  155. // } else {
  156. // //睇未來最近一次的預約
  157. // const futureReservation = response.filter((r) => {
  158. // const bookTime = new Date(r.book_time);
  159. // const fifteenMinutesAfterBookTime = new Date(bookTime.getTime() + 15 * 60 * 1000);
  160. // return now < bookTime || (now > bookTime && now <= fifteenMinutesAfterBookTime);
  161. // });
  162. // if (futureReservation.length > 0) {
  163. // futureReservation.sort((a, b) => new Date(a.end_time) - new Date(b.end_time));
  164. // const closestReservation = futureReservation[0];
  165. // console.log('i am closest reservation', closestReservation);
  166. // setCurrentStatus('futureReservation');
  167. // setData(closestReservation);
  168. // }
  169. // }
  170. // }
  171. // }
  172. // } catch (error) {
  173. // console.error('Error fetching reservation histories:', error);
  174. // } finally {
  175. // setIsLoading(false);
  176. // }
  177. // }, []);
  178. const fetchReservationData = useCallback(async () => {
  179. setIsLoading(true);
  180. try {
  181. const now = new Date();
  182. const response = await chargeStationService.fetchReservationHistories();
  183. // console.log('response', response);
  184. lastUpdateTimeRef.current = Date.now();
  185. // Check if there are any reservations
  186. if (Object.keys(response).length === 0) {
  187. console.log('no reservation data');
  188. setCurrentStatus('noReservation');
  189. } else {
  190. // Check for penalty reservations
  191. const penaltyReservation = response.filter(
  192. (r) => r.actual_start_time != null && r.actual_end_time != null && r.connector.Status === 7
  193. );
  194. if (penaltyReservation.length > 0) {
  195. setCurrentStatus('penaltyReservation');
  196. setData(penaltyReservation);
  197. } else {
  198. // Check for ongoing reservations (已插槍的預約)
  199. const onGoingReservation = response.filter((r) => r.status.id === '7');
  200. console.log('onGoingReservation', onGoingReservation);
  201. if (onGoingReservation.length > 0) {
  202. setCurrentStatus('onGoingReservation');
  203. setData(onGoingReservation);
  204. } else {
  205. // Check for recently passed reservations (仍未開始插槍充電的預約)
  206. const recentlyPassedReservations = response.filter((r) => {
  207. const bookTime = new Date(r.book_time);
  208. const fifteenMinutesAfterBookTime = new Date(bookTime.getTime() + 15 * 60 * 1000);
  209. const isWithin15MinutesAndStatus6 =
  210. now > bookTime && now <= fifteenMinutesAfterBookTime && r.status.id === '6';
  211. return isWithin15MinutesAndStatus6;
  212. });
  213. if (recentlyPassedReservations.length > 0) {
  214. console.log(' i am recentlyPassedReservation', recentlyPassedReservations);
  215. setCurrentStatus('recentlyPassedReservations');
  216. setData(recentlyPassedReservations);
  217. } else {
  218. const futureReservation = response.filter((r) => {
  219. const bookTime = new Date(r.book_time);
  220. const fifteenMinutesAfterBookTime = new Date(bookTime.getTime() + 15 * 60 * 1000);
  221. return now < bookTime || (now > bookTime && now <= fifteenMinutesAfterBookTime);
  222. });
  223. if (futureReservation.length > 0) {
  224. futureReservation.sort((a, b) => new Date(a.end_time) - new Date(b.end_time));
  225. const closestReservation = futureReservation[0];
  226. setCurrentStatus('futureReservation');
  227. setData(closestReservation);
  228. } else {
  229. //if you are here, it means there are no future reservations (but there are past reservations)
  230. setCurrentStatus('noReservation');
  231. }
  232. }
  233. }
  234. }
  235. }
  236. } catch (error) {
  237. console.error('Error fetching reservation histories:', error);
  238. } finally {
  239. setIsLoading(false);
  240. }
  241. }, []);
  242. const checkAndUpdateData = useCallback(() => {
  243. const currentTime = Date.now();
  244. const timeSinceLastUpdate = currentTime - lastUpdateTimeRef.current;
  245. if (timeSinceLastUpdate > 60000) {
  246. // If more than a minute has passed
  247. fetchReservationData();
  248. }
  249. }, [fetchReservationData]);
  250. useEffect(() => {
  251. const subscription = AppState.addEventListener('change', (nextAppState) => {
  252. if (nextAppState === 'active') {
  253. checkAndUpdateData();
  254. }
  255. });
  256. return () => {
  257. subscription.remove();
  258. };
  259. }, [checkAndUpdateData]);
  260. useEffect(() => {
  261. fetchReservationData();
  262. intervalRef.current = setInterval(fetchReservationData, 60000);
  263. return () => {
  264. if (intervalRef.current) {
  265. clearInterval(intervalRef.current);
  266. }
  267. };
  268. }, [fetchReservationData]);
  269. useFocusEffect(
  270. useCallback(() => {
  271. let isActive = true;
  272. const fetchData = async () => {
  273. try {
  274. await fetchReservationData();
  275. } catch (error) {
  276. console.error('Error in useFocusEffect:', error);
  277. }
  278. };
  279. fetchData();
  280. // Cleanup function
  281. return () => {
  282. isActive = false;
  283. // Any additional cleanup logic can go here
  284. };
  285. }, [fetchReservationData])
  286. );
  287. if (isLoading) {
  288. return (
  289. <View className="flex-1 justify-center">
  290. <ActivityIndicator color="#34657b" size="large" />
  291. </View>
  292. );
  293. }
  294. return (
  295. <View className="flex-1">
  296. {currentStatus === 'noReservation' && <NoChargingOngoingPageComponent />}
  297. {currentStatus === 'penaltyReservation' && <ChargingPenaltyPageComponent data={data} />}
  298. {currentStatus === 'onGoingReservation' && <ChargingPageComponent data={data} />}
  299. {currentStatus === 'recentlyPassedReservations' && <ChargingHurryUpPageComponent data={data} />}
  300. {currentStatus === 'futureReservation' && <FutureReservationPageComponent data={data} />}
  301. </View>
  302. );
  303. };
  304. export default ChargingPage;