|
|
@@ -18,6 +18,11 @@ import { chargeStationService } from '../../../../service/chargeStationService';
|
|
|
import { authenticationService } from '../../../../service/authService';
|
|
|
import { walletService } from '../../../../service/walletService';
|
|
|
import useUserInfoStore from '../../../../providers/userinfo_store';
|
|
|
+import Modal from 'react-native-modal';
|
|
|
+import NormalButton from '../../../../component/global/normal_button';
|
|
|
+import { ceil } from 'lodash';
|
|
|
+import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
|
+
|
|
|
const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
|
|
|
|
|
|
const ChooseCar = ({ carData, loading, selectedCar, setSelectedCar }) => {
|
|
|
@@ -122,6 +127,23 @@ const ScanQrPage = () => {
|
|
|
const [loading2, setLoading2] = useState(false);
|
|
|
const [loading3, setLoading3] = useState(false);
|
|
|
const [carData, setCarData] = useState([]);
|
|
|
+ const [isModalVisible, setModalVisible] = useState(false);
|
|
|
+ const [availableSlots, setAvailableSlots] = useState({
|
|
|
+ 25: false,
|
|
|
+ 30: false,
|
|
|
+ 40: false,
|
|
|
+ 45: false,
|
|
|
+ full: false
|
|
|
+ });
|
|
|
+ const [selectedDuration, setSelectedDuration] = useState(null);
|
|
|
+
|
|
|
+ const planMap = {
|
|
|
+ 25: { duration: 40, fee: 70, kWh: 20, displayDuration: 25 },
|
|
|
+ 30: { duration: 45, fee: 87.5, kWh: 25, displayDuration: 30 },
|
|
|
+ 40: { duration: 55, fee: 105, kWh: 30, displayDuration: 40 },
|
|
|
+ 45: { duration: 60, fee: 140, kWh: 40, displayDuration: 45 },
|
|
|
+ full: { duration: 120, fee: 280, displayDuration: '充滿停機' } // Assuming 2 hours for "充滿停機"
|
|
|
+ };
|
|
|
|
|
|
// Effect for requesting camera permissions
|
|
|
useEffect(() => {
|
|
|
@@ -136,31 +158,48 @@ const ScanQrPage = () => {
|
|
|
}, []);
|
|
|
|
|
|
// Effect for fetching user's cars
|
|
|
+ // useEffect(() => {
|
|
|
+ // const fetchAllCars = async () => {
|
|
|
+ // try {
|
|
|
+ // const response = await chargeStationService.getUserCars();
|
|
|
+ // if (response) {
|
|
|
+ // console.log('data', response.data);
|
|
|
+ // const carTypes = response.data.map((item: any) => ({
|
|
|
+ // id: item.id,
|
|
|
+ // name: item.car_type.name,
|
|
|
+ // image: item.car_type.type_image_url
|
|
|
+ // }));
|
|
|
+ // // console.log('carTypes', carTypes);
|
|
|
+ // // console.log('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', carTypes);
|
|
|
+ // let updatedCarTypes = [...carTypes];
|
|
|
+ // for (let i = 0; i < carTypes.length; i++) {
|
|
|
+ // const car = updatedCarTypes[i];
|
|
|
+ // const imageUrl = await chargeStationService.getProcessedImageUrl(car.image);
|
|
|
+ // updatedCarTypes[i] = {
|
|
|
+ // ...car,
|
|
|
+ // image: imageUrl
|
|
|
+ // };
|
|
|
+ // }
|
|
|
+ // setCarData(updatedCarTypes);
|
|
|
+ // // console.log('updatedCarTypes', updatedCarTypes);
|
|
|
+ // return true;
|
|
|
+ // }
|
|
|
+ // } catch (error) {
|
|
|
+ // console.log(error);
|
|
|
+ // } finally {
|
|
|
+ // setLoading(false);
|
|
|
+ // }
|
|
|
+ // };
|
|
|
+ // fetchAllCars();
|
|
|
+ // }, []);
|
|
|
+
|
|
|
useEffect(() => {
|
|
|
- const fetchAllCars = async () => {
|
|
|
+ const fetchDefaultCar = async () => {
|
|
|
try {
|
|
|
- const response = await chargeStationService.getUserCars();
|
|
|
+ const response = await chargeStationService.getUserDefaultCars();
|
|
|
if (response) {
|
|
|
- // console.log(response.data);
|
|
|
- const carTypes = response.data.map((item: any) => ({
|
|
|
- id: item.id,
|
|
|
- name: item.car_type.name,
|
|
|
- image: item.car_type.type_image_url
|
|
|
- }));
|
|
|
- console.log('carTypes', carTypes);
|
|
|
- // console.log('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', carTypes);
|
|
|
- let updatedCarTypes = [...carTypes];
|
|
|
- for (let i = 0; i < carTypes.length; i++) {
|
|
|
- const car = updatedCarTypes[i];
|
|
|
- const imageUrl = await chargeStationService.getProcessedImageUrl(car.image);
|
|
|
- updatedCarTypes[i] = {
|
|
|
- ...car,
|
|
|
- image: imageUrl
|
|
|
- };
|
|
|
- }
|
|
|
- setCarData(updatedCarTypes);
|
|
|
- console.log('updatedCarTypes', updatedCarTypes);
|
|
|
- return true;
|
|
|
+ console.log('default car', response.data.id);
|
|
|
+ setSelectedCar(response.data.id);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.log(error);
|
|
|
@@ -168,7 +207,7 @@ const ScanQrPage = () => {
|
|
|
setLoading(false);
|
|
|
}
|
|
|
};
|
|
|
- fetchAllCars();
|
|
|
+ fetchDefaultCar();
|
|
|
}, []);
|
|
|
|
|
|
if (!permission) {
|
|
|
@@ -208,109 +247,22 @@ const ScanQrPage = () => {
|
|
|
const response = await chargeStationService.getTodayReservation();
|
|
|
if (response) {
|
|
|
const now = new Date();
|
|
|
-
|
|
|
const onlyThisConnector = response.filter(
|
|
|
(reservation: any) => reservation.connector.ConnectorID === data
|
|
|
);
|
|
|
|
|
|
- onlyThisConnector.sort((a: any, b: any) => {
|
|
|
- const timeA = new Date(a.book_time).getTime();
|
|
|
- const timeB = new Date(b.book_time).getTime();
|
|
|
- return timeA - timeB;
|
|
|
- });
|
|
|
-
|
|
|
- let previousReservation = null;
|
|
|
- let upcomingReservation = null;
|
|
|
-
|
|
|
- for (let i = 0; i < onlyThisConnector.length; i++) {
|
|
|
- const reservationTime = new Date(onlyThisConnector[i].book_time).getTime();
|
|
|
- if (reservationTime <= now.getTime()) {
|
|
|
- previousReservation = onlyThisConnector[i];
|
|
|
- } else {
|
|
|
- upcomingReservation = onlyThisConnector[i];
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- const relevantReservations = [previousReservation || null, upcomingReservation || null];
|
|
|
-
|
|
|
- //this find the one res before and one res after current time.
|
|
|
- console.log('Relevant reservations:', relevantReservations);
|
|
|
-
|
|
|
- const getNearestSlot = (date: Date) => {
|
|
|
- const minutes = date.getMinutes();
|
|
|
- const nearestSlot = new Date(date);
|
|
|
- nearestSlot.setMinutes(minutes < 30 ? 0 : 30);
|
|
|
- nearestSlot.setSeconds(0);
|
|
|
- nearestSlot.setMilliseconds(0);
|
|
|
- return nearestSlot;
|
|
|
+ // Check availability for each duration
|
|
|
+ const availability = {
|
|
|
+ 25: checkAvailability(onlyThisConnector, now, 40),
|
|
|
+ 30: checkAvailability(onlyThisConnector, now, 45),
|
|
|
+ 40: checkAvailability(onlyThisConnector, now, 55),
|
|
|
+ 45: checkAvailability(onlyThisConnector, now, 60),
|
|
|
+ full: checkAvailability(onlyThisConnector, now, 120)
|
|
|
};
|
|
|
|
|
|
- const currentSlot = getNearestSlot(now);
|
|
|
- const nextSlot = new Date(currentSlot.getTime() + 30 * 60 * 1000);
|
|
|
-
|
|
|
- const AreOrdersAdjacentToNow = relevantReservations.map((reservation, index) => {
|
|
|
- if (!reservation) return false;
|
|
|
-
|
|
|
- const reservationTime = new Date(reservation.book_time);
|
|
|
- return index === 0
|
|
|
- ? reservationTime.getTime() === currentSlot.getTime()
|
|
|
- : reservationTime.getTime() === nextSlot.getTime();
|
|
|
- });
|
|
|
-
|
|
|
- console.log('AreOrdersAdjacentToNow:', AreOrdersAdjacentToNow);
|
|
|
-
|
|
|
- if (!AreOrdersAdjacentToNow[0] && !AreOrdersAdjacentToNow[1]) {
|
|
|
- console.log('Charging machine is available. Starting charging process...');
|
|
|
- startCharging(data);
|
|
|
- } else if (AreOrdersAdjacentToNow[0]) {
|
|
|
- const previousReservation = relevantReservations[0];
|
|
|
- if (previousReservation && previousReservation.user.id === userID) {
|
|
|
- const reservationTime = new Date(previousReservation.book_time);
|
|
|
- const timeDifference = now.getTime() - reservationTime.getTime();
|
|
|
- const minutesDifference = timeDifference / (1000 * 60);
|
|
|
-
|
|
|
- if (minutesDifference <= 15) {
|
|
|
- console.log('User arrived within 15 minutes of their reservation.');
|
|
|
- startCharging(data);
|
|
|
- } else {
|
|
|
- console.log('User arrived more than 15 minutes late for their reservation.');
|
|
|
- if (!AreOrdersAdjacentToNow[1]) {
|
|
|
- console.log('Next slot is available. Allowing charging despite late arrival.');
|
|
|
- startCharging(data);
|
|
|
- } else {
|
|
|
- Alert.alert(
|
|
|
- '預約已過期',
|
|
|
- '您的預約時間已經過期,且下一個時段已被預約。請重新預約。'
|
|
|
- );
|
|
|
- console.log('Next slot is not available. Charging not allowed.');
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- Alert.alert('無法使用', '此充電槍已經被預約,請選擇其他位置');
|
|
|
- }
|
|
|
- } else if (AreOrdersAdjacentToNow[1]) {
|
|
|
- const upcomingReservation = relevantReservations[1];
|
|
|
- if (upcomingReservation && upcomingReservation.user.id === userID) {
|
|
|
- const minutesUntilReservation =
|
|
|
- (new Date(upcomingReservation.book_time).getTime() - now.getTime()) / (1000 * 60);
|
|
|
- if (minutesUntilReservation <= 5) {
|
|
|
- console.log('User arrived slightly early for their upcoming reservation.');
|
|
|
- startCharging(data);
|
|
|
- } else {
|
|
|
- Alert.alert(
|
|
|
- '預約時間未到',
|
|
|
- `您的預約時間還有 ${Math.round(minutesUntilReservation)} 分鐘開始。請稍後再試。`
|
|
|
- );
|
|
|
- }
|
|
|
- } else {
|
|
|
- Alert.alert('已被預約', '此充電槍已被其他用戶預約。請選擇其他位置。');
|
|
|
- }
|
|
|
- } else {
|
|
|
- Alert.alert('無法使用', '此充電槍目前無法使用。請選擇其他位置或稍後再試。');
|
|
|
- }
|
|
|
+ setAvailableSlots(availability);
|
|
|
+ setModalVisible(true);
|
|
|
} else {
|
|
|
- console.log('No response from getTodayReservation');
|
|
|
Alert.alert('系統錯誤', '無法獲取預約信息。請稍後再試。');
|
|
|
}
|
|
|
} catch (error) {
|
|
|
@@ -347,108 +299,23 @@ const ScanQrPage = () => {
|
|
|
const response = await chargeStationService.getTodayReservation();
|
|
|
if (response) {
|
|
|
const now = new Date();
|
|
|
-
|
|
|
const onlyThisConnector = response.filter(
|
|
|
(reservation: any) => reservation.connector.ConnectorID === data
|
|
|
);
|
|
|
-
|
|
|
- onlyThisConnector.sort((a: any, b: any) => {
|
|
|
- const timeA = new Date(a.book_time).getTime();
|
|
|
- const timeB = new Date(b.book_time).getTime();
|
|
|
- return timeA - timeB;
|
|
|
- });
|
|
|
-
|
|
|
- let previousReservation = null;
|
|
|
- let upcomingReservation = null;
|
|
|
-
|
|
|
- for (let i = 0; i < onlyThisConnector.length; i++) {
|
|
|
- const reservationTime = new Date(onlyThisConnector[i].book_time).getTime();
|
|
|
- if (reservationTime <= now.getTime()) {
|
|
|
- previousReservation = onlyThisConnector[i];
|
|
|
- } else {
|
|
|
- upcomingReservation = onlyThisConnector[i];
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- const relevantReservations = [previousReservation || null, upcomingReservation || null];
|
|
|
-
|
|
|
- console.log('Relevant reservations:', relevantReservations);
|
|
|
-
|
|
|
- const getNearestSlot = (date: Date) => {
|
|
|
- const minutes = date.getMinutes();
|
|
|
- const nearestSlot = new Date(date);
|
|
|
- nearestSlot.setMinutes(minutes < 30 ? 0 : 30);
|
|
|
- nearestSlot.setSeconds(0);
|
|
|
- nearestSlot.setMilliseconds(0);
|
|
|
- return nearestSlot;
|
|
|
+ console.log('onlyThisConnector', onlyThisConnector);
|
|
|
+
|
|
|
+ // Check availability for each duration
|
|
|
+ const availability = {
|
|
|
+ 25: checkAvailability(onlyThisConnector, now, 40),
|
|
|
+ 30: checkAvailability(onlyThisConnector, now, 45),
|
|
|
+ 40: checkAvailability(onlyThisConnector, now, 55),
|
|
|
+ 45: checkAvailability(onlyThisConnector, now, 60),
|
|
|
+ full: checkAvailability(onlyThisConnector, now, 120)
|
|
|
};
|
|
|
|
|
|
- const currentSlot = getNearestSlot(now);
|
|
|
- const nextSlot = new Date(currentSlot.getTime() + 30 * 60 * 1000);
|
|
|
-
|
|
|
- const AreOrdersAdjacentToNow = relevantReservations.map((reservation, index) => {
|
|
|
- if (!reservation) return false;
|
|
|
-
|
|
|
- const reservationTime = new Date(reservation.book_time);
|
|
|
- return index === 0
|
|
|
- ? reservationTime.getTime() === currentSlot.getTime()
|
|
|
- : reservationTime.getTime() === nextSlot.getTime();
|
|
|
- });
|
|
|
-
|
|
|
- console.log('AreOrdersAdjacentToNow:', AreOrdersAdjacentToNow);
|
|
|
-
|
|
|
- if (!AreOrdersAdjacentToNow[0] && !AreOrdersAdjacentToNow[1]) {
|
|
|
- console.log('Charging machine is available. Starting charging process...');
|
|
|
- startCharging(data);
|
|
|
- } else if (AreOrdersAdjacentToNow[0]) {
|
|
|
- const previousReservation = relevantReservations[0];
|
|
|
- if (previousReservation && previousReservation.user.id === userID) {
|
|
|
- const reservationTime = new Date(previousReservation.book_time);
|
|
|
- const timeDifference = now.getTime() - reservationTime.getTime();
|
|
|
- const minutesDifference = timeDifference / (1000 * 60);
|
|
|
-
|
|
|
- if (minutesDifference <= 15) {
|
|
|
- console.log('User arrived within 15 minutes of their reservation.');
|
|
|
- startCharging(data);
|
|
|
- } else {
|
|
|
- console.log('User arrived more than 15 minutes late for their reservation.');
|
|
|
- if (!AreOrdersAdjacentToNow[1]) {
|
|
|
- console.log('Next slot is available. Allowing charging despite late arrival.');
|
|
|
- startCharging(data);
|
|
|
- } else {
|
|
|
- Alert.alert(
|
|
|
- '預約已過期',
|
|
|
- '您的預約時間已經過期,且下一個時段已被預約。請重新預約。'
|
|
|
- );
|
|
|
- console.log('Next slot is not available. Charging not allowed.');
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- Alert.alert('無法使用', '此充電槍已經被預約,請選擇其他位置');
|
|
|
- }
|
|
|
- } else if (AreOrdersAdjacentToNow[1]) {
|
|
|
- const upcomingReservation = relevantReservations[1];
|
|
|
- if (upcomingReservation && upcomingReservation.user.id === userID) {
|
|
|
- const minutesUntilReservation =
|
|
|
- (new Date(upcomingReservation.book_time).getTime() - now.getTime()) / (1000 * 60);
|
|
|
- if (minutesUntilReservation <= 5) {
|
|
|
- console.log('User arrived slightly early for their upcoming reservation.');
|
|
|
- startCharging(data);
|
|
|
- } else {
|
|
|
- Alert.alert(
|
|
|
- '預約時間未到',
|
|
|
- `您的預約時間還有 ${Math.round(minutesUntilReservation)} 分鐘開始。請稍後再試。`
|
|
|
- );
|
|
|
- }
|
|
|
- } else {
|
|
|
- Alert.alert('已被預約', '此充電槍已被其他用戶預約。請選擇其他位置。');
|
|
|
- }
|
|
|
- } else {
|
|
|
- Alert.alert('無法使用', '此充電槍目前無法使用。請選擇其他位置或稍後再試。');
|
|
|
- }
|
|
|
+ setAvailableSlots(availability);
|
|
|
+ setModalVisible(true);
|
|
|
} else {
|
|
|
- console.log('No response from getTodayReservation');
|
|
|
Alert.alert('系統錯誤', '無法獲取預約信息。請稍後再試。');
|
|
|
}
|
|
|
} catch (error) {
|
|
|
@@ -462,31 +329,67 @@ const ScanQrPage = () => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- //prepare data for submission
|
|
|
- const dataForSubmission = {
|
|
|
- stationID: '2405311022116801000',
|
|
|
- connector: scannedResult,
|
|
|
- user: userID,
|
|
|
- book_time: now,
|
|
|
- end_time: now,
|
|
|
- total_power: 0,
|
|
|
- total_fee: 0,
|
|
|
- promotion_code: '',
|
|
|
- car: selectedCar,
|
|
|
- type: 'walking',
|
|
|
- is_ic_call: false
|
|
|
+ const checkAvailability = (reservations, startTime, duration) => {
|
|
|
+ const endTime = new Date(startTime.getTime() + duration * 60000);
|
|
|
+ console.log('now', now);
|
|
|
+ console.log('endTime', endTime);
|
|
|
+ console.log('reservations', reservations);
|
|
|
+ return !reservations.some((reservation) => {
|
|
|
+ const resStart = new Date(reservation.book_time);
|
|
|
+ const resEnd = new Date(reservation.end_time);
|
|
|
+ return startTime < resEnd && endTime > resStart;
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
- const startCharging = async (scanResult: string) => {
|
|
|
- try {
|
|
|
- if (selectedCar === '') {
|
|
|
- Alert.alert('請選擇車輛');
|
|
|
- return;
|
|
|
+ const handleDurationSelect = (duration) => {
|
|
|
+ setSelectedDuration(duration);
|
|
|
+ };
|
|
|
+ const handleCancel = () => {
|
|
|
+ setSelectedDuration(null);
|
|
|
+ setModalVisible(false);
|
|
|
+ };
|
|
|
+ const handleConfirm = () => {
|
|
|
+ if (selectedDuration !== null) {
|
|
|
+ const now = new Date();
|
|
|
+ let endTime;
|
|
|
+ let fee;
|
|
|
+ let totalPower;
|
|
|
+
|
|
|
+ if (selectedDuration === 'full') {
|
|
|
+ endTime = new Date(now.getTime() + 2 * 60 * 60 * 1000); // 2 hours for "充滿停機"
|
|
|
+ fee = 280;
|
|
|
+ totalPower = 0; // Set to 0 for "充滿停機"
|
|
|
+ } else {
|
|
|
+ const durationInMinutes = parseInt(selectedDuration);
|
|
|
+ endTime = new Date(now.getTime() + durationInMinutes * 60 * 1000);
|
|
|
+ fee = planMap[selectedDuration].fee;
|
|
|
+ totalPower = durationInMinutes; // Use the actual duration for other cases
|
|
|
}
|
|
|
|
|
|
+ const dataForSubmission = {
|
|
|
+ stationID: '2405311022116801000',
|
|
|
+ connector: scannedResult,
|
|
|
+ user: userID,
|
|
|
+ book_time: now,
|
|
|
+ end_time: endTime,
|
|
|
+ total_power: totalPower,
|
|
|
+ total_fee: fee,
|
|
|
+ promotion_code: '',
|
|
|
+ car: selectedCar,
|
|
|
+ type: 'walking',
|
|
|
+ is_ic_call: false
|
|
|
+ };
|
|
|
+
|
|
|
+ startCharging(dataForSubmission);
|
|
|
+ setModalVisible(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const startCharging = async (dataForSubmission) => {
|
|
|
+ try {
|
|
|
const response = await walletService.submitPayment(
|
|
|
dataForSubmission.stationID,
|
|
|
- scanResult,
|
|
|
+ dataForSubmission.connector,
|
|
|
dataForSubmission.user,
|
|
|
dataForSubmission.book_time,
|
|
|
dataForSubmission.end_time,
|
|
|
@@ -497,12 +400,19 @@ const ScanQrPage = () => {
|
|
|
dataForSubmission.type,
|
|
|
dataForSubmission.is_ic_call
|
|
|
);
|
|
|
-
|
|
|
if (response) {
|
|
|
console.log('Charging started from startCharging', response);
|
|
|
- router.push('(auth)/(tabs)/(charging)/chargingPage');
|
|
|
+
|
|
|
+ // Set a flag in AsyncStorage to indicate charging has started
|
|
|
+
|
|
|
+ await AsyncStorage.setItem('chargingStarted', 'true');
|
|
|
+
|
|
|
+ Alert.alert('啟動成功', '請稍後等待頁面自動跳轉至充電介面', [{ text: 'OK', onPress: () => {} }]);
|
|
|
+
|
|
|
+ // Start the navigation attempt loop
|
|
|
+ startNavigationAttempts();
|
|
|
} else {
|
|
|
- console.log('Failed to start chargi12312312ng:', response);
|
|
|
+ console.log('Failed to start charging:', response);
|
|
|
Alert.alert('掃描失敗 請稍後再試。', response);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
@@ -510,6 +420,40 @@ const ScanQrPage = () => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+ const startNavigationAttempts = () => {
|
|
|
+ let attempts = 0;
|
|
|
+ const maxAttempts = 10; // Try for about 2.5 minutes (10 * 15 seconds)
|
|
|
+
|
|
|
+ const attemptNavigation = async () => {
|
|
|
+ try {
|
|
|
+ const chargingStarted = await AsyncStorage.getItem('chargingStarted');
|
|
|
+ if (chargingStarted === 'true') {
|
|
|
+ // Wait for 2 seconds before navigating
|
|
|
+ await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
|
+ await AsyncStorage.removeItem('chargingStarted');
|
|
|
+ router.push('(auth)/(tabs)/(charging)/chargingPage');
|
|
|
+ // If navigation is successful, clear the flag
|
|
|
+ } else {
|
|
|
+ throw new Error('Navigation not ready');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ attempts++;
|
|
|
+ if (attempts < maxAttempts) {
|
|
|
+ // If navigation fails, try again after 15 seconds
|
|
|
+ setTimeout(attemptNavigation, 15000);
|
|
|
+ } else {
|
|
|
+ // If all attempts fail, show an alert to the user
|
|
|
+ Alert.alert('導航失敗', '無法自動跳轉到充電頁面。請手動導航到充電頁面。', [
|
|
|
+ { text: 'OK', onPress: () => {} }
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // Start the first attempt after 15 seconds
|
|
|
+ setTimeout(attemptNavigation, 15000);
|
|
|
+ };
|
|
|
+
|
|
|
return (
|
|
|
<View style={styles.container} ref={viewRef}>
|
|
|
{loading ? (
|
|
|
@@ -528,12 +472,12 @@ const ScanQrPage = () => {
|
|
|
>
|
|
|
<View style={styles.overlay}>
|
|
|
<View style={styles.topOverlay}>
|
|
|
- <ChooseCar
|
|
|
+ {/* <ChooseCar
|
|
|
carData={carData}
|
|
|
loading={loading}
|
|
|
selectedCar={selectedCar}
|
|
|
setSelectedCar={setSelectedCar}
|
|
|
- />
|
|
|
+ /> */}
|
|
|
</View>
|
|
|
<View style={styles.centerRow}>
|
|
|
<View style={styles.leftOverlay}></View>
|
|
|
@@ -543,7 +487,7 @@ const ScanQrPage = () => {
|
|
|
<View className="items-center justify-between" style={styles.bottomOverlay}>
|
|
|
<View>
|
|
|
<Text className="text-white text-lg font-bold mt-2 text-center">
|
|
|
- 請選擇充電車輛{'\n'}及掃瞄充電座上的二維碼
|
|
|
+ 請掃瞄充電座上的二維碼
|
|
|
</Text>
|
|
|
</View>
|
|
|
<View className="flex-row space-x-2 items-center ">
|
|
|
@@ -558,6 +502,56 @@ const ScanQrPage = () => {
|
|
|
</View>
|
|
|
</CameraView>
|
|
|
)}
|
|
|
+ <Modal isVisible={isModalVisible} backdropOpacity={0.5} animationIn="fadeIn" animationOut="fadeOut">
|
|
|
+ <View style={styles.modalContent} className="flex flex-col">
|
|
|
+ <Text className="text-xl font-bold mt-2 text-center">請選擇充電時間</Text>
|
|
|
+ <Text className="text-base m-2 mb-4 text-center">按鈕呈紅色代表該時段已被他人預約</Text>
|
|
|
+ <View className="flex flex-row flex-wrap ">
|
|
|
+ {Object.entries(availableSlots).map(([duration, available]) => (
|
|
|
+ <NormalButton
|
|
|
+ key={duration}
|
|
|
+ title={
|
|
|
+ duration === 'full' ? (
|
|
|
+ <Text className={selectedDuration === duration ? 'text-white' : ''}>
|
|
|
+ 充滿停機
|
|
|
+ </Text>
|
|
|
+ ) : (
|
|
|
+ <Text
|
|
|
+ className={selectedDuration === duration ? 'text-white' : ''}
|
|
|
+ >{`${planMap[duration].kWh} 度電 - ${planMap[duration].displayDuration} 分鐘`}</Text>
|
|
|
+ )
|
|
|
+ }
|
|
|
+ onPress={() => handleDurationSelect(duration)}
|
|
|
+ extendedStyle={[
|
|
|
+ styles.durationButton,
|
|
|
+ {
|
|
|
+ backgroundColor: available
|
|
|
+ ? selectedDuration === duration
|
|
|
+ ? '#02677d'
|
|
|
+ : 'white'
|
|
|
+ : 'red',
|
|
|
+ borderColor: available ? 'black' : 'red',
|
|
|
+ borderWidth: 1
|
|
|
+ }
|
|
|
+ ]}
|
|
|
+ disabled={!available}
|
|
|
+ />
|
|
|
+ ))}
|
|
|
+ </View>
|
|
|
+ {selectedDuration && (
|
|
|
+ <NormalButton
|
|
|
+ title={<Text className="text-white">確認</Text>}
|
|
|
+ onPress={handleConfirm}
|
|
|
+ extendedStyle={styles.confirmButton}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ <NormalButton
|
|
|
+ title={<Text className="">取消</Text>}
|
|
|
+ onPress={handleCancel}
|
|
|
+ extendedStyle={styles.cancelButton}
|
|
|
+ />
|
|
|
+ </View>
|
|
|
+ </Modal>
|
|
|
</View>
|
|
|
);
|
|
|
};
|
|
|
@@ -598,6 +592,27 @@ const styles = StyleSheet.create({
|
|
|
bottomOverlay: {
|
|
|
flex: 35,
|
|
|
backgroundColor: 'rgba(0,0,0,0.5)'
|
|
|
+ },
|
|
|
+ modalContent: {
|
|
|
+ backgroundColor: 'white',
|
|
|
+ padding: 22,
|
|
|
+
|
|
|
+ alignItems: 'center',
|
|
|
+ borderRadius: 4,
|
|
|
+ borderColor: 'rgba(0, 0, 0, 0.1)'
|
|
|
+ },
|
|
|
+ durationButton: { margin: 5 },
|
|
|
+ confirmButton: {
|
|
|
+ marginTop: 20,
|
|
|
+ width: '100%'
|
|
|
+ },
|
|
|
+ cancelButton: {
|
|
|
+ marginTop: 20,
|
|
|
+ width: '100%',
|
|
|
+ backgroundColor: 'white',
|
|
|
+ borderColor: 'black',
|
|
|
+ borderWidth: 1,
|
|
|
+ color: 'black'
|
|
|
}
|
|
|
});
|
|
|
|