chargingRecord.tsx 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  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. import { useTranslation } from '../../util/hooks/useTranslation';
  20. export interface TabItem {
  21. imgURL: ImageSourcePropType;
  22. date: string;
  23. time: string;
  24. chargeStationName: string;
  25. chargeStationAddress: string;
  26. stationLat: string | number;
  27. stationLng: string | number;
  28. distance: string;
  29. format_order_id: string;
  30. actual_total_power?: number;
  31. actual_end_time?: string;
  32. actual_fee?: number;
  33. current_price?: number;
  34. id?: string;
  35. status?: any
  36. }
  37. interface TabViewComponentProps {
  38. tabItems: TabItem[];
  39. isLoading?: boolean;
  40. }
  41. const TabViewComponent: React.FC<TabViewComponentProps> = ({
  42. isLoading,
  43. tabItems,
  44. }) => {
  45. const layout = useWindowDimensions();
  46. const [currentLocation, setCurrentLocation] = useState<Location.LocationObject | null>(null);
  47. useEffect(() => {
  48. const getCurrentLocation = async () => {
  49. let { status } = await Location.requestForegroundPermissionsAsync();
  50. if (status !== 'granted') {
  51. console.error('Permission to access location was denied');
  52. return;
  53. }
  54. let location = await Location.getLastKnownPositionAsync({});
  55. setCurrentLocation(location);
  56. };
  57. getCurrentLocation();
  58. }, []);
  59. const FirstRoute = ({ tabItems, isLoading, currentLocation }: {
  60. tabItems: TabItem[];
  61. isLoading?: boolean;
  62. currentLocation: Location.LocationObject | null
  63. }) => (
  64. <View style={{ flex: 1, backgroundColor: 'white' }}>
  65. {isLoading ? (
  66. <View className="items-center justify-center flex-1">
  67. <ActivityIndicator color="#34657b" />
  68. </View>
  69. ) : (
  70. <FlashList
  71. nestedScrollEnabled={true}
  72. data={tabItems.filter((item) => item?.actual_total_power && item?.actual_total_power !== 0)}
  73. renderItem={({ item }) => <TabItem item={item} currentLocation={currentLocation} />}
  74. keyExtractor={(item, index) => index.toString()}
  75. />
  76. )}
  77. </View>
  78. );
  79. return (
  80. <FirstRoute tabItems={tabItems} isLoading={isLoading} currentLocation={currentLocation} />
  81. );
  82. };
  83. const TabItem = ({ item, currentLocation }: { item: TabItem; currentLocation: Location.LocationObject | null }) => {
  84. const [distance, setDistance] = useState<number | null>(null);
  85. const { t } = useTranslation();
  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 flex-wrap gap-1.5 mr-2">
  108. <Text
  109. style={{
  110. fontWeight: '700',
  111. color: '#02677D',
  112. fontSize: 16
  113. }}
  114. >
  115. {`${item.date}${t('chargingHistory.charging_record.day')} - ${item.time}${t('chargingHistory.charging_record.to')}${item.actual_end_time}`}
  116. </Text>
  117. <View className="flex flex-row flex-wrap justify-between">
  118. <Text
  119. style={{
  120. fontWeight: '400',
  121. fontSize: 14,
  122. color: '#222222',
  123. flexWrap: 'wrap',
  124. flexShrink: 1
  125. }}
  126. numberOfLines={0}
  127. >
  128. {t('chargingHistory.charging_record.charged_amount')}:{' '}
  129. {item.actual_total_power
  130. ? item.actual_total_power % 1 === 0
  131. ? item.actual_total_power
  132. : item.actual_total_power.toFixed(1)
  133. : ''}
  134. </Text>
  135. </View>
  136. <View className="flex flex-row flex-wrap justify-between">
  137. <Text
  138. style={{
  139. fontWeight: '400',
  140. fontSize: 14,
  141. color: '#222222',
  142. flexWrap: 'wrap',
  143. flexShrink: 1
  144. }}
  145. numberOfLines={0}
  146. >
  147. {t('chargingHistory.charging_record.amount_payable')}:{' '}
  148. {item.actual_fee !== undefined && item.actual_fee !== null
  149. ? item.actual_fee <= 0
  150. ? '$0'
  151. : item.actual_fee % 1 === 0
  152. ? `$${item.actual_fee}`
  153. : `$${item.actual_fee.toFixed(1)}`
  154. : ''}
  155. </Text>
  156. </View>
  157. <Text
  158. style={{
  159. fontWeight: '400',
  160. fontSize: 14,
  161. color: '#888888'
  162. }}
  163. >
  164. {item.chargeStationName}
  165. </Text>
  166. <View className="flex flex-row space-x-2">
  167. {
  168. item.status.id === 7 || item.status.id === 13?
  169. <Text
  170. style={{
  171. fontWeight: '400',
  172. fontSize: 14,
  173. color: '#02677D'
  174. }}
  175. >
  176. {t('chargingHistory.charging_record.order_in_progress')}
  177. </Text>
  178. :
  179. <Text
  180. style={{
  181. fontWeight: '400',
  182. fontSize: 14,
  183. color: '#02677D'
  184. }}
  185. onPress={() => {
  186. router.push({ pathname: 'chargingDetailsPage', params: { id: item.id, chargeStationName: item.chargeStationName }})
  187. }}
  188. >
  189. {t('chargingHistory.charging_record.order_details')} &gt;
  190. </Text>
  191. }
  192. </View>
  193. </View>
  194. </View>
  195. </Pressable>
  196. );
  197. };
  198. export default TabViewComponent;
  199. const styles = StyleSheet.create({
  200. container: {
  201. flexDirection: 'row',
  202. width: '100%',
  203. flex: 1,
  204. alignItems: 'center'
  205. },
  206. image: {
  207. width: 105,
  208. height: 110,
  209. margin: 15,
  210. borderRadius: 10
  211. },
  212. textContainer: {
  213. flex: 1,
  214. flexDirection: 'column',
  215. gap: 8,
  216. marginTop: 20,
  217. marginRight: 8 // Add right margin to prevent text from touching the edge
  218. }
  219. });