import { View, Text, ScrollView, Pressable, StyleSheet, Image, Dimensions, ActivityIndicator, Platform, Linking, Alert } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; 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 { useEffect, useState } from 'react'; import DropdownSelect from '../global/dropdown_select'; import { chargeStationService } from '../../service/chargeStationService'; import * as Location from 'expo-location'; import { calculateDistance } from '../global/distanceCalculator'; import Modal from 'react-native-modal'; import useUserInfoStore from '../../providers/userinfo_store'; interface AccordionItemProps { title: string; children: React.ReactNode; isOpen: boolean; onToggle: () => void; isSelected: boolean; } const AccordionItem: React.FC = ({ title, children, isOpen, onToggle, isSelected }) => ( {title} {isOpen && {children}} ); const MakingBookingPageComponent = () => { const [isModalVisible, setModalVisible] = useState(false); const [routerParams, setRouterParams] = useState(null); const [openDrawer, setOpenDrawer] = useState(1); const [selectedTime, setSelectedTime] = useState(''); const [availableTimeSlots, setAvailableTimeSlots] = useState([]); const [selectedDrawer, setSelectedDrawer] = useState(1); const [isLoading, setIsLoading] = useState(true); const [selectedDate, setSelectedDate] = useState(''); const toggleDrawer = (index: number) => { setOpenDrawer(openDrawer === index ? null : index); setSelectedDrawer(index); }; const [defaultCar, setDefaultCar] = useState(null); const [isDefaultCarLoading, setIsDefaultCarLoading] = useState(true); const [availableDate, setAvailableDate] = useState([]); const [car, setCar] = useState([]); const [selectedCarID, setSelectedCarID] = useState(''); const [selectedChargingGun, setSelectedChargingGun] = useState(''); const [chargingBasedOnWatt, setChargingBasedOnWatt] = useState(true); const [stopChargingUponBatteryFull, setStopChargingUponBatteryFull] = useState(false); const [selectedCar, setSelectedCar] = useState(''); const [selectedDuration, setSelectedDuration] = useState(''); const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); const [price, setPrice] = useState(0); 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; const chargeStationAddress = params.chargeStationAddress as string; const chargeStationLat = params.chargeStationLat as string; const chargeStationLng = params.chargeStationLng as string; const [selectedWatt, setSelectedWatt] = useState(''); const [availableConnectorDropdownOptions, setAvailableConnectorDropdownOptions] = useState([]); const [carCapacitance, setCarCapacitance] = useState(''); const [currentLocation, setCurrentLocation] = useState(null); const [distance, setDistance] = useState(null); const [upcomingReservations, setUpcomingReservation] = useState([]); const [carLoadingState, setCarLoadingState] = useState(false); 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(); if (status !== 'granted') { console.error('Permission to access location was denied'); return; } let location = await Location.getLastKnownPositionAsync({}); setCurrentLocation(location); }; getCurrentLocation(); }, []); useEffect(() => { const fetchPrice = async () => { try { const price = await chargeStationService.fetchChargeStationPrice(chargeStationID); setPrice(price); } catch (error) { console.error('Error fetching price:', error); } }; fetchPrice(); }, []); const connectorIDToLabelMap = { '101708240502475001': '1', '101708240502476001': '2', '101708240502477001': '3', '101708240502478001': '4', '101708240502474001': '5', '101708240502474002': '6' }; 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}`; }; const handleNavigationPress = () => { const latitude = chargeStationLat; const longitude = chargeStationLng; const label = encodeURIComponent(chargeStationName); // Google Maps App URL const googleMapsUrl = `https://www.google.com/maps/search/?api=1&query=${latitude},${longitude}`; // Fallback URL for web browser const webUrl = `https://www.google.com/maps/dir/?api=1&destination=${latitude},${longitude}`; Linking.canOpenURL(googleMapsUrl) .then((supported) => { if (supported) { Linking.openURL(googleMapsUrl); } else { Linking.openURL(webUrl).catch((err) => { console.error('An error occurred', err); Alert.alert( 'Error', "Unable to open Google Maps. Please make sure it's installed on your device.", [{ text: 'OK' }], { cancelable: false } ); }); } }) .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; }); } setAvailableTimeSlots(filteredSlots); }; const handleConfirmation = (value: any) => { setModalVisible(true); const selectedOption = formattedConnectorDropdownOptions.find((option) => option.value === value); const label = selectedOption ? selectedOption.label : ''; setRouterParams({ pathname: '/bookingConfirmationPage', params: { chargeStationName, chargeStationAddress, chargeStationID, connectorID: value, connectorLabel: label, userID, carCapacitance: carCapacitance, carID: selectedCarID, carName: selectedCar, date: selectedDate, bookTime: selectedTime, chargingMethod: stopChargingUponBatteryFull ? 'stopChargingUponBatteryFull' : 'chargingBasedOnWatt', chargingWatt: selectedWatt || '', price: price } }); }; const handleModalConfirm = () => { setModalVisible(false); if (routerParams) { router.push(routerParams); } }; return ( { if (router.canGoBack()) { router.back(); } else { router.replace('./'); } }} > {chargeStationName} {chargeStationAddress} 路線 } // onPress={() => console.log('路線')} onPress={handleNavigationPress} extendedStyle={{ backgroundColor: '#E3F2F8', borderRadius: 61, paddingHorizontal: 20, paddingVertical: 8 }} /> Walk-In {/* {distance} */} {stopChargingUponBatteryFull === true || selectedWatt !== '' ? ( { setSelectedDuration(''); setChargingBasedOnWatt(false); setStopChargingUponBatteryFull(false); setSelectedTime(''); setSelectedDate(''); setSelectedWatt(''); setOpenDrawer(1); setSelectedDrawer(1); setSelectedChargingGun(''); }} > 選擇充電方案 {selectedWatt !== '' ? `按每道電 - ${selectedWatt.split('~')[0]}` : '充滿停機'} ) : ( {}} isSelected={selectedDrawer === 1} > { setChargingBasedOnWatt(!chargingBasedOnWatt); setStopChargingUponBatteryFull(false); }} > 按每度電 {/* { setStopChargingUponBatteryFull(!stopChargingUponBatteryFull); setChargingBasedOnWatt(false); setSelectedDrawer(2); setOpenDrawer(2); }} className={`border rounded-lg border-[#34667c] w-[47%] items-center bg-white ${ stopChargingUponBatteryFull ? ' bg-[#34667c]' : '' }`} > 充滿停機 */} {chargingBasedOnWatt === true && ( {['20 kWh~25mins', '25 kWh~30mins', '30 kWh~40mins', '40 kWh~45mins'].map( (watt) => ( { setSelectedWatt(watt); setOpenDrawer(2); setSelectedDrawer(2); handleSendingSize(watt); }} > {watt.split('~')[0]} {watt.split('~')[1]} ) )} )} )} {/* select gun */} {/* select gun */} {selectedChargingGun !== '' ? ( { setSelectedChargingGun(''); setOpenDrawer(2); setSelectedDrawer(2); }} > 選擇充電座 {connectorIDToLabelMap[selectedChargingGun]} 號充電座 ) : ( { if (selectedWatt) { toggleDrawer(2); } }} isSelected={selectedDrawer === 2} > {selectedWatt !== '' ? ( { setSelectedChargingGun(value); setSelectedDrawer(3); setOpenDrawer(3); }} extendedStyle={{ borderColor: '#34667c', marginTop: 4, padding: 12 }} /> ) : ( 請先選擇充電方案 )} )} setModalVisible(false)} backdropOpacity={0.5} animationIn="fadeIn" animationOut="fadeOut" > 已選擇日期時間: {formatDateString(selectedDate)} - {selectedTime} 若客戶逾時超過15分鐘,系統將視作自動放棄預約,客戶需要重新預約一次。 本公司有權保留全數費用,恕不退還。按下確認代表您已閱讀並同意上述條款。 我確認} onPress={handleModalConfirm} extendedStyle={styles.confirmButton} /> {selectedDate !== '' && selectedTime !== '' ? ( <> { setOpenDrawer(3); setSelectedDrawer(3); setSelectedDate(''); setSelectedTime(''); }} > 選擇日期 {formatDateString(selectedDate)} - {selectedTime} ) : ( { if (stopChargingUponBatteryFull !== false || selectedDuration !== '') { toggleDrawer(3); } }} isSelected={selectedDrawer === 3} > {isDateLoading ? ( ) : ( {availableDate.map((date) => ( { setSelectedDate(date); handleDateToTimeSlot(date, selectedChargingGun); }} > {formatDateString(date)} ))} )} {selectedDate !== '' && ( <> 選擇時間 {isLoading ? ( ) : ( {availableTimeSlots.map((slot, index) => ( { 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); }} > {slot.startTime} ))} )} )} )} {/* { if (selectedTime) { // toggleDrawer(3); } }} isSelected={selectedDrawer === 3} > { setSelectedChargingGun(value); handleConfirmation(value); }} extendedStyle={{ borderColor: '#34667c', marginTop: 4, padding: 12 }} /> setModalVisible(false)} backdropOpacity={0.5} animationIn="fadeIn" animationOut="fadeOut" > 若客戶逾時超過15分鐘,系統將視作自動放棄預約,客戶需要重新預約一次。 本公司有權保留全數費用,恕不退還。按下確認代表您已閱讀並同意上述條款。 我確認} onPress={handleModalConfirm} extendedStyle={styles.confirmButton} /> */} 充電站資訊 ); }; export default MakingBookingPageComponent; const styles = StyleSheet.create({ grayColor: { color: '#888888' }, topLeftTriangle: { width: 0, height: 0, borderLeftWidth: 50, borderBottomWidth: 50, borderLeftColor: '#02677D', borderBottomColor: 'transparent', position: 'absolute', top: 0, left: 0 }, modalContent: { backgroundColor: 'white', padding: 22, justifyContent: 'center', alignItems: 'center', borderRadius: 4, borderColor: 'rgba(0, 0, 0, 0.1)' }, modalText: { fontSize: 18, marginBottom: 12, textAlign: 'center' }, confirmButton: { backgroundColor: '#34667c', paddingHorizontal: 30, paddingVertical: 10, borderRadius: 5 }, text: { fontWeight: 300, color: '#000000' } });