makingBookingPageComponent.tsx 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. import {
  2. View,
  3. Text,
  4. ScrollView,
  5. Pressable,
  6. StyleSheet,
  7. Image,
  8. Dimensions,
  9. ActivityIndicator
  10. } from 'react-native';
  11. import { SafeAreaView } from 'react-native-safe-area-context';
  12. import { router } from 'expo-router';
  13. import NormalButton from '../global/normal_button';
  14. import {
  15. CheckMarkLogoSvg,
  16. DirectionLogoSvg,
  17. PreviousPageBlackSvg
  18. } from '../global/SVG';
  19. import { ChargingStationTabView } from '../global/chargingStationTabView';
  20. import ChooseCarForChargingRow from '../global/chooseCarForChargingRow';
  21. import { useEffect, useState } from 'react';
  22. import DropdownSelect from '../global/dropdown_select';
  23. import { chargeStationService } from '../../service/chargeStationService';
  24. const dummyDataChooseCarForCharging = [
  25. {
  26. imageUrl: require('../../assets/car1.png'),
  27. VehicleName: 'TESLA - Model 3',
  28. isDefault: true
  29. },
  30. { VehicleName: 'TESLA - Model Y', isDefault: false },
  31. {
  32. imageUrl: require('../../assets/car1.png'),
  33. VehicleName: 'TESLA - Model X',
  34. isDefault: false
  35. },
  36. { VehicleName: 'TESLA - Model 3', isDefault: false }
  37. ];
  38. interface AccordionItemProps {
  39. title: string;
  40. children: React.ReactNode;
  41. isOpen: boolean;
  42. onToggle: () => void;
  43. isSelected: boolean;
  44. }
  45. const AccordionItem: React.FC<AccordionItemProps> = ({
  46. title,
  47. children,
  48. isOpen,
  49. onToggle,
  50. isSelected
  51. }) => (
  52. <View className={`${isSelected ? 'bg-[#e7f5f8]' : 'bg-white'}`}>
  53. <View className="mx-[5%]">
  54. <Pressable onPress={onToggle}>
  55. <Text
  56. className={` pt-2 text-lg ${
  57. isSelected ? 'text-[#222222]' : 'text-[#888888] '
  58. }}`}
  59. >
  60. {title}
  61. </Text>
  62. </Pressable>
  63. {isOpen && <View>{children}</View>}
  64. </View>
  65. </View>
  66. );
  67. const MakingBookingPageComponent = () => {
  68. const [openDrawer, setOpenDrawer] = useState<number | null>(0);
  69. const [selectedTime, setSelectedTime] = useState<string>('');
  70. const [availableTimeSlots, setAvailableTimeSlots] = useState<string[]>([]);
  71. const [selectedDrawer, setSelectedDrawer] = useState<number>(0);
  72. const [isLoading, setIsLoading] = useState(true);
  73. const [selectedDate, setSelectedDate] = useState<string>('');
  74. const toggleDrawer = (index: number) => {
  75. setOpenDrawer(openDrawer === index ? null : index);
  76. setSelectedDrawer(index);
  77. };
  78. const [availableDate, setAvailableDate] = useState<string[]>([]);
  79. useEffect(() => {
  80. const fetchingAvailableDates = async () => {
  81. const fetchedDates = await chargeStationService.fetchAvailableDates(
  82. '2405311022116801000'
  83. );
  84. setAvailableDate(fetchedDates);
  85. };
  86. fetchingAvailableDates();
  87. }, []);
  88. useEffect(() => {
  89. const fetchingAvailableTimeSlots = async () => {
  90. setIsLoading(true);
  91. try {
  92. const fetchedTimeSlots =
  93. await chargeStationService.fetchAvailableTimeSlots(
  94. '2405311022116801000',
  95. selectedDate
  96. );
  97. setAvailableTimeSlots(fetchedTimeSlots);
  98. console.log(`I AM TIMESLOT ${fetchedTimeSlots}`);
  99. } catch (error) {
  100. console.error('Error fetching time slots:', error);
  101. } finally {
  102. setIsLoading(false);
  103. }
  104. };
  105. if (selectedDate) {
  106. fetchingAvailableTimeSlots();
  107. }
  108. }, [selectedDate]);
  109. const timeData = [
  110. '14:00',
  111. '14:15',
  112. '14:30',
  113. '14:45',
  114. '15:00',
  115. '15:15',
  116. '15:30',
  117. '15:45',
  118. '16:00',
  119. '16:15',
  120. '16:30',
  121. '16:45',
  122. '17:00',
  123. '17:15',
  124. '17:30',
  125. '17:45'
  126. ];
  127. const dateData = [
  128. '今天',
  129. '3/15',
  130. '3/16',
  131. '3/17',
  132. '3/18',
  133. '3/19',
  134. '3/20',
  135. '3/21'
  136. ];
  137. const [uiState, setUiState] = useState({
  138. isCarSelected: false,
  139. isChargingPlanOpen: false,
  140. isDateOpen: false,
  141. isCharigingGunOpen: false
  142. });
  143. const [selectedChargingGun, setSelectedChargingGun] = useState('');
  144. const [chargingBasedOnWatt, setChargingBasedOnWatt] = useState(false);
  145. const [stopChargingUponBatteryFull, setStopChargingUponBatteryFull] =
  146. useState(false);
  147. const [selectedCar, setSelectedCar] = useState('');
  148. const [selectedDuration, setSelectedDuration] = useState('');
  149. const { width: screenWidth, height: screenHeight } =
  150. Dimensions.get('window');
  151. const layoutWidth = screenWidth;
  152. const layoutHeight = screenHeight * 0.4;
  153. return (
  154. <SafeAreaView
  155. style={{
  156. flex: 1,
  157. backgroundColor: 'white'
  158. }}
  159. edges={['right', 'top', 'left']}
  160. >
  161. <ScrollView
  162. className="flex-1 bg-white"
  163. showsVerticalScrollIndicator={false}
  164. >
  165. <View className="pb-4 ">
  166. <View className="ml-[5%] pt-8">
  167. <Pressable
  168. style={{ alignSelf: 'flex-start' }}
  169. onPress={() => {
  170. if (router.canGoBack()) {
  171. router.back();
  172. } else {
  173. router.replace('./');
  174. }
  175. }}
  176. >
  177. <PreviousPageBlackSvg />
  178. </Pressable>
  179. <Text className="text-3xl mt-8">上環街市充電站</Text>
  180. <View className="flex-column">
  181. <View className="flex-row justify-between items-center mr-[5%]">
  182. <Text
  183. className="text-base"
  184. style={styles.grayColor}
  185. >
  186. 香港上環皇后大道中345號
  187. </Text>
  188. <NormalButton
  189. title={
  190. <View className="flex-row items-center justify-center text-center space-x-1">
  191. <DirectionLogoSvg />
  192. <Text className="text-base">
  193. 路線
  194. </Text>
  195. </View>
  196. }
  197. onPress={() => console.log('路線')}
  198. extendedStyle={{
  199. backgroundColor: '#E3F2F8',
  200. borderRadius: 61,
  201. paddingHorizontal: 20,
  202. paddingVertical: 8
  203. }}
  204. />
  205. </View>
  206. <View className="flex-row space-x-2 items-center">
  207. <CheckMarkLogoSvg />
  208. <Text>Walk-In</Text>
  209. <Text>400米</Text>
  210. </View>
  211. </View>
  212. </View>
  213. </View>
  214. <View>
  215. {selectedCar !== '' ? (
  216. <>
  217. <Pressable
  218. onPress={() => {
  219. setSelectedCar('');
  220. setOpenDrawer(0);
  221. setSelectedDrawer(0);
  222. setSelectedDuration('');
  223. setChargingBasedOnWatt(false);
  224. setStopChargingUponBatteryFull(false);
  225. setSelectedDate('');
  226. setSelectedTime('');
  227. }}
  228. >
  229. <View className="mx-[5%]">
  230. <View className="flex-row items-center pt-4">
  231. <Text className="text-lg pr-2 text-[#34667c]">
  232. 選擇充電車輛
  233. </Text>
  234. <CheckMarkLogoSvg />
  235. </View>
  236. <Text className="text-lg pb-4">
  237. {selectedCar}
  238. </Text>
  239. </View>
  240. </Pressable>
  241. </>
  242. ) : (
  243. <AccordionItem
  244. title="選擇充電車輪"
  245. isOpen={openDrawer === 0}
  246. onToggle={() => toggleDrawer(0)}
  247. isSelected={selectedDrawer === 0}
  248. >
  249. <ScrollView
  250. horizontal={true}
  251. contentContainerStyle={{
  252. alignItems: 'center',
  253. flexDirection: 'row',
  254. marginVertical: 8
  255. }}
  256. className="space-x-2 "
  257. >
  258. {dummyDataChooseCarForCharging.map(
  259. (car, index) => (
  260. <ChooseCarForChargingRow
  261. onPress={() => {
  262. setSelectedCar(car.VehicleName);
  263. setSelectedDrawer(1);
  264. setOpenDrawer(1);
  265. }}
  266. imageUrl={car.imageUrl}
  267. key={`${car.VehicleName}+${index}`}
  268. VehicleName={car.VehicleName}
  269. isDefault={car.isDefault}
  270. />
  271. )
  272. )}
  273. </ScrollView>
  274. </AccordionItem>
  275. )}
  276. {stopChargingUponBatteryFull === true ||
  277. selectedDuration !== '' ? (
  278. <Pressable
  279. onPress={() => {
  280. setSelectedDuration('');
  281. setChargingBasedOnWatt(false);
  282. setStopChargingUponBatteryFull(false);
  283. setSelectedTime('');
  284. setSelectedDate('');
  285. setOpenDrawer(1);
  286. setSelectedDrawer(1);
  287. }}
  288. >
  289. <View className="mx-[5%] ">
  290. <View className="flex-row items-center pt-4">
  291. <Text className="text-lg pr-2 text-[#34667c]">
  292. 選擇充電方案
  293. </Text>
  294. <CheckMarkLogoSvg />
  295. </View>
  296. <Text className="text-lg pb-4">
  297. {selectedDuration !== ''
  298. ? `按每道電 - ${selectedDuration}`
  299. : '充滿停機'}
  300. </Text>
  301. </View>
  302. </Pressable>
  303. ) : (
  304. <AccordionItem
  305. title="選擇充電方案"
  306. isOpen={openDrawer === 1}
  307. onToggle={() => {
  308. if (selectedCar !== '') {
  309. toggleDrawer(1);
  310. }
  311. }}
  312. isSelected={selectedDrawer === 1}
  313. >
  314. <View className="flex-row justify-between mt-2 mb-3">
  315. <Pressable
  316. className={`border rounded-lg border-[#34667c] w-[47%] items-center bg-white ${
  317. chargingBasedOnWatt
  318. ? 'bg-[#34667c] '
  319. : ''
  320. }`}
  321. onPress={() => {
  322. setChargingBasedOnWatt(
  323. !chargingBasedOnWatt
  324. );
  325. setStopChargingUponBatteryFull(false);
  326. }}
  327. >
  328. <Text
  329. className={`text-base p-2 text-[#34667c] ${
  330. chargingBasedOnWatt
  331. ? ' text-white'
  332. : 'text-[#34667c]'
  333. }`}
  334. >
  335. 按每度電
  336. </Text>
  337. </Pressable>
  338. <Pressable
  339. onPress={() => {
  340. setStopChargingUponBatteryFull(
  341. !stopChargingUponBatteryFull
  342. );
  343. setChargingBasedOnWatt(false);
  344. setSelectedDrawer(2);
  345. setOpenDrawer(2);
  346. }}
  347. className={`border rounded-lg border-[#34667c] w-[47%] items-center bg-white ${
  348. stopChargingUponBatteryFull
  349. ? ' bg-[#34667c]'
  350. : ''
  351. }`}
  352. >
  353. <Text
  354. className={`text-base p-2 text-[#34667c] ${
  355. stopChargingUponBatteryFull
  356. ? ' text-white'
  357. : 'text-[#34667c]'
  358. }`}
  359. >
  360. 充滿停機
  361. </Text>
  362. </Pressable>
  363. </View>
  364. {chargingBasedOnWatt === true && (
  365. <View className="flex-row w-full justify-between mb-3">
  366. {['30分鐘', '45分鐘', '60分鐘', '其他'].map(
  367. (duration) => (
  368. <Pressable
  369. key={duration}
  370. className={`${
  371. selectedDuration ===
  372. duration
  373. ? 'bg-[#34667c] '
  374. : 'bg-white'
  375. } border border-[#34667c] rounded-lg w-[22%] items-center`}
  376. onPress={() => {
  377. setSelectedDuration(
  378. duration
  379. );
  380. setOpenDrawer(2);
  381. setSelectedDrawer(2);
  382. }}
  383. >
  384. <Text
  385. className={`text-base p-2 ${
  386. selectedDuration ===
  387. duration
  388. ? 'text-white'
  389. : 'text-[#34667c]'
  390. } `}
  391. >
  392. {duration}
  393. </Text>
  394. </Pressable>
  395. )
  396. )}
  397. </View>
  398. )}
  399. </AccordionItem>
  400. )}
  401. {selectedTime !== '' ? (
  402. <Pressable
  403. onPress={() => {
  404. setOpenDrawer(2);
  405. setSelectedDrawer(2);
  406. setSelectedDate('');
  407. setSelectedTime('');
  408. }}
  409. >
  410. <View className="mx-[5%] ">
  411. <View className="flex-row items-center pt-4">
  412. <Text className="text-lg pr-2 text-[#34667c]">
  413. 選擇日期
  414. </Text>
  415. <CheckMarkLogoSvg />
  416. </View>
  417. <Text className="text-lg pb-4">
  418. {selectedDate} - {selectedTime}
  419. </Text>
  420. </View>
  421. </Pressable>
  422. ) : (
  423. <AccordionItem
  424. title="選擇日期 (月/日)"
  425. isOpen={openDrawer === 2}
  426. onToggle={() => {
  427. if (
  428. stopChargingUponBatteryFull !== false ||
  429. selectedDuration !== ''
  430. ) {
  431. toggleDrawer(2);
  432. }
  433. }}
  434. isSelected={selectedDrawer === 2}
  435. >
  436. <View className="flex-row w-full flex-wrap mb-1 ">
  437. {availableDate.map((date) => (
  438. <Pressable
  439. key={date}
  440. className={`${
  441. selectedDate === date
  442. ? 'bg-[#34667c] '
  443. : 'bg-white'
  444. } border border-[#34667c] rounded-lg w-[22%] items-center mt-1 mr-1 mb-1`}
  445. onPress={() => {
  446. setSelectedDate(date);
  447. }}
  448. >
  449. <Text
  450. className={`text-base p-2 ${
  451. selectedDate === date
  452. ? 'text-white'
  453. : 'text-[#34667c]'
  454. } `}
  455. >
  456. {date}
  457. </Text>
  458. </Pressable>
  459. ))}
  460. </View>
  461. {selectedDate !== '' && (
  462. <>
  463. <Text className="text-lg pr-2 ">
  464. 選擇時間
  465. </Text>
  466. {isLoading ? (
  467. <View className="flex-1 mb-2">
  468. <ActivityIndicator />
  469. </View>
  470. ) : (
  471. <View className="flex-row w-full justify-between mb-3 flex-wrap my-2 ">
  472. {availableTimeSlots.map((time) => (
  473. <Pressable
  474. key={time}
  475. className={`${
  476. selectedTime === time
  477. ? 'bg-[#34667c] '
  478. : 'bg-white'
  479. } border border-[#34667c] rounded-lg w-[22%] items-center mb-2`}
  480. onPress={() => {
  481. setSelectedTime(time);
  482. setOpenDrawer(3);
  483. setSelectedDrawer(3);
  484. }}
  485. >
  486. <Text
  487. className={`text-base p-2 ${
  488. selectedTime ===
  489. time
  490. ? 'text-white'
  491. : 'text-[#34667c]'
  492. } `}
  493. >
  494. {time}
  495. </Text>
  496. </Pressable>
  497. ))}
  498. </View>
  499. )}
  500. </>
  501. )}
  502. </AccordionItem>
  503. )}
  504. <View className="">
  505. <AccordionItem
  506. title="選擇充電座"
  507. isOpen={openDrawer === 3}
  508. onToggle={() => {
  509. if (selectedTime) {
  510. toggleDrawer(3);
  511. }
  512. }}
  513. isSelected={selectedDrawer === 3}
  514. >
  515. <View className="">
  516. <DropdownSelect
  517. dropdownOptions={[
  518. { label: '1', value: '1' },
  519. { label: '2', value: '2' }
  520. ]}
  521. placeholder={'選擇充電座號碼'}
  522. onSelect={(value) => {
  523. setSelectedChargingGun(value);
  524. router.push('/bookingConfirmationPage');
  525. }}
  526. extendedStyle={{
  527. borderColor: '#34667c',
  528. marginTop: 4
  529. }}
  530. />
  531. <Image
  532. style={{
  533. width: layoutWidth * 0.97,
  534. height: layoutHeight
  535. }}
  536. resizeMode="contain"
  537. source={require('../../assets/ChargeStationLayout.png')}
  538. />
  539. </View>
  540. </AccordionItem>
  541. </View>
  542. </View>
  543. <View className="mx-[5%] flex-1">
  544. <View
  545. className="flex-row border-slate-300 mt-3 mb-6 rounded-2xl flex-1"
  546. style={{ borderWidth: 1 }}
  547. >
  548. <View className="flex-1 m-4">
  549. <View className="flex-1 flex-row ">
  550. <View className=" flex-1 flex-column justify-between">
  551. <Text
  552. className="text-xl "
  553. style={styles.text}
  554. >
  555. 收費
  556. </Text>
  557. <View className="flex-row items-center space-x-2">
  558. <Text className="text-3xl text-[#02677D]">
  559. $20
  560. </Text>
  561. <Text style={styles.text}>
  562. 每15分鐘
  563. </Text>
  564. </View>
  565. </View>
  566. <View className="items-center justify-center">
  567. <View className="w-[1px] h-[60%] bg-[#CCCCCC]" />
  568. </View>
  569. <View className="flex-1 flex-column ">
  570. <View className="flex-1"></View>
  571. <View className="flex-row items-center ml-4 space-x-2 ">
  572. <Text className="text-3xl text-[#02677D]">
  573. $3.5
  574. </Text>
  575. <Text style={styles.text}>每度電</Text>
  576. </View>
  577. </View>
  578. </View>
  579. </View>
  580. </View>
  581. <Text className="text-xl pb-2 " style={styles.text}>
  582. 充電站資訊
  583. </Text>
  584. <View className="h-[250px]">
  585. <ChargingStationTabView titles={['充電插頭', '其他']} />
  586. </View>
  587. </View>
  588. </ScrollView>
  589. </SafeAreaView>
  590. );
  591. };
  592. export default MakingBookingPageComponent;
  593. const styles = StyleSheet.create({
  594. grayColor: {
  595. color: '#888888'
  596. },
  597. topLeftTriangle: {
  598. width: 0,
  599. height: 0,
  600. borderLeftWidth: 50,
  601. borderBottomWidth: 50,
  602. borderLeftColor: '#02677D',
  603. borderBottomColor: 'transparent',
  604. position: 'absolute',
  605. top: 0,
  606. left: 0
  607. },
  608. text: {
  609. fontWeight: 300,
  610. color: '#000000'
  611. }
  612. });