chargingRecord.tsx 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. //the size of the TabView will follow its parent-container's size.
  2. import * as React from 'react';
  3. import * as Location from 'expo-location';
  4. import {
  5. View,
  6. Text,
  7. useWindowDimensions,
  8. Dimensions,
  9. StyleSheet,
  10. Image,
  11. ImageSourcePropType,
  12. ActivityIndicator,
  13. Pressable
  14. } from 'react-native';
  15. import { FlashList } from '@shopify/flash-list';
  16. import { useEffect, useState } from 'react';
  17. import { calculateDistance } from './distanceCalculator';
  18. import { router } from 'expo-router';
  19. export interface TabItem {
  20. imgURL: ImageSourcePropType;
  21. date: string;
  22. time: string;
  23. chargeStationName: string;
  24. chargeStationAddress: string;
  25. stationLat: string | number;
  26. stationLng: string | number;
  27. distance: string;
  28. format_order_id: string;
  29. actual_total_power?: number;
  30. actual_end_time?: string;
  31. actual_fee?: number;
  32. current_price?: number;
  33. id?: string;
  34. status?: any
  35. }
  36. interface TabViewComponentProps {
  37. titles: string[];
  38. tabItems: TabItem[];
  39. isLoading?: boolean;
  40. }
  41. const TabViewComponent: React.FC<TabViewComponentProps> = ({
  42. titles,
  43. isLoading,
  44. tabItems,
  45. }) => {
  46. const layout = useWindowDimensions();
  47. const [currentLocation, setCurrentLocation] = useState<Location.LocationObject | null>(null);
  48. useEffect(() => {
  49. const getCurrentLocation = async () => {
  50. let { status } = await Location.requestForegroundPermissionsAsync();
  51. if (status !== 'granted') {
  52. console.error('Permission to access location was denied');
  53. return;
  54. }
  55. let location = await Location.getLastKnownPositionAsync({});
  56. setCurrentLocation(location);
  57. };
  58. getCurrentLocation();
  59. }, []);
  60. const FirstRoute = ({ tabItems, isLoading, currentLocation }: {
  61. tabItems: TabItem[];
  62. isLoading?: boolean;
  63. currentLocation: Location.LocationObject | null
  64. }) => (
  65. <View style={{ flex: 1, backgroundColor: 'white' }}>
  66. {isLoading ? (
  67. <View className="items-center justify-center flex-1">
  68. <ActivityIndicator color="#34657b" />
  69. </View>
  70. ) : (
  71. <FlashList
  72. nestedScrollEnabled={true}
  73. data={tabItems.filter((item) => item?.actual_total_power && item?.actual_total_power !== 0)}
  74. renderItem={({ item }) => <TabItem item={item} currentLocation={currentLocation} />}
  75. keyExtractor={(item, index) => index.toString()}
  76. />
  77. )}
  78. </View>
  79. );
  80. return (
  81. <FirstRoute tabItems={tabItems} isLoading={isLoading} currentLocation={currentLocation} />
  82. );
  83. };
  84. const TabItem = ({ item, currentLocation }: { item: TabItem; currentLocation: Location.LocationObject | null }) => {
  85. const [distance, setDistance] = useState<number | null>(null);
  86. useEffect(() => {
  87. const getDistance = async () => {
  88. if (currentLocation) {
  89. const result = await calculateDistance(
  90. Number(item.stationLat),
  91. Number(item.stationLng),
  92. currentLocation
  93. );
  94. setDistance(result);
  95. }
  96. };
  97. getDistance();
  98. }, [currentLocation, item.stationLat, item.stationLng]);
  99. return (
  100. <Pressable
  101. onPress={() => {
  102. console.log(item.format_order_id);
  103. }}
  104. >
  105. <View style={styles.container}>
  106. <Image style={styles.image} source={item.imgURL} />
  107. <View className="flex flex-col gap-2 mr-2">
  108. <Text
  109. style={{
  110. fontWeight: '700',
  111. color: '#02677D',
  112. fontSize: 16
  113. }}
  114. >
  115. {`${item.date}日 - ${item.time}至${item.actual_end_time}`}
  116. </Text>
  117. <View className="flex flex-row justify-between space-x-2">
  118. <Text
  119. style={{
  120. fontWeight: '400',
  121. fontSize: 14,
  122. color: '#222222'
  123. }}
  124. >
  125. 已充電度數:{' '}
  126. {item.actual_total_power
  127. ? item.actual_total_power % 1 === 0
  128. ? item.actual_total_power
  129. : item.actual_total_power.toFixed(1)
  130. : ''}
  131. </Text>
  132. <Text
  133. style={{
  134. fontWeight: '400',
  135. fontSize: 14,
  136. color: '#222222'
  137. }}
  138. >
  139. 應付金額:{' '}
  140. {item.actual_fee !== undefined && item.actual_fee !== null
  141. ? item.actual_fee <= 0
  142. ? '$0'
  143. : item.actual_fee % 1 === 0
  144. ? `$${item.actual_fee}`
  145. : `$${item.actual_fee.toFixed(1)}`
  146. : ''}
  147. </Text>
  148. </View>
  149. <Text
  150. style={{
  151. fontWeight: '400',
  152. fontSize: 14,
  153. color: '#888888'
  154. }}
  155. >
  156. {item.chargeStationName}
  157. </Text>
  158. <View className="flex flex-row space-x-2">
  159. {
  160. item.status.id === 7 || item.status.id === 13?
  161. <Text
  162. style={{
  163. fontWeight: '400',
  164. fontSize: 14,
  165. color: '#02677D'
  166. }}
  167. >
  168. 訂單進行中
  169. </Text>
  170. :
  171. <Text
  172. style={{
  173. fontWeight: '400',
  174. fontSize: 14,
  175. color: '#02677D'
  176. }}
  177. onPress={() => {
  178. router.push({ pathname: 'chargingDetailsPage', params: { id: item.id, chargeStationName: item.chargeStationName }})
  179. }}
  180. >
  181. 訂單詳情 &gt;
  182. </Text>
  183. }
  184. </View>
  185. </View>
  186. </View>
  187. </Pressable>
  188. );
  189. };
  190. export default TabViewComponent;
  191. const styles = StyleSheet.create({
  192. container: {
  193. flexDirection: 'row',
  194. width: '100%',
  195. flex: 1,
  196. alignItems: 'center'
  197. },
  198. image: {
  199. width: 100,
  200. height: 100,
  201. margin: 15,
  202. borderRadius: 10
  203. },
  204. textContainer: {
  205. flex: 1,
  206. flexDirection: 'column',
  207. gap: 8,
  208. marginTop: 20,
  209. marginRight: 8 // Add right margin to prevent text from touching the edge
  210. }
  211. });