|
|
@@ -16,11 +16,9 @@ import { router, useLocalSearchParams } from 'expo-router';
|
|
|
import NormalButton from '../global/normal_button';
|
|
|
import { CheckMarkLogoSvg, DirectionLogoSvg, PreviousPageBlackSvg } from '../global/SVG';
|
|
|
import { ChargingStationTabView } from '../global/chargingStationTabView';
|
|
|
-import ChooseCarForChargingRow from '../global/chooseCarForChargingRow';
|
|
|
import { useEffect, useState } from 'react';
|
|
|
import DropdownSelect from '../global/dropdown_select';
|
|
|
import { chargeStationService } from '../../service/chargeStationService';
|
|
|
-import { authenticationService } from '../../service/authService';
|
|
|
import * as Location from 'expo-location';
|
|
|
import { calculateDistance } from '../global/distanceCalculator';
|
|
|
import Modal from 'react-native-modal';
|
|
|
@@ -73,7 +71,7 @@ const MakingBookingPageComponent = () => {
|
|
|
const layoutWidth = screenWidth;
|
|
|
const layoutHeight = screenHeight * 0.32;
|
|
|
const { userID, setUserID } = useUserInfoStore();
|
|
|
-
|
|
|
+ const [formattedDates, setFormattedDates] = useState([]);
|
|
|
const params = useLocalSearchParams();
|
|
|
const chargeStationID = params.chargeStationID as string;
|
|
|
const chargeStationName = params.chargeStationName as string;
|
|
|
@@ -87,8 +85,126 @@ const MakingBookingPageComponent = () => {
|
|
|
const [distance, setDistance] = useState<string | null>(null);
|
|
|
const [upcomingReservations, setUpcomingReservation] = useState([]);
|
|
|
const [carLoadingState, setCarLoadingState] = useState(false);
|
|
|
- const [isDateLoading, setIsDateLoading] = useState(true);
|
|
|
+ const [isDateLoading, setIsDateLoading] = useState(false);
|
|
|
+ const [dataResponse, setDataResponse] = useState([]);
|
|
|
|
|
|
+ //check for unpaid penalties
|
|
|
+ useEffect(() => {
|
|
|
+ const checkUnpaidPenalties = async () => {
|
|
|
+ try {
|
|
|
+ const reservationHistories = await chargeStationService.fetchReservationHistories();
|
|
|
+ const unpaidPenalties = reservationHistories.filter(
|
|
|
+ (reservation) => reservation.penalty_fee > 0 && reservation.penalty_paid_status === false
|
|
|
+ );
|
|
|
+
|
|
|
+ if (unpaidPenalties.length > 0) {
|
|
|
+ const mostRecentUnpaidReservation = unpaidPenalties.reduce((mostRecent, current) => {
|
|
|
+ return new Date(mostRecent.created_at) > new Date(current.created_at) ? mostRecent : current;
|
|
|
+ }, unpaidPenalties[0]);
|
|
|
+
|
|
|
+ Alert.alert(
|
|
|
+ '未付罰款',
|
|
|
+ '您有未支付的罰款。請先支付罰款後再開始充電。',
|
|
|
+ [
|
|
|
+ {
|
|
|
+ text: '查看詳情',
|
|
|
+ onPress: () => {
|
|
|
+ // Navigate to a page showing penalty details
|
|
|
+ router.push({
|
|
|
+ pathname: '(auth)/(tabs)/(home)/penaltyPaymentPage',
|
|
|
+ params: {
|
|
|
+ book_time: mostRecentUnpaidReservation.book_time,
|
|
|
+ end_time: mostRecentUnpaidReservation.end_time,
|
|
|
+ actual_end_time: mostRecentUnpaidReservation.actual_end_time,
|
|
|
+ penalty_fee: mostRecentUnpaidReservation.penalty_fee,
|
|
|
+ format_order_id: mostRecentUnpaidReservation.format_order_id,
|
|
|
+ id: mostRecentUnpaidReservation.id,
|
|
|
+ stationName:
|
|
|
+ mostRecentUnpaidReservation.connector.EquipmentID.StationID.snapshot
|
|
|
+ .StationName,
|
|
|
+ address:
|
|
|
+ mostRecentUnpaidReservation.connector.EquipmentID.StationID.snapshot
|
|
|
+ .Address
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ text: '返回',
|
|
|
+ onPress: () => {
|
|
|
+ if (router.canGoBack()) {
|
|
|
+ router.back();
|
|
|
+ } else {
|
|
|
+ router.push('/mainPage');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ { cancelable: false }
|
|
|
+ );
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Error checking unpaid penalties:', error);
|
|
|
+ // Handle the error appropriately (e.g., show an error message to the user)
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ checkUnpaidPenalties();
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ const handleSendingSize = async (watt) => {
|
|
|
+ try {
|
|
|
+ setIsLoading(true);
|
|
|
+ const wattValue = parseInt(watt.split(' ')[0]);
|
|
|
+ console.log('wattValue', wattValue);
|
|
|
+ let size: number;
|
|
|
+ //make the duration based on watt
|
|
|
+ switch (wattValue) {
|
|
|
+ case 20:
|
|
|
+ size = 25;
|
|
|
+ break;
|
|
|
+ case 25:
|
|
|
+ size = 30;
|
|
|
+ break;
|
|
|
+ case 30:
|
|
|
+ size = 40;
|
|
|
+ break;
|
|
|
+ case 40:
|
|
|
+ size = 45;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ console.error('Invalid selectedWatt value');
|
|
|
+ return; // Exit the function if selectedWatt is invalid
|
|
|
+ }
|
|
|
+
|
|
|
+ const response = await chargeStationService.getReservationWithSize(size);
|
|
|
+ if (response) {
|
|
|
+ setDataResponse(response);
|
|
|
+ // console.log('respoasdasdasdnse', response);
|
|
|
+
|
|
|
+ const uniqueDates = new Set();
|
|
|
+ response.forEach((item) => {
|
|
|
+ item.timeSlot.forEach((slot) => {
|
|
|
+ uniqueDates.add(slot.date);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ const availableDates = Array.from(uniqueDates).sort();
|
|
|
+
|
|
|
+ setAvailableDate(availableDates);
|
|
|
+ // console.log('formattedDates', formattedDates);
|
|
|
+ } else {
|
|
|
+ console.log('No response from getReservationWithSize');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Error in handleSendingSize:', error);
|
|
|
+ // Handle the error appropriately, maybe show an alert to the user
|
|
|
+ } finally {
|
|
|
+ setIsLoading(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ //get location
|
|
|
useEffect(() => {
|
|
|
const getCurrentLocation = async () => {
|
|
|
let { status } = await Location.requestForegroundPermissionsAsync();
|
|
|
@@ -104,117 +220,119 @@ const MakingBookingPageComponent = () => {
|
|
|
}, []);
|
|
|
|
|
|
//getDefaultCar
|
|
|
- useEffect(() => {
|
|
|
- const fetchDefaultCar = async () => {
|
|
|
- setIsDefaultCarLoading(true);
|
|
|
- try {
|
|
|
- const response = await chargeStationService.getUserDefaultCars();
|
|
|
- if (response && response.data) {
|
|
|
- setDefaultCar(response.data.id);
|
|
|
- setSelectedCarID(response.data.id);
|
|
|
- setCarCapacitance(response.data.car_type.capacitance);
|
|
|
- setSelectedCar(response.data.id);
|
|
|
- console.log('*******', response.data.car_type.capacitance);
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.log('Error fetching default car:', error);
|
|
|
- } finally {
|
|
|
- setIsDefaultCarLoading(false);
|
|
|
- }
|
|
|
- };
|
|
|
- fetchDefaultCar();
|
|
|
- }, []);
|
|
|
- const formatDistance = (distanceInMeters: number): string => {
|
|
|
- if (distanceInMeters < 1000) {
|
|
|
- return `${Math.round(distanceInMeters)}米`;
|
|
|
- } else {
|
|
|
- const distanceInKm = distanceInMeters / 1000;
|
|
|
- return `${distanceInKm.toFixed(1)}公里`;
|
|
|
- }
|
|
|
- };
|
|
|
- const getUpcomingReservations = (reservations: any, daysAhead = 3) => {
|
|
|
- const today = new Date();
|
|
|
- const threeDaysLater = new Date(today);
|
|
|
- threeDaysLater.setDate(today.getDate() + daysAhead);
|
|
|
-
|
|
|
- return reservations
|
|
|
- .filter((reservation) => {
|
|
|
- const reservationDate = new Date(reservation.book_time);
|
|
|
- return reservationDate >= today && reservationDate <= threeDaysLater;
|
|
|
- })
|
|
|
- .sort((a, b) => new Date(a.reservationDate) - new Date(b.reservationDate));
|
|
|
- };
|
|
|
+ // useEffect(() => {
|
|
|
+ // const fetchDefaultCar = async () => {
|
|
|
+ // setIsDefaultCarLoading(true);
|
|
|
+ // try {
|
|
|
+ // const response = await chargeStationService.getUserDefaultCars();
|
|
|
+ // if (response && response.data) {
|
|
|
+ // setDefaultCar(response.data.id);
|
|
|
+ // setSelectedCarID(response.data.id);
|
|
|
+ // setCarCapacitance(response.data.car_type.capacitance);
|
|
|
+ // setSelectedCar(response.data.id);
|
|
|
+ // console.log('*******', response.data.car_type.capacitance);
|
|
|
+ // }
|
|
|
+ // } catch (error) {
|
|
|
+ // console.log('Error fetching default car:', error);
|
|
|
+ // } finally {
|
|
|
+ // setIsDefaultCarLoading(false);
|
|
|
+ // }
|
|
|
+ // };
|
|
|
+ // fetchDefaultCar();
|
|
|
+ // }, []);
|
|
|
+ // const formatDistance = (distanceInMeters: number): string => {
|
|
|
+ // if (distanceInMeters < 1000) {
|
|
|
+ // return `${Math.round(distanceInMeters)}米`;
|
|
|
+ // } else {
|
|
|
+ // const distanceInKm = distanceInMeters / 1000;
|
|
|
+ // return `${distanceInKm.toFixed(1)}公里`;
|
|
|
+ // }
|
|
|
+ // };
|
|
|
+ // const getUpcomingReservations = (reservations: any, daysAhead = 3) => {
|
|
|
+ // const today = new Date();
|
|
|
+ // const threeDaysLater = new Date(today);
|
|
|
+ // threeDaysLater.setDate(today.getDate() + daysAhead);
|
|
|
+
|
|
|
+ // return reservations
|
|
|
+ // .filter((reservation) => {
|
|
|
+ // const reservationDate = new Date(reservation.book_time);
|
|
|
+ // return reservationDate >= today && reservationDate <= threeDaysLater;
|
|
|
+ // })
|
|
|
+ // .sort((a, b) => new Date(a.reservationDate) - new Date(b.reservationDate));
|
|
|
+ // };
|
|
|
|
|
|
//USE BELOW to find upcoming reservations, then I filter availableDate and time based on the upcoming ones so user cannot book the same time twice.
|
|
|
- const formatReservations = (reservations) => {
|
|
|
- const formattedReservations = {};
|
|
|
+ // const formatReservations = (reservations) => {
|
|
|
+ // const formattedReservations = {};
|
|
|
|
|
|
- reservations.forEach((reservation) => {
|
|
|
- const bookTime = new Date(reservation.book_time);
|
|
|
- const date = formatDate(bookTime);
|
|
|
- const time = formatTime(bookTime);
|
|
|
+ // reservations.forEach((reservation) => {
|
|
|
+ // const bookTime = new Date(reservation.book_time);
|
|
|
+ // const date = formatDate(bookTime);
|
|
|
+ // const time = formatTime(bookTime);
|
|
|
|
|
|
- if (!formattedReservations[date]) {
|
|
|
- formattedReservations[date] = [];
|
|
|
- }
|
|
|
- formattedReservations[date].push(time);
|
|
|
- });
|
|
|
+ // if (!formattedReservations[date]) {
|
|
|
+ // formattedReservations[date] = [];
|
|
|
+ // }
|
|
|
+ // formattedReservations[date].push(time);
|
|
|
+ // });
|
|
|
|
|
|
- return Object.entries(formattedReservations).map(([date, times]) => ({
|
|
|
- date,
|
|
|
- times
|
|
|
- }));
|
|
|
- };
|
|
|
+ // return Object.entries(formattedReservations).map(([date, times]) => ({
|
|
|
+ // date,
|
|
|
+ // times
|
|
|
+ // }));
|
|
|
+ // };
|
|
|
|
|
|
- const formatDate = (date) => {
|
|
|
- const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
|
- const day = String(date.getDate()).padStart(2, '0');
|
|
|
- return `${month}/${day}`;
|
|
|
- };
|
|
|
+ // const formatDate = (date) => {
|
|
|
+ // const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
|
+ // const day = String(date.getDate()).padStart(2, '0');
|
|
|
+ // return `${month}/${day}`;
|
|
|
+ // };
|
|
|
+
|
|
|
+ // const formatTime = (date) => {
|
|
|
+ // return date.toTimeString().slice(0, 5);
|
|
|
+ // };
|
|
|
+
|
|
|
+ // useEffect(() => {
|
|
|
+ // const fetchReservationHistories = async () => {
|
|
|
+ // try {
|
|
|
+ // const response = await chargeStationService.fetchReservationHistories();
|
|
|
+ // if (response) {
|
|
|
+ // // console.log('response', response);
|
|
|
+ // // console.log('Reservation histories:', response);
|
|
|
+ // const upcomingReservations = getUpcomingReservations(response);
|
|
|
+ // // console.log('upcomingReservations', upcomingReservations);
|
|
|
+ // const formattedReservations = formatReservations(upcomingReservations);
|
|
|
+ // // console.log('formattedReservations', formattedReservations);
|
|
|
+ // setUpcomingReservation(formattedReservations);
|
|
|
+ // }
|
|
|
+ // 2;
|
|
|
+ // } catch (error) {
|
|
|
+ // console.log(error);
|
|
|
+ // }
|
|
|
+ // };
|
|
|
+ // fetchReservationHistories();
|
|
|
+ // }, []);
|
|
|
|
|
|
- const formatTime = (date) => {
|
|
|
- return date.toTimeString().slice(0, 5);
|
|
|
- };
|
|
|
- useEffect(() => {
|
|
|
- const fetchReservationHistories = async () => {
|
|
|
- try {
|
|
|
- const response = await chargeStationService.fetchReservationHistories();
|
|
|
- if (response) {
|
|
|
- // console.log('response', response);
|
|
|
- // console.log('Reservation histories:', response);
|
|
|
- const upcomingReservations = getUpcomingReservations(response);
|
|
|
- // console.log('upcomingReservations', upcomingReservations);
|
|
|
- const formattedReservations = formatReservations(upcomingReservations);
|
|
|
- // console.log('formattedReservations', formattedReservations);
|
|
|
- setUpcomingReservation(formattedReservations);
|
|
|
- }
|
|
|
- 2;
|
|
|
- } catch (error) {
|
|
|
- console.log(error);
|
|
|
- }
|
|
|
- };
|
|
|
- fetchReservationHistories();
|
|
|
- }, []);
|
|
|
//USE ABOVE to find upcoming reservations, then I filter availableDate and time based on the upcoming ones so user cannot book the same time twice.
|
|
|
|
|
|
- useEffect(() => {
|
|
|
- const getDistance = async () => {
|
|
|
- if (currentLocation) {
|
|
|
- try {
|
|
|
- const distance = await calculateDistance(
|
|
|
- Number(params.chargeStationLat),
|
|
|
- Number(params.chargeStationLng),
|
|
|
- currentLocation
|
|
|
- );
|
|
|
- setDistance(formatDistance(distance));
|
|
|
- } catch (error) {
|
|
|
- console.error('Error calculating distance:', error);
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
+ // useEffect(() => {
|
|
|
+ // const getDistance = async () => {
|
|
|
+ // if (currentLocation) {
|
|
|
+ // try {
|
|
|
+ // const distance = await calculateDistance(
|
|
|
+ // Number(params.chargeStationLat),
|
|
|
+ // Number(params.chargeStationLng),
|
|
|
+ // currentLocation
|
|
|
+ // );
|
|
|
+ // setDistance(formatDistance(distance));
|
|
|
+ // } catch (error) {
|
|
|
+ // console.error('Error calculating distance:', error);
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // };
|
|
|
|
|
|
- getDistance();
|
|
|
- }, [params.chargeStationLat, params.chargeStationLng, currentLocation]);
|
|
|
+ // getDistance();
|
|
|
+ // }, [params.chargeStationLat, params.chargeStationLng, currentLocation]);
|
|
|
|
|
|
useEffect(() => {
|
|
|
const fetchPrice = async () => {
|
|
|
@@ -228,18 +346,18 @@ const MakingBookingPageComponent = () => {
|
|
|
fetchPrice();
|
|
|
}, []);
|
|
|
|
|
|
- function appendImageUrlToCarResult(carData, updatedVehicles) {
|
|
|
- return carData.map((car) => {
|
|
|
- const matchingVehicle = updatedVehicles.find((vehicle) => vehicle.car_type.name === car.car_name);
|
|
|
- if (matchingVehicle) {
|
|
|
- return {
|
|
|
- ...car,
|
|
|
- type_image_url: matchingVehicle.car_type.type_image_url
|
|
|
- };
|
|
|
- }
|
|
|
- return car;
|
|
|
- });
|
|
|
- }
|
|
|
+ // function appendImageUrlToCarResult(carData, updatedVehicles) {
|
|
|
+ // return carData.map((car) => {
|
|
|
+ // const matchingVehicle = updatedVehicles.find((vehicle) => vehicle.car_type.name === car.car_name);
|
|
|
+ // if (matchingVehicle) {
|
|
|
+ // return {
|
|
|
+ // ...car,
|
|
|
+ // type_image_url: matchingVehicle.car_type.type_image_url
|
|
|
+ // };
|
|
|
+ // }
|
|
|
+ // return car;
|
|
|
+ // });
|
|
|
+ // }
|
|
|
|
|
|
// useEffect(() => {
|
|
|
// setCarLoadingState(true);
|
|
|
@@ -290,87 +408,87 @@ const MakingBookingPageComponent = () => {
|
|
|
// fetchUserInfoAndCarData();
|
|
|
// }, []);
|
|
|
|
|
|
- useEffect(() => {
|
|
|
- const fetchingAvailableTimeSlots = async () => {
|
|
|
- setIsLoading(true);
|
|
|
- try {
|
|
|
- const fetchedTimeSlots = await chargeStationService.fetchAvailableTimeSlots(
|
|
|
- chargeStationID,
|
|
|
- selectedDate
|
|
|
- );
|
|
|
- const now = new Date();
|
|
|
- const today = `${String(now.getMonth() + 1).padStart(2, '0')}/${String(now.getDate()).padStart(
|
|
|
- 2,
|
|
|
- '0'
|
|
|
- )}`;
|
|
|
-
|
|
|
- let filteredTimeSlots = fetchedTimeSlots;
|
|
|
-
|
|
|
- //filter out today's time slots that have already passed
|
|
|
- if (selectedDate === today) {
|
|
|
- const currentHours = now.getHours();
|
|
|
- const currentMinutes = now.getMinutes();
|
|
|
-
|
|
|
- filteredTimeSlots = fetchedTimeSlots.filter((time) => {
|
|
|
- const [hours, minutes] = time.split(':').map(Number);
|
|
|
- if (hours > currentHours) return true;
|
|
|
- if (hours === currentHours && minutes > currentMinutes) return true;
|
|
|
- return false;
|
|
|
- });
|
|
|
- }
|
|
|
- //filter out time slots that are already fully booked
|
|
|
- const reservedSlotsForDate = upcomingReservations.find((res) => res.date === selectedDate);
|
|
|
+ // useEffect(() => {
|
|
|
+ // const fetchingAvailableTimeSlots = async () => {
|
|
|
+ // setIsLoading(true);
|
|
|
+ // try {
|
|
|
+ // const fetchedTimeSlots = await chargeStationService.fetchAvailableTimeSlots(
|
|
|
+ // chargeStationID,
|
|
|
+ // selectedDate
|
|
|
+ // );
|
|
|
+ // const now = new Date();
|
|
|
+ // const today = `${String(now.getMonth() + 1).padStart(2, '0')}/${String(now.getDate()).padStart(
|
|
|
+ // 2,
|
|
|
+ // '0'
|
|
|
+ // )}`;
|
|
|
|
|
|
- if (reservedSlotsForDate) {
|
|
|
- filteredTimeSlots = filteredTimeSlots.filter((time) => !reservedSlotsForDate.times.includes(time));
|
|
|
- }
|
|
|
- setAvailableTimeSlots(filteredTimeSlots);
|
|
|
- } catch (error) {
|
|
|
- console.error('Error fetching time slots:', error);
|
|
|
- } finally {
|
|
|
- setIsLoading(false);
|
|
|
- }
|
|
|
- };
|
|
|
+ // let filteredTimeSlots = fetchedTimeSlots;
|
|
|
|
|
|
- if (selectedDate) {
|
|
|
- fetchingAvailableTimeSlots();
|
|
|
- }
|
|
|
- }, [selectedDate]);
|
|
|
+ // //filter out today's time slots that have already passed
|
|
|
+ // if (selectedDate === today) {
|
|
|
+ // const currentHours = now.getHours();
|
|
|
+ // const currentMinutes = now.getMinutes();
|
|
|
|
|
|
- useEffect(() => {
|
|
|
- const fetchConnectorOptions = async () => {
|
|
|
- try {
|
|
|
- const fetchedData = await chargeStationService.fetchSpecificChargeStation(chargeStationID);
|
|
|
- console.log('fetchedDate', fetchedData);
|
|
|
- const dateObject = fetchedData.find((item) => item.date === selectedDate);
|
|
|
- console.log('dateObject', dateObject);
|
|
|
- if (!dateObject) {
|
|
|
- setAvailableConnectorDropdownOptions([]);
|
|
|
- return;
|
|
|
- }
|
|
|
+ // filteredTimeSlots = fetchedTimeSlots.filter((time) => {
|
|
|
+ // const [hours, minutes] = time.split(':').map(Number);
|
|
|
+ // if (hours > currentHours) return true;
|
|
|
+ // if (hours === currentHours && minutes > currentMinutes) return true;
|
|
|
+ // return false;
|
|
|
+ // });
|
|
|
+ // }
|
|
|
+ // //filter out time slots that are already fully booked
|
|
|
+ // const reservedSlotsForDate = upcomingReservations.find((res) => res.date === selectedDate);
|
|
|
|
|
|
- const rangeObject = dateObject.range.find((range) => range.start === selectedTime);
|
|
|
- console.log('rangeObject', rangeObject);
|
|
|
- if (!rangeObject) {
|
|
|
- setAvailableConnectorDropdownOptions([]);
|
|
|
- return;
|
|
|
- }
|
|
|
+ // if (reservedSlotsForDate) {
|
|
|
+ // filteredTimeSlots = filteredTimeSlots.filter((time) => !reservedSlotsForDate.times.includes(time));
|
|
|
+ // }
|
|
|
+ // setAvailableTimeSlots(filteredTimeSlots);
|
|
|
+ // } catch (error) {
|
|
|
+ // console.error('Error fetching time slots:', error);
|
|
|
+ // } finally {
|
|
|
+ // setIsLoading(false);
|
|
|
+ // }
|
|
|
+ // };
|
|
|
|
|
|
- const connectorIDs = rangeObject.connectors
|
|
|
- .filter((connector) => connector.Status === '待机')
|
|
|
- .map((connector) => connector.ConnectorID);
|
|
|
+ // if (selectedDate) {
|
|
|
+ // fetchingAvailableTimeSlots();
|
|
|
+ // }
|
|
|
+ // }, [selectedDate]);
|
|
|
|
|
|
- console.log('connectorIDs', connectorIDs);
|
|
|
+ // useEffect(() => {
|
|
|
+ // const fetchConnectorOptions = async () => {
|
|
|
+ // try {
|
|
|
+ // const fetchedData = await chargeStationService.fetchSpecificChargeStation(chargeStationID);
|
|
|
+ // console.log('fetchedDate', fetchedData);
|
|
|
+ // const dateObject = fetchedData.find((item) => item.date === selectedDate);
|
|
|
+ // console.log('dateObject', dateObject);
|
|
|
+ // if (!dateObject) {
|
|
|
+ // setAvailableConnectorDropdownOptions([]);
|
|
|
+ // return;
|
|
|
+ // }
|
|
|
|
|
|
- setAvailableConnectorDropdownOptions(connectorIDs);
|
|
|
- } catch (error) {
|
|
|
- console.error('Error fetching charge station data:', error);
|
|
|
- setAvailableConnectorDropdownOptions([]);
|
|
|
- }
|
|
|
- };
|
|
|
+ // const rangeObject = dateObject.range.find((range) => range.start === selectedTime);
|
|
|
+ // console.log('rangeObject', rangeObject);
|
|
|
+ // if (!rangeObject) {
|
|
|
+ // setAvailableConnectorDropdownOptions([]);
|
|
|
+ // return;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // const connectorIDs = rangeObject.connectors
|
|
|
+ // .filter((connector) => connector.Status === '待机')
|
|
|
+ // .map((connector) => connector.ConnectorID);
|
|
|
|
|
|
- fetchConnectorOptions();
|
|
|
- }, [chargeStationID, selectedDate, selectedTime]);
|
|
|
+ // console.log('connectorIDs', connectorIDs);
|
|
|
+
|
|
|
+ // setAvailableConnectorDropdownOptions(connectorIDs);
|
|
|
+ // } catch (error) {
|
|
|
+ // console.error('Error fetching charge station data:', error);
|
|
|
+ // setAvailableConnectorDropdownOptions([]);
|
|
|
+ // }
|
|
|
+ // };
|
|
|
+
|
|
|
+ // fetchConnectorOptions();
|
|
|
+ // }, [chargeStationID, selectedDate, selectedTime]);
|
|
|
|
|
|
// old
|
|
|
// const formattedConnectorDropdownOptions = availableConnectorDropdownOptions.map((id, index) => ({
|
|
|
@@ -386,34 +504,37 @@ const MakingBookingPageComponent = () => {
|
|
|
'101708240502474002': '6'
|
|
|
};
|
|
|
|
|
|
- console.log('availableConnectorDropdownOptions', availableConnectorDropdownOptions);
|
|
|
- const formattedConnectorDropdownOptions = availableConnectorDropdownOptions
|
|
|
- .map((id) => ({
|
|
|
- label: connectorIDToLabelMap[id] || '',
|
|
|
- value: id
|
|
|
- }))
|
|
|
- .filter((option) => option.label !== '')
|
|
|
- .sort((a, b) => parseInt(a.label) - parseInt(b.label));
|
|
|
- console.log('formattedConnectorDropdownOptions', formattedConnectorDropdownOptions);
|
|
|
+ const connectorIDToLabelMapArray = [
|
|
|
+ { value: '101708240502475001', label: '1' },
|
|
|
+ { value: '101708240502476001', label: '2' },
|
|
|
+ { value: '101708240502477001', label: '3' },
|
|
|
+ { value: '101708240502478001', label: '4' },
|
|
|
+ { value: '101708240502474001', label: '5' },
|
|
|
+ { value: '101708240502474002', label: '6' }
|
|
|
+ ];
|
|
|
+ const formatDateString = (dateString: string) => {
|
|
|
+ const [year, month, day] = dateString.split('-');
|
|
|
+ return `${month}/${day}`;
|
|
|
+ };
|
|
|
|
|
|
- useEffect(() => {
|
|
|
- const fetchingAvailableDates = async () => {
|
|
|
- setIsDateLoading(true);
|
|
|
- try {
|
|
|
- const fetchedDates = await chargeStationService.fetchAvailableDates(chargeStationID);
|
|
|
- console.log('fetchedDates', fetchedDates);
|
|
|
- setAvailableDate(fetchedDates);
|
|
|
+ // useEffect(() => {
|
|
|
+ // const fetchingAvailableDates = async () => {
|
|
|
+ // setIsDateLoading(true);
|
|
|
+ // try {
|
|
|
+ // const fetchedDates = await chargeStationService.fetchAvailableDates(chargeStationID);
|
|
|
+ // console.log('fetchedDates', fetchedDates);
|
|
|
+ // setAvailableDate(fetchedDates);
|
|
|
|
|
|
- console.log(fetchedDates.slice(0, 3));
|
|
|
- } catch (error) {
|
|
|
- console.error('Error fetching available dates:', error);
|
|
|
- } finally {
|
|
|
- setIsDateLoading(false);
|
|
|
- }
|
|
|
- };
|
|
|
+ // console.log(fetchedDates.slice(0, 3));
|
|
|
+ // } catch (error) {
|
|
|
+ // console.error('Error fetching available dates:', error);
|
|
|
+ // } finally {
|
|
|
+ // setIsDateLoading(false);
|
|
|
+ // }
|
|
|
+ // };
|
|
|
|
|
|
- fetchingAvailableDates();
|
|
|
- }, [chargeStationID]);
|
|
|
+ // fetchingAvailableDates();
|
|
|
+ // }, [chargeStationID]);
|
|
|
|
|
|
const handleNavigationPress = () => {
|
|
|
const latitude = chargeStationLat;
|
|
|
@@ -447,6 +568,44 @@ const MakingBookingPageComponent = () => {
|
|
|
.catch((err) => console.error('An error occurred', err));
|
|
|
};
|
|
|
|
|
|
+ const handleDateToTimeSlot = (date: string, connectorId: string) => {
|
|
|
+ // Find the correct connector object
|
|
|
+ const connectorData = dataResponse.find((item) => item.connector === connectorId);
|
|
|
+
|
|
|
+ if (!connectorData) {
|
|
|
+ console.error(`No data found for connector ${connectorId}`);
|
|
|
+ setAvailableTimeSlots([]);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Find the timeSlot object for the selected date
|
|
|
+ const selectedTimeSlot = connectorData.timeSlot.find((slot) => slot.date === date);
|
|
|
+
|
|
|
+ if (!selectedTimeSlot) {
|
|
|
+ console.error(`No time slots found for date ${date}`);
|
|
|
+ setAvailableTimeSlots([]);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const now = new Date();
|
|
|
+ const selectedDateObj = new Date(date);
|
|
|
+ const isToday = selectedDateObj.toDateString() === now.toDateString();
|
|
|
+
|
|
|
+ let filteredSlots = selectedTimeSlot.availableTime;
|
|
|
+
|
|
|
+ if (isToday) {
|
|
|
+ filteredSlots = filteredSlots.filter((slot) => {
|
|
|
+ const [hours, minutes] = slot.startTime.split(':').map(Number);
|
|
|
+ const slotTime = new Date(selectedDateObj);
|
|
|
+ slotTime.setHours(hours, minutes, 0, 0);
|
|
|
+ return slotTime > now;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('Available Time Slots:', filteredSlots);
|
|
|
+ setAvailableTimeSlots(filteredSlots);
|
|
|
+ };
|
|
|
+
|
|
|
const handleConfirmation = (value: any) => {
|
|
|
setModalVisible(true);
|
|
|
const selectedOption = formattedConnectorDropdownOptions.find((option) => option.value === value);
|
|
|
@@ -473,11 +632,10 @@ const MakingBookingPageComponent = () => {
|
|
|
};
|
|
|
|
|
|
const handleModalConfirm = () => {
|
|
|
- // Alert.alert('提示', '此功能正在準備中,敬請期待', [{ text: '確定', onPress: () => router.push('/mainPage') }], {
|
|
|
- // cancelable: false
|
|
|
- // });
|
|
|
setModalVisible(false);
|
|
|
+
|
|
|
if (routerParams) {
|
|
|
+ console.log('routerParams', routerParams);
|
|
|
router.push(routerParams);
|
|
|
}
|
|
|
};
|
|
|
@@ -616,6 +774,7 @@ const MakingBookingPageComponent = () => {
|
|
|
setSelectedWatt('');
|
|
|
setOpenDrawer(1);
|
|
|
setSelectedDrawer(1);
|
|
|
+ setSelectedChargingGun('');
|
|
|
}}
|
|
|
>
|
|
|
<View className="mx-[5%] ">
|
|
|
@@ -687,6 +846,9 @@ const MakingBookingPageComponent = () => {
|
|
|
setSelectedWatt(watt);
|
|
|
setOpenDrawer(2);
|
|
|
setSelectedDrawer(2);
|
|
|
+ handleSendingSize(watt);
|
|
|
+ console.log('selectedWatt', selectedWatt);
|
|
|
+ console.log('watt', watt);
|
|
|
}}
|
|
|
>
|
|
|
<Text
|
|
|
@@ -706,35 +868,126 @@ const MakingBookingPageComponent = () => {
|
|
|
)}
|
|
|
</AccordionItem>
|
|
|
)}
|
|
|
- {selectedTime !== '' ? (
|
|
|
- <Pressable
|
|
|
- onPress={() => {
|
|
|
- setOpenDrawer(2);
|
|
|
- setSelectedDrawer(2);
|
|
|
- setSelectedDate('');
|
|
|
- setSelectedTime('');
|
|
|
- }}
|
|
|
- >
|
|
|
- <View className="mx-[5%] ">
|
|
|
- <View className="flex-row items-center pt-4">
|
|
|
- <Text className="text-lg pr-2 text-[#34667c]">選擇日期</Text>
|
|
|
- <CheckMarkLogoSvg />
|
|
|
+ {/* select gun */}
|
|
|
+ {/* select gun */}
|
|
|
+ <View className="">
|
|
|
+ {selectedChargingGun !== '' ? (
|
|
|
+ <Pressable
|
|
|
+ onPress={() => {
|
|
|
+ setSelectedChargingGun('');
|
|
|
+ setOpenDrawer(2);
|
|
|
+ setSelectedDrawer(2);
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <View className="mx-[5%]">
|
|
|
+ <View className="flex-row items-center pt-4">
|
|
|
+ <Text className="text-lg pr-2 text-[#34667c]">選擇充電座</Text>
|
|
|
+ <CheckMarkLogoSvg />
|
|
|
+ </View>
|
|
|
+ <View className="text-lg pb-4 flex flex-row items-center">
|
|
|
+ <Text className="text-[#34667c] font-[600] text-2xl pr-2">
|
|
|
+ {connectorIDToLabelMap[selectedChargingGun]}
|
|
|
+ </Text>
|
|
|
+ <Text className="text-lg">號充電座</Text>
|
|
|
+ </View>
|
|
|
</View>
|
|
|
- <Text className="text-lg pb-4">
|
|
|
- {selectedDate} - {selectedTime}
|
|
|
- </Text>
|
|
|
- </View>
|
|
|
- </Pressable>
|
|
|
+ </Pressable>
|
|
|
+ ) : (
|
|
|
+ <AccordionItem
|
|
|
+ title="選擇充電座"
|
|
|
+ isOpen={openDrawer === 2}
|
|
|
+ onToggle={() => {
|
|
|
+ if (selectedWatt) {
|
|
|
+ toggleDrawer(2);
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ isSelected={selectedDrawer === 2}
|
|
|
+ >
|
|
|
+ {selectedWatt !== '' ? (
|
|
|
+ <View className="">
|
|
|
+ <DropdownSelect
|
|
|
+ dropdownOptions={connectorIDToLabelMapArray}
|
|
|
+ placeholder={'選擇充電座號碼'}
|
|
|
+ onSelect={(value) => {
|
|
|
+ setSelectedChargingGun(value);
|
|
|
+ setSelectedDrawer(3);
|
|
|
+ setOpenDrawer(3);
|
|
|
+ }}
|
|
|
+ extendedStyle={{
|
|
|
+ borderColor: '#34667c',
|
|
|
+ marginTop: 4,
|
|
|
+ padding: 12
|
|
|
+ }}
|
|
|
+ />
|
|
|
+
|
|
|
+ <Image
|
|
|
+ style={{
|
|
|
+ width: layoutWidth * 0.9,
|
|
|
+ height: layoutHeight
|
|
|
+ }}
|
|
|
+ resizeMode="contain"
|
|
|
+ source={require('../../assets/floorPlan1.png')}
|
|
|
+ />
|
|
|
+ </View>
|
|
|
+ ) : (
|
|
|
+ <Text className="text-base text-gray-500 py-2">請先選擇充電方案</Text>
|
|
|
+ )}
|
|
|
+ </AccordionItem>
|
|
|
+ )}
|
|
|
+ </View>
|
|
|
+ <Modal
|
|
|
+ isVisible={isModalVisible}
|
|
|
+ // onBackdropPress={() => setModalVisible(false)}
|
|
|
+ backdropOpacity={0.5}
|
|
|
+ animationIn="fadeIn"
|
|
|
+ animationOut="fadeOut"
|
|
|
+ >
|
|
|
+ <View style={styles.modalContent}>
|
|
|
+ <Text className="text-2xl font-[500] text-[#34667c] mb-6">
|
|
|
+ 已選擇日期時間: {formatDateString(selectedDate)} - {selectedTime}
|
|
|
+ </Text>
|
|
|
+ <Text style={styles.modalText} className="text-[#34667c]">
|
|
|
+ 若客戶逾時超過15分鐘,系統將視作自動放棄預約,客戶需要重新預約一次。
|
|
|
+ 本公司有權保留全數費用,恕不退還。按下確認代表您已閱讀並同意上述條款。
|
|
|
+ </Text>
|
|
|
+ <NormalButton
|
|
|
+ title={<Text className="text-white">我確認</Text>}
|
|
|
+ onPress={handleModalConfirm}
|
|
|
+ extendedStyle={styles.confirmButton}
|
|
|
+ />
|
|
|
+ </View>
|
|
|
+ </Modal>
|
|
|
+ {selectedDate !== '' && selectedTime !== '' ? (
|
|
|
+ <>
|
|
|
+ <Pressable
|
|
|
+ onPress={() => {
|
|
|
+ setOpenDrawer(3);
|
|
|
+ setSelectedDrawer(3);
|
|
|
+ setSelectedDate('');
|
|
|
+ setSelectedTime('');
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <View className="mx-[5%] ">
|
|
|
+ <View className="flex-row items-center pt-4">
|
|
|
+ <Text className="text-lg pr-2 text-[#34667c]">選擇日期</Text>
|
|
|
+ <CheckMarkLogoSvg />
|
|
|
+ </View>
|
|
|
+ <Text className="text-lg pb-4">
|
|
|
+ {formatDateString(selectedDate)} - {selectedTime}
|
|
|
+ </Text>
|
|
|
+ </View>
|
|
|
+ </Pressable>
|
|
|
+ </>
|
|
|
) : (
|
|
|
<AccordionItem
|
|
|
title="選擇日期 (月/日)"
|
|
|
- isOpen={openDrawer === 2}
|
|
|
+ isOpen={openDrawer === 3}
|
|
|
onToggle={() => {
|
|
|
if (stopChargingUponBatteryFull !== false || selectedDuration !== '') {
|
|
|
- toggleDrawer(2);
|
|
|
+ toggleDrawer(3);
|
|
|
}
|
|
|
}}
|
|
|
- isSelected={selectedDrawer === 2}
|
|
|
+ isSelected={selectedDrawer === 3}
|
|
|
>
|
|
|
{isDateLoading ? (
|
|
|
<View className="flex-1 items-center justify-center py-4">
|
|
|
@@ -742,7 +995,7 @@ const MakingBookingPageComponent = () => {
|
|
|
</View>
|
|
|
) : (
|
|
|
<View className="flex-row w-full flex-wrap mb-1 ">
|
|
|
- {availableDate.slice(0, 3).map((date) => (
|
|
|
+ {availableDate.map((date) => (
|
|
|
<Pressable
|
|
|
key={date}
|
|
|
className={`${
|
|
|
@@ -750,6 +1003,7 @@ const MakingBookingPageComponent = () => {
|
|
|
} border border-[#34667c] rounded-lg w-[22%] items-center mt-1 mr-1 mb-1`}
|
|
|
onPress={() => {
|
|
|
setSelectedDate(date);
|
|
|
+ handleDateToTimeSlot(date, selectedChargingGun);
|
|
|
}}
|
|
|
>
|
|
|
<Text
|
|
|
@@ -757,7 +1011,7 @@ const MakingBookingPageComponent = () => {
|
|
|
selectedDate === date ? 'text-white' : 'text-[#34667c]'
|
|
|
} `}
|
|
|
>
|
|
|
- {date}
|
|
|
+ {formatDateString(date)}
|
|
|
</Text>
|
|
|
</Pressable>
|
|
|
))}
|
|
|
@@ -772,24 +1026,48 @@ const MakingBookingPageComponent = () => {
|
|
|
</View>
|
|
|
) : (
|
|
|
<View className="flex-row w-full mb-3 flex-wrap my-2 ">
|
|
|
- {availableTimeSlots.map((time) => (
|
|
|
+ {availableTimeSlots.map((slot, index) => (
|
|
|
<Pressable
|
|
|
- key={time}
|
|
|
+ key={index}
|
|
|
className={`${
|
|
|
- selectedTime === time ? 'bg-[#34667c] ' : 'bg-white'
|
|
|
+ selectedTime === slot.startTime ? 'bg-[#34667c] ' : 'bg-white'
|
|
|
} border border-[#34667c] mr-2 rounded-lg w-[22%] items-center mb-2`}
|
|
|
onPress={() => {
|
|
|
- setSelectedTime(time);
|
|
|
- setOpenDrawer(3);
|
|
|
- setSelectedDrawer(3);
|
|
|
+ setSelectedTime(slot.startTime);
|
|
|
+ setRouterParams({
|
|
|
+ pathname: '/bookingConfirmationPage',
|
|
|
+ params: {
|
|
|
+ chargeStationName,
|
|
|
+ chargeStationAddress,
|
|
|
+ chargeStationID,
|
|
|
+ connectorID: selectedChargingGun,
|
|
|
+ connectorLabel:
|
|
|
+ connectorIDToLabelMap[selectedChargingGun],
|
|
|
+ userID,
|
|
|
+ carCapacitance: carCapacitance,
|
|
|
+ carID: selectedCarID,
|
|
|
+ carName: selectedCar,
|
|
|
+ date: selectedDate,
|
|
|
+ bookTime: slot.startTime,
|
|
|
+ endTime: slot.endTime,
|
|
|
+ chargingMethod: stopChargingUponBatteryFull
|
|
|
+ ? 'stopChargingUponBatteryFull'
|
|
|
+ : 'chargingBasedOnWatt',
|
|
|
+ chargingWatt: selectedWatt || '',
|
|
|
+ price: price
|
|
|
+ }
|
|
|
+ });
|
|
|
+ setModalVisible(true);
|
|
|
}}
|
|
|
>
|
|
|
<Text
|
|
|
className={`text-base p-2 ${
|
|
|
- selectedTime === time ? 'text-white' : 'text-[#34667c]'
|
|
|
+ selectedTime === slot.startTime
|
|
|
+ ? 'text-white'
|
|
|
+ : 'text-[#34667c]'
|
|
|
} `}
|
|
|
>
|
|
|
- {time}
|
|
|
+ {slot.startTime}
|
|
|
</Text>
|
|
|
</Pressable>
|
|
|
))}
|
|
|
@@ -799,7 +1077,8 @@ const MakingBookingPageComponent = () => {
|
|
|
)}
|
|
|
</AccordionItem>
|
|
|
)}
|
|
|
- <View className="">
|
|
|
+
|
|
|
+ {/* <View className="">
|
|
|
<AccordionItem
|
|
|
title="選擇充電座"
|
|
|
isOpen={openDrawer === 3}
|
|
|
@@ -855,30 +1134,9 @@ const MakingBookingPageComponent = () => {
|
|
|
</Modal>
|
|
|
</View>
|
|
|
</AccordionItem>
|
|
|
- </View>
|
|
|
+ </View> */}
|
|
|
</View>
|
|
|
|
|
|
- {/* {routerParams ? (
|
|
|
- <View className="mx-[5%] my-4">
|
|
|
- <NormalButton
|
|
|
- title={
|
|
|
- <Text
|
|
|
- style={{
|
|
|
- fontWeight: '700',
|
|
|
- fontSize: 20,
|
|
|
- color: '#fff'
|
|
|
- }}
|
|
|
- >
|
|
|
- 新增
|
|
|
- </Text>
|
|
|
- }
|
|
|
- onPress={() => handleConfirmation(selectedChargingGun)}
|
|
|
- />
|
|
|
- </View>
|
|
|
- ) : (
|
|
|
- ''
|
|
|
- )} */}
|
|
|
-
|
|
|
<View className="mx-[5%] flex-1">
|
|
|
<Text className="text-xl pb-2 mt-6" style={styles.text}>
|
|
|
充電站資訊
|