reservationLocationPage.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. import Svg, { Path, Rect } from 'react-native-svg';
  2. import * as React from 'react';
  3. import {
  4. View,
  5. Text,
  6. useWindowDimensions,
  7. StyleSheet,
  8. Image,
  9. ImageSourcePropType,
  10. Pressable
  11. } from 'react-native';
  12. import { TabView, SceneMap, TabBar } from 'react-native-tab-view';
  13. import { FlashList } from '@shopify/flash-list';
  14. import { SafeAreaView } from 'react-native-safe-area-context';
  15. import NormalInput from '../global/normal_input';
  16. import { router } from 'expo-router';
  17. export interface TabItem {
  18. imgURL: ImageSourcePropType;
  19. date: string;
  20. time: string;
  21. chargeStationName: string;
  22. chargeStationAddress: string;
  23. distance: string;
  24. }
  25. interface TabViewComponentProps {
  26. titles: string[];
  27. tabItems: TabItem[];
  28. }
  29. const CheckMarkLogoSvg = () => (
  30. <Svg width="20" height="20" viewBox="0 0 20 20" fill="none">
  31. <Rect width="20" height="20" rx="10" fill="#02677D" />
  32. <Path
  33. d="M7.95837 15L3.20837 10.25L4.39587 9.06251L7.95837 12.625L15.6042 4.97917L16.7917 6.16667L7.95837 15Z"
  34. fill="white"
  35. />
  36. </Svg>
  37. );
  38. const dummyTabItems: TabItem[] = [
  39. {
  40. imgURL: require('../../assets/dummyStationPicture.png'),
  41. date: '今天',
  42. time: '16:30',
  43. chargeStationName: '上環街市充電站',
  44. chargeStationAddress: '香港上環皇后大道中345號',
  45. distance: '400米'
  46. },
  47. {
  48. imgURL: require('../../assets/dummyStationPicture2.png'),
  49. date: '3月15',
  50. time: '17:45',
  51. chargeStationName: '中環IFC充電站',
  52. chargeStationAddress: '香港中環皇后大道中999號',
  53. distance: '680米'
  54. },
  55. {
  56. imgURL: require('../../assets/dummyStationPicture2.png'),
  57. date: '4月20',
  58. time: '12:30',
  59. chargeStationName: '中環IFC充電站',
  60. chargeStationAddress: '香港中環皇后大道中999號',
  61. distance: '680米'
  62. },
  63. {
  64. imgURL: require('../../assets/dummyStationPicture.png'),
  65. date: '今天',
  66. time: '16:30',
  67. chargeStationName: '上環街市充電站',
  68. chargeStationAddress: '香港上環皇后大道中345號',
  69. distance: '400米'
  70. },
  71. {
  72. imgURL: require('../../assets/dummyStationPicture.png'),
  73. date: '今天',
  74. time: '16:30',
  75. chargeStationName: '上環街市充電站',
  76. chargeStationAddress: '香港上環皇后大道中345號',
  77. distance: '400米'
  78. }
  79. ];
  80. const LocationTabComponent: React.FC<TabViewComponentProps> = ({
  81. titles,
  82. tabItems
  83. }) => {
  84. const layout = useWindowDimensions();
  85. const FirstRoute = () => (
  86. <View style={{ flex: 1, backgroundColor: 'white' }}>
  87. <FlashList
  88. data={tabItems}
  89. renderItem={({ item }) => {
  90. return (
  91. <View style={styles.container}>
  92. <Image style={styles.image} source={item.imgURL} />
  93. <View style={styles.textContainer}>
  94. <Text
  95. style={{
  96. fontWeight: 400,
  97. fontSize: 18,
  98. color: '#222222'
  99. }}
  100. >
  101. {item.chargeStationName}
  102. </Text>
  103. <Text
  104. style={{
  105. fontWeight: 400,
  106. fontSize: 14,
  107. color: '#888888'
  108. }}
  109. >
  110. {item.chargeStationAddress}
  111. </Text>
  112. <View className=" flex-row space-x-2 items-center">
  113. <CheckMarkLogoSvg />
  114. <Text
  115. style={{
  116. fontWeight: 400,
  117. fontSize: 14,
  118. color: '#222222'
  119. }}
  120. >
  121. Walk-in
  122. </Text>
  123. </View>
  124. </View>
  125. <Text
  126. style={{
  127. fontWeight: 400,
  128. fontSize: 16,
  129. color: '#888888',
  130. marginTop: 22
  131. }}
  132. >
  133. {item.distance}
  134. </Text>
  135. </View>
  136. );
  137. }}
  138. estimatedItemSize={10}
  139. />
  140. </View>
  141. );
  142. //tab 2
  143. const SecondRoute = () => (
  144. <View style={{ flex: 1, backgroundColor: 'white' }}>
  145. <FlashList
  146. data={tabItems}
  147. renderItem={({ item }) => {
  148. return (
  149. <View style={styles.container}>
  150. <Image style={styles.image} source={item.imgURL} />
  151. <View style={styles.textContainer}>
  152. <Text
  153. style={{
  154. fontWeight: 400,
  155. fontSize: 18,
  156. color: '#222222'
  157. }}
  158. >
  159. {item.chargeStationName}
  160. </Text>
  161. <Text
  162. style={{
  163. fontWeight: 400,
  164. fontSize: 14,
  165. color: '#888888'
  166. }}
  167. >
  168. {item.chargeStationAddress}
  169. </Text>
  170. <View className=" flex-row space-x-2 items-center">
  171. <CheckMarkLogoSvg />
  172. <Text
  173. style={{
  174. fontWeight: 400,
  175. fontSize: 14,
  176. color: '#222222'
  177. }}
  178. >
  179. Walk-in
  180. </Text>
  181. </View>
  182. </View>
  183. <Text
  184. style={{
  185. fontWeight: 400,
  186. fontSize: 16,
  187. color: '#888888',
  188. marginTop: 22
  189. }}
  190. >
  191. {item.distance}
  192. </Text>
  193. </View>
  194. );
  195. }}
  196. estimatedItemSize={10}
  197. />
  198. </View>
  199. );
  200. const renderScene = SceneMap({
  201. firstRoute: FirstRoute,
  202. secondRoute: SecondRoute
  203. });
  204. const [routes] = React.useState([
  205. { key: 'firstRoute', title: titles[0] },
  206. { key: 'secondRoute', title: titles[1] }
  207. ]);
  208. const [index, setIndex] = React.useState(0);
  209. const renderTabBar = (props: any) => (
  210. <TabBar
  211. {...props}
  212. renderLabel={({ route, focused }) => (
  213. <Text
  214. style={{
  215. color: focused ? '#025c72' : '#888888',
  216. fontWeight: focused ? '900' : 'thin',
  217. fontSize: 20
  218. }}
  219. >
  220. {route.title}
  221. </Text>
  222. )}
  223. indicatorStyle={{
  224. backgroundColor: '#025c72'
  225. }}
  226. style={{
  227. backgroundColor: 'white',
  228. borderColor: '#DBE4E8',
  229. elevation: 0,
  230. marginHorizontal: 15,
  231. borderBottomWidth: 0.5
  232. }}
  233. />
  234. );
  235. return (
  236. <TabView
  237. navigationState={{ index, routes }}
  238. renderScene={renderScene}
  239. onIndexChange={setIndex}
  240. initialLayout={{ width: layout.width }}
  241. renderTabBar={renderTabBar}
  242. />
  243. );
  244. };
  245. const styles = StyleSheet.create({
  246. container: { flexDirection: 'row' },
  247. image: { width: 100, height: 100, margin: 15, borderRadius: 10 },
  248. textContainer: { flexDirection: 'column', gap: 8, marginTop: 22 }
  249. });
  250. const ReservationLocationPage = () => {
  251. const CrossLogoSvg = () => (
  252. <Svg width="24" height="24" viewBox="0 0 24 24" fill="none">
  253. <Path
  254. d="M2.33333 23.3333L0 21L9.33333 11.6667L0 2.33333L2.33333 0L11.6667 9.33333L21 0L23.3333 2.33333L14 11.6667L23.3333 21L21 23.3333L11.6667 14L2.33333 23.3333Z"
  255. fill="#222222"
  256. />
  257. </Svg>
  258. );
  259. return (
  260. <SafeAreaView className="flex-1 bg-white">
  261. <View className="flex-1 mx-[5%]">
  262. <View className="min-h-[250px] flex-column">
  263. <View className="flex-2 justify-center ">
  264. <View className="pt-6 ">
  265. {/* For now, clicking the X button goes back to login page. */}
  266. {/* Once home page component is created, i will change it */}
  267. <Pressable onPress={() => router.push('/')}>
  268. <CrossLogoSvg />
  269. </Pressable>
  270. </View>
  271. </View>
  272. <View className="flex-1 justify-center ">
  273. <Text className="text-[50px] font-normal">
  274. 預約地點
  275. </Text>
  276. </View>
  277. <View className="flex-1 justify-center mb-6 ">
  278. <NormalInput
  279. placeholder="搜尋所有充電站或地區.."
  280. onChangeText={() => console.log('input')}
  281. extendedStyle={{ padding: 20 }}
  282. />
  283. </View>
  284. </View>
  285. <View className="flex-1">
  286. <LocationTabComponent
  287. titles={['附近的充電站', '所有的充電站']}
  288. tabItems={dummyTabItems}
  289. />
  290. </View>
  291. </View>
  292. </SafeAreaView>
  293. );
  294. };
  295. export default ReservationLocationPage;