chargingPage.tsx 15 KB

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