import { CameraView, useCameraPermissions } from 'expo-camera'; import { useEffect, useRef, useState } from 'react'; import { ActivityIndicator, Alert, AppState, Dimensions, Linking, Pressable, ScrollView, StyleSheet, Text, Vibration, View } from 'react-native'; import sha256 from 'crypto-js/sha256'; import ChooseCarForChargingRow from '../../../../component/global/chooseCarForChargingRow'; import { CrossLogoWhiteSvg, QuestionSvg } from '../../../../component/global/SVG'; import { router } from 'expo-router'; 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 }) => { // const isLargeScreen = screenHeight >= 800; // const defaultImageUrl = require('../../../../assets/car1.png'); // return ( // // // // {loading ? ( // // // // ) : ( // // // { // if (router.canGoBack()) { // router.back(); // } else { // router.replace('mainPage'); // } // }} // > // // // 選擇充電車輛 // // // // {carData.map((car, index) => ( // { // setSelectedCar(car.id); // console.log(car.id); // }} // isSelected={selectedCar === car.id} // // imageUrl={image} // VehicleName={car.name} // isDefault={car.isDefault} // /> // ))} // // // )} // // // // ); // }; //reminder: scan qr code page, ic call should be false const ScanQrPage = () => { const { userID, currentPrice, setCurrentPrice } = useUserInfoStore(); const [currentPriceFetchedWhenScanQr, setCurrentPriceFetchedWhenScanQr] = useState(0); const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); const [permission, requestPermission] = useCameraPermissions(); const [scanned, setScanned] = useState(false); const viewRef = useRef(null); const [scannedResult, setScannedResult] = useState(''); const [selectedCar, setSelectedCar] = useState(''); const now = new Date(); const [loading, setLoading] = useState(true); const [loading2, setLoading2] = useState(false); const [loading3, setLoading3] = useState(false); const [carData, setCarData] = useState([]); const [isModalVisible, setModalVisible] = useState(false); const [isConfirmLoading, setIsConfirmLoading] = useState(false); const [availableSlots, setAvailableSlots] = useState({ // 3: false, 25: false, 30: false, 40: false, 45: false, full: false }); const [selectedDuration, setSelectedDuration] = useState(null); const appState = useRef(AppState.currentState); const [paymentStatus, setPaymentStatus] = useState(null); const [isExpectingPayment, setIsExpectingPayment] = useState(false); const paymentInitiatedTime = useRef(null); const PAYMENT_CHECK_TIMEOUT = 5 * 60 * 1000; // 5 minutes in milliseconds const [outTradeNo, setOutTradeNo] = useState(''); const [totalFee, setTotalFee] = useState(0); const [walletBalance, setWalletBalance] = useState(0); // Effect for requesting camera permissions useEffect(() => { (async () => { const { status } = await requestPermission(); if (status !== 'granted') { alert( '我們需要相機權限來掃描機器上的二維碼,以便識別並啟動充電機器。我們不會儲存或共享任何掃描到的資訊。 請前往設定開啟相機權限' ); } })(); }, []); // 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_typ e.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 fetchDefaultCar = async () => { try { const response = await chargeStationService.getUserDefaultCars(); if (response) { // console.log('default car', response.data.id); setSelectedCar(response.data.id); } } catch (error) { console.log(error); } finally { setLoading(false); } }; fetchDefaultCar(); }, []); //check current wallet useEffect(() => { const getWalletBalance = async () => { try { const response = await walletService.getWalletBalance(); if (response) { // console.log('walletBalance setting up', response); setWalletBalance(response); } } catch (error) { console.log(error); } }; getWalletBalance(); }, []); const fetchCurrentPrice = async () => { try { const response = await chargeStationService.getCurrentPrice(); if (response) { setCurrentPriceFetchedWhenScanQr(response); setCurrentPrice(response); return response; } } catch (error) { console.error('Error fetching current price:', error); Alert.alert('錯誤', '無法獲取當前價格,請稍後再試'); return null; } }; const planMap = { // 3: { duration: 10, kWh: 3, displayDuration: 5, fee: 3 * currentPrice }, 25: { duration: 40, kWh: 20, displayDuration: 25, fee: 20 * currentPrice }, 30: { duration: 45, kWh: 25, displayDuration: 30, fee: 25 * currentPrice }, 40: { duration: 55, kWh: 30, displayDuration: 40, fee: 30 * currentPrice }, 45: { duration: 60, kWh: 40, displayDuration: 45, fee: 40 * currentPrice }, full: { duration: 120, displayDuration: '充滿停機', fee: 80 * currentPrice } }; // Function to handle barcode scanning const handleBarCodeScanned = async ({ bounds, data, type }: { bounds?: any; data: any; type: any }) => { if ( !bounds || typeof bounds.origin?.x !== 'number' || typeof bounds.origin?.y !== 'number' || typeof bounds.size?.width !== 'number' || typeof bounds.size?.height !== 'number' ) { // console.log('Invalid or missing bounds data:', bounds); // Proceed with scanning logic without bounds checking setScanned(true); setScannedResult(data); Vibration.vibrate(100); // console.log(`type: ${type} data: ${data} typeofData ${typeof data}`); try { const price = await fetchCurrentPrice(); console.log('fetchedCurrentPrice in scanQrPage', price); if (!price) { return; // Exit if price fetch failed } const response = await chargeStationService.getTodayReservation(); if (response) { const now = new Date(); const onlyThisConnector = response.filter( (reservation: any) => reservation.connector.ConnectorID === data ); // Check availability for each duration``````````````````````````````````````````````````````````````````` const availability = { // 3: checkAvailability(onlyThisConnector, now, 10) && walletBalance >= 3 * price, 25: checkAvailability(onlyThisConnector, now, 40) && walletBalance >= 20 * price, 30: checkAvailability(onlyThisConnector, now, 45) && walletBalance >= 25 * price, 40: checkAvailability(onlyThisConnector, now, 55) && walletBalance >= 30 * price, 45: checkAvailability(onlyThisConnector, now, 60) && walletBalance >= 40 * price, full: checkAvailability(onlyThisConnector, now, 120) && walletBalance >= 80 * price }; setAvailableSlots(availability); setModalVisible(true); } else { Alert.alert('系統錯誤', '無法獲取預約信息。請稍後再試。'); } } catch (error) { console.error("Error fetching today's reservations:", error); Alert.alert('系統錯誤', '發生未知錯誤。請稍後再試。'); } setTimeout(() => { setScanned(false); }, 2000); return; } // ----------------------------------------------------------------------------------------------------- const { origin, size } = bounds; // Calculate the size of the square transparent area const transparentAreaSize = Math.min(screenWidth * 0.6, screenHeight * 0.3); const transparentAreaX = (screenWidth - transparentAreaSize) / 2; const transparentAreaY = (screenHeight - transparentAreaSize) / 2; // Check if the barcode is within the transparent area if ( origin.x >= transparentAreaX && origin.y >= transparentAreaY && origin.x + size.width <= transparentAreaX + transparentAreaSize && origin.y + size.height <= transparentAreaY + transparentAreaSize ) { setScanned(true); setScannedResult(data); Vibration.vibrate(100); // console.log(` type: ${type} data: ${data} typeofData ${typeof data}`); try { const price = await fetchCurrentPrice(); console.log('fetchedCurrentPrice in scanQrPage', price); if (!price) { return; // Exit if price fetch failed } const response = await chargeStationService.getTodayReservation(); if (response) { const now = new Date(); const onlyThisConnector = response.filter( (reservation: any) => reservation.connector.ConnectorID === data ); console.log('onlyThisConnector', onlyThisConnector); // Check availability for each duration const availability = { // 3: checkAvailability(onlyThisConnector, now, 10) && walletBalance >= 3 * price, 25: checkAvailability(onlyThisConnector, now, 40) && walletBalance >= 20 * price, 30: checkAvailability(onlyThisConnector, now, 45) && walletBalance >= 25 * price, 40: checkAvailability(onlyThisConnector, now, 55) && walletBalance >= 30 * price, 45: checkAvailability(onlyThisConnector, now, 60) && walletBalance >= 40 * price, full: checkAvailability(onlyThisConnector, now, 120) && walletBalance >= 80 * price }; // console.log('availability', availability); // console.log('walletBalance', walletBalance); // console.log('currentPricecurrentPricecurrentPricecurrentPricecurrentPrice', currentPrice); setAvailableSlots(availability); setModalVisible(true); } else { Alert.alert('系統錯誤', '無法獲取預約信息。請稍後再試。'); } } catch (error) { console.error("Error fetching today's reservations:", error); Alert.alert('系統錯誤', '發生未知錯誤。請稍後再試。'); } setTimeout(() => { setScanned(false); }, 2000); } }; const checkAvailability = (reservations, startTime, duration) => { const endTime = new Date(startTime.getTime() + duration * 60000); // console.log('now', startTime); // console.log('endTime', endTime); // console.log('reservations', reservations); return !reservations.some((reservation) => { // Ignore reservations with status '9' (cancelled) if (reservation.status.id === '9' || reservation.status.id === '13') { return false; } // For status '8' (early finished), check actual_end_time if (reservation.status.id === '8' && reservation.actual_end_time) { const actualEndTime = new Date(reservation.actual_end_time); if (actualEndTime <= startTime) { return false; // Treat as available if actual end time is before or at start time } } const resStart = new Date(reservation.book_time); const resEnd = new Date(reservation.end_time); return startTime < resEnd && endTime > resStart; }); }; const handleDurationSelect = (duration) => { setSelectedDuration(duration); // console.log(duration); }; const handleCancel = () => { setSelectedDuration(null); setModalVisible(false); if (router.canGoBack()) { router.back(); } else { router.push('/mainPage'); } }; const handleConfirm = () => { if (selectedDuration !== null) { const now = new Date(); let endTime; let fee; let totalPower; //i create a planMap2 because i want to move the planMap inside this component but i dont wanna move the outside one because i dont wanna make any potential disruptive changes const planMap2 = { // 3: { duration: 10, kWh: 3, displayDuration: 5, fee: 3 * currentPriceFetchedWhenScanQr }, 25: { duration: 40, kWh: 20, displayDuration: 25, fee: 20 * currentPriceFetchedWhenScanQr }, 30: { duration: 45, kWh: 25, displayDuration: 30, fee: 25 * currentPriceFetchedWhenScanQr }, 40: { duration: 55, kWh: 30, displayDuration: 40, fee: 30 * currentPriceFetchedWhenScanQr }, 45: { duration: 60, kWh: 40, displayDuration: 45, fee: 40 * currentPriceFetchedWhenScanQr }, full: { duration: 120, displayDuration: '充滿停機', fee: 80 * currentPriceFetchedWhenScanQr } }; if (selectedDuration === 'full') { endTime = new Date(now.getTime() + 2 * 60 * 60 * 1000); // 2 hours for "充滿停機" fee = planMap2.full.fee; totalPower = 80; // Set to 130 for "充滿停機" } else { const durationInMinutes = parseInt(selectedDuration); endTime = new Date(now.getTime() + durationInMinutes * 60 * 1000); // console.log('endTime', endTime); fee = planMap2[selectedDuration].fee; totalPower = planMap2[selectedDuration].kWh; } setTotalFee(fee); console.log('fee in scanQrPage-- this is the total_fee i send to backend', fee); const dataForSubmission = { stationID: '2405311022116801000', connector: scannedResult, user: userID, book_time: now, end_time: endTime, total_power: totalPower, total_fee: fee, // total_fee: 1, promotion_code: '', car: selectedCar, type: 'walking', is_ic_call: false }; startCharging(dataForSubmission); setIsConfirmLoading(true); } }; //below commented is the original WORKING startCharging, if i fucked up, return back to using this!!! // const startCharging = async (dataForSubmission) => { // try { // const wallet = await walletService.getWalletBalance(); // console.log('wallet in startCharging in scanQrPage', wallet); // const response = await walletService.submitPayment( // dataForSubmission.stationID, // dataForSubmission.connector, // dataForSubmission.user, // dataForSubmission.book_time, // dataForSubmission.end_time, // dataForSubmission.total_power, // dataForSubmission.total_fee, // dataForSubmission.promotion_code, // dataForSubmission.car, // dataForSubmission.type, // dataForSubmission.is_ic_call // ); // if (response.status === 200 || response.status === 201) { // setSelectedDuration(null); // console.log('Charging started from startCharging', response); // setIsConfirmLoading(false); // // Set a flag in AsyncStorage to indicate charging has started // await AsyncStorage.setItem('chargingStarted', 'true'); // Alert.alert('啟動成功', '請稍後等待頁面自動跳轉至充電介面', [ // { // text: 'OK', // onPress: async () => { // setModalVisible(false); // setLoading(true); // // Wait for 2 seconds // await new Promise((resolve) => setTimeout(resolve, 2000)); // // Hide loading spinner and navigate // setLoading(false); // router.push('(auth)/(tabs)/(charging)/chargingPage'); // } // } // ]); // // Start the navigation attempt loop // // startNavigationAttempts(); // } else if (response.status === 400) { // console.log('400 error in paymentSummaryPageComponent'); // Alert.alert('餘額不足', '您的餘額不足,請充值後再試。'); // } else { // console.log('Failed to start charging:', response); // Alert.alert('掃描失敗 請稍後再試。', response); // } // } catch (error) { // console.log('Failed to start chasssssssrging:', error); // } // }; //below is the new flow for startCharging. // useEffect(() => { // const subscription = AppState.addEventListener('change', (nextAppState) => { // if ( // appState.current.match(/inactive|background/) && // nextAppState === 'active' && // isExpectingPayment && // // outTradeNo && // paymentInitiatedTime.current // ) { // const currentTime = new Date().getTime(); // if (currentTime - paymentInitiatedTime.current < PAYMENT_CHECK_TIMEOUT) { // checkPaymentStatus(); // } else { // // Payment check timeout reached // setIsExpectingPayment(false); // setOutTradeNo(''); // paymentInitiatedTime.current = null; // Alert.alert( // 'Payment Timeout', // 'The payment status check has timed out. Please check your payment history.' // ); // } // } // appState.current = nextAppState; // }); // return () => { // subscription.remove(); // }; // }, [outTradeNo, isExpectingPayment]); //check payment status useEffect(() => { const subscription = AppState.addEventListener('change', (nextAppState) => { if ( appState.current.match(/inactive|background/) && nextAppState === 'active' && isExpectingPayment && // outTradeNo && paymentInitiatedTime.current ) { const currentTime = new Date().getTime(); if (currentTime - paymentInitiatedTime.current < PAYMENT_CHECK_TIMEOUT) { checkPaymentStatus(); } else { // Payment check timeout reached setIsExpectingPayment(false); setOutTradeNo(''); paymentInitiatedTime.current = null; Alert.alert( 'Payment Timeout', 'The payment status check has timed out. Please check your payment history.' ); } } appState.current = nextAppState; }); return () => { subscription.remove(); }; }, [outTradeNo, isExpectingPayment]); const checkPaymentStatus = async () => { try { // console.log('outTradeNo in scanQR Page checkpaymentstatus ', outTradeNo); const result = await walletService.checkPaymentStatus(outTradeNo); setPaymentStatus(result); // console.log('checkPaymentStatus from scan QR checkpaymentStatus', result); if (result && !result.some((item) => item.errmsg?.includes('處理中'))) { // Payment successful // console.log('totalFee', totalFee); Alert.alert( '付款已成功', `你已成功增值HKD $${ Number.isInteger(totalFee) ? totalFee : totalFee.toFixed(1) }。請重新掃描去啟動充電槍。`, [ { text: '確認', onPress: async () => { setModalVisible(false); router.dismiss(); } } ] ); } else { Alert.alert('付款失敗', '請再試一次。', [ { text: '確定', onPress: () => { setModalVisible(false); router.dismiss(); } } ]); } setIsExpectingPayment(false); setOutTradeNo(''); paymentInitiatedTime.current = null; } catch (error) { console.error('Failed to check payment status:', error); Alert.alert('Error', 'Failed to check payment status. Please check your payment history.'); } }; function formatTime(utcTimeString) { // Parse the UTC time string const date = new Date(utcTimeString); // Add 8 hours date.setHours(date.getHours()); // Format the date const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0'); // Return the formatted string return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } const oneTimeCharging = async (inputAmount) => { try { const response = await walletService.getOutTradeNo(); // console.log('outtradeno in oneTimeCharging', response); if (response) { setOutTradeNo(response); setIsExpectingPayment(true); paymentInitiatedTime.current = new Date().getTime(); const now = new Date(); const formattedTime = formatTime(now); let amount = inputAmount * 100; const origin = 'https://openapi-hk.qfapi.com/checkstand/#/?'; const obj = { appcode: '6937EF25DF6D4FA78BB2285441BC05E9', goods_name: 'Crazy Charge 錢包增值', out_trade_no: response, paysource: 'crazycharge_checkout', return_url: 'https://crazycharge.com.hk/completed', failed_url: 'https://crazycharge.com.hk/failed', notify_url: 'https://api.crazycharge.com.hk/api/v1/clients/qfpay/webhook', sign_type: 'sha256', txamt: amount, txcurrcd: 'HKD', txdtm: formattedTime }; const paramStringify = (json, flag?) => { let str = ''; let keysArr = Object.keys(json); keysArr.sort().forEach((val) => { if (!json[val]) return; str += `${val}=${flag ? encodeURIComponent(json[val]) : json[val]}&`; }); return str.slice(0, -1); }; const api_key = '8F59E31F6ADF4D2894365F2BB6D2FF2C'; const params = paramStringify(obj); const sign = sha256(`${params}${api_key}`).toString(); const url = `${origin}${paramStringify(obj, true)}&sign=${sign}`; try { // console.log(url); const supported = await Linking.canOpenURL(url); if (supported) { await Linking.openURL(url); } else { Alert.alert('錯誤', '請稍後再試'); } } catch (error) { console.error('Top-up failed:', error); Alert.alert('Error', '一次性付款失敗,請稍後再試'); } } else { console.log('nasdasdasdsdfgo'); } } catch (error) { console.log(error); } }; const startCharging = async (dataForSubmission) => { try { //before i start below logic, i need to check if the user has penalty unpaid. //i will call fetchReservationHistories. and the api will return an array of object, within the object there is a field called "penalty_fee". //if any reservation has penalty_fee > 0, i will show an alert to the user, and once click the alert it will takes them to a page that show the detail of the reservation. const reservationHistories = await chargeStationService.fetchReservationHistories(); const unpaidPenalties = reservationHistories.filter( (reservation) => reservation.penalty_fee > 0 && reservation.penalty_paid_status === false ); const mostRecentUnpaidReservation = unpaidPenalties.reduce((mostRecent, current) => { return new Date(mostRecent.created_at) > new Date(current.created_at) ? mostRecent : current; }, unpaidPenalties[0]); if (unpaidPenalties.length > 0) { Alert.alert( '未付罰款', '您有未支付的罰款。請先支付罰款後再開始充電。', [ { text: '查看詳情', onPress: () => { // Navigate to a page showing penalty details setModalVisible(false); setLoading(false); 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: () => { setModalVisible(false); if (router.canGoBack()) { router.back(); } else { router.push('/mainPage'); } } } ], { cancelable: false } ); return; } const wallet = await walletService.getWalletBalance(); if (wallet < dataForSubmission.total_fee) { oneTimeCharging(dataForSubmission.total_fee); // const remainingAmount = dataForSubmission.total_fee - wallet; // oneTimeCharging(remainingAmount); return; } const response = await walletService.submitPayment( dataForSubmission.stationID, dataForSubmission.connector, dataForSubmission.user, dataForSubmission.book_time, dataForSubmission.end_time, dataForSubmission.total_power, dataForSubmission.total_fee, dataForSubmission.promotion_code, dataForSubmission.car, dataForSubmission.type, dataForSubmission.is_ic_call ); if (response.status === 200 || response.status === 201) { setSelectedDuration(null); setIsConfirmLoading(false); await AsyncStorage.setItem('chargingStarted', 'true'); Alert.alert('啟動成功', '請按下確認並等待頁面稍後自動跳轉至充電介面', [ { text: 'OK', onPress: async () => { setModalVisible(false); setLoading(true); // Wait for 2 seconds await new Promise((resolve) => setTimeout(resolve, 2000)); // Hide loading spinner and navigate setLoading(false); router.navigate('(auth)/(tabs)/(home)/mainPage'); router.push('(auth)/(tabs)/(charging)/chargingPage'); } } ]); } else if (response.status === 400) { console.log('400 error in paymentSummaryPageComponent'); Alert.alert('餘額不足', '掃描失敗 請稍後再試。'); } else { console.log('Failed to start charging:', response); Alert.alert('掃描失敗 請稍後再試。', response); } } catch (error) { console.log('Failed to start chasssssssrging:', error); } }; // 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 ( // // {loading ? ( // // // // ) : ( // // // // {/* */} // { // if (router.canGoBack()) { // router.back(); // } else { // router.push('/mainPage'); // } // }} // > // // // // // // // // // // // // 請掃瞄充電座上的二維碼 // // // // // router.push('assistancePage')}> // 需要協助? // // // // // // // )} // // // 請選擇充電時間 // 按鈕呈紅色代表該時段已被他人預約 // // {Object.entries(availableSlots).map(([duration, available]) => ( // // 充滿停機 // // ) : ( // {`${planMap[duration].kWh} 度電 - ${planMap[duration].displayDuration} 分鐘`} // ) // } // onPress={() => handleDurationSelect(duration)} // extendedStyle={[ // styles.durationButton, // { // backgroundColor: available // ? selectedDuration === duration // ? '#02677d' // : 'white' // : 'red', // borderColor: available ? 'black' : 'red', // borderWidth: 1 // } // ]} // disabled={!available} // /> // ))} // // {selectedDuration && ( // // ) : ( // 確認 // ) // } // onPress={handleConfirm} // extendedStyle={styles.confirmButton} // /> // )} // 取消} // onPress={handleCancel} // extendedStyle={styles.cancelButton} // /> // // // // ); // console.log('availableSlots', availableSlots); return ( {!permission ? ( ) : !permission.granted ? ( 我們需要相機權限來掃描機器上的二維碼,以便識別並啟動充電機器。我們不會儲存或共享任何掃描到的資訊。 請前往設定開啟相機權限 ) : loading ? ( ) : ( { if (router.canGoBack()) { router.back(); } else { router.push('/mainPage'); } }} > 請掃瞄充電座上的二維碼 router.push('assistancePage')}> 需要協助? )} 請選擇充電時間 {Object.values(availableSlots).some((available) => !available) && ( 按鈕呈紅色代表現時錢包餘額不足以支付選擇的充電時間,選擇紅色按鈕並按下確認鍵會進行一次性充值付費 )} {Object.entries(availableSlots).map(([duration, available]) => ( 充滿停機 (最多80度電) ) : ( {`${planMap[duration].kWh} 度電 - ${planMap[duration].displayDuration} 分鐘`} ) } onPress={() => handleDurationSelect(duration)} extendedStyle={[ styles.durationButton, { backgroundColor: available ? selectedDuration === duration ? '#02677d' : 'white' : selectedDuration === duration ? '#8B0000' // Darker red when selected : 'red', // Normal red when not selected borderColor: available ? 'black' : 'red', borderWidth: 1 } ]} /> ))} {selectedDuration && ( ) : ( 確認 ) } onPress={handleConfirm} extendedStyle={styles.confirmButton} /> )} 取消} onPress={handleCancel} extendedStyle={styles.cancelButton} /> ); }; const styles = StyleSheet.create({ container: { flex: 1 }, camera: { flex: 1 }, overlay: { flex: 1 }, topOverlay: { flex: 35, alignItems: 'center', backgroundColor: 'rgba(0,0,0,0.5)' }, centerRow: { flex: 30, flexDirection: 'row' }, leftOverlay: { flex: 20, backgroundColor: 'rgba(0,0,0,0.5)' }, transparentArea: { flex: 60, aspectRatio: 1, position: 'relative' }, rightOverlay: { flex: 20, backgroundColor: 'rgba(0,0,0,0.5)' }, bottomOverlay: { flex: 35, backgroundColor: 'rgba(0,0,0,0.5)' }, closeButton: { position: 'absolute', top: 40, left: 20, zIndex: 1 }, 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' } }); export default ScanQrPage;