Ian Fung 1 year ago
parent
commit
9419bfab0d

+ 11 - 3
app.json

@@ -2,7 +2,7 @@
     "expo": {
         "name": "Crazycharge",
         "slug": "template",
-        "version": "1.1.10",
+        "version": "1.1.12",
         "orientation": "portrait",
         "icon": "./assets/CC_Logo.png",
         "userInterfaceStyle": "light",
@@ -24,7 +24,7 @@
             },
             "permissions": ["android.permission.CAMERA", "android.permission.RECORD_AUDIO"],
             "package": "hk.com.crazycharge",
-            "versionCode": 6
+            "versionCode": 7
         },
         "web": {
             "favicon": "./assets/favicon.png"
@@ -36,9 +36,17 @@
                 "expo-camera",
                 {
                     "cameraPermission": "我們需要相機權限來掃描機器上的二維碼,以便識別並啟動充電機器。我們不會儲存或共享任何掃描到的資訊。",
-                    "microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone",
+                    "microphonePermission": "我們需要麥克風權限來允許語音輸入,以便提供更好的充電體驗。我們不會儲存或共享任何錄製到的資訊。",
                     "recordAudioAndroid": true
                 }
+            ],
+            [
+                "expo-location",
+                {
+                    "locationAlwaysAndWhenInUsePermission": "我們需要您的位置資訊來尋找附近的充電站。您的位置資訊只會被用於尋找充電站。我們不會儲存或共享任何資訊",
+                    "locationAlwaysPermission": "我們需要您的位置資訊來尋找附近的充電站。您的位置資訊只會被用於尋找充電站。我們不會儲存或共享任何資訊",
+                    "locationWhenInUsePermission": "我們需要您的位置資訊來尋找附近的充電站。您的位置資訊只會被用於尋找充電站。我們不會儲存或共享任何資訊"
+                }
             ]
         ],
         "extra": {

+ 255 - 240
app/(auth)/(tabs)/(home)/scanQrPage.tsx

@@ -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'
     }
 });
 

BIN
assets/dummyStationPicture.png


BIN
assets/dummyStationPicture1.png


+ 10 - 1
component/accountPages/accountMainPageComponent.tsx

@@ -8,6 +8,7 @@ import {
     MyCarSvg,
     QuestionMarkIconSvg,
     SettingIconSvg,
+    VipCodeIcoonSvg,
     WalletSvg
 } from '../global/SVG';
 
@@ -32,13 +33,21 @@ const AccountMainPageComponent = () => {
                         <Text>錢包</Text>
                     </Pressable>
 
-                    <Pressable
+                    {/* <Pressable
                         className="h-[114px] w-[30%] bg-[#e7f2f8] items-center justify-center rounded-xl"
                         onPress={() => router.replace('myVehiclePage')}
                     >
                         <MyCarSvg />
                         <Text>我的車輛</Text>
+                    </Pressable> */}
+                    <Pressable
+                        className="h-[114px] w-[30%] bg-[#e7f2f8] items-center justify-center rounded-xl"
+                        onPress={() => router.replace('vipQrPage')}
+                    >
+                        <VipCodeIcoonSvg />
+                        <Text>VIP Code</Text>
                     </Pressable>
+
                     <Pressable
                         className="h-[114px] w-[30%] bg-[#e7f2f8] items-center justify-center rounded-xl"
                         onPress={() => router.push('/activityRecordPage')}

+ 14 - 3
component/bookingMenuPage/bookingConfirmationPage.tsx

@@ -9,7 +9,7 @@ import useBookingStore from '../../providers/booking_store';
 
 const BookingConfirmationPageComponent = () => {
     const params = useLocalSearchParams();
-    console.log(params);
+    console.log('paramsparamsparamsparamsparamsparamsparamsparams', params);
 
     const { setSelectedCouponName, setSelectedCouponRedeemCode } = useCouponStore();
 
@@ -114,13 +114,24 @@ const BookingConfirmationPageComponent = () => {
                             </View>
                             <View className="flex-column flex-1">
                                 <Text style={styles.grayColor} className="text-base">
-                                    車輛
+                                    預約充電座號碼
                                 </Text>
                                 <Text style={styles.greenColor} className="text-lg">
-                                    {params.carName}
+                                    {params.connectorLabel}號充電座
                                 </Text>
                             </View>
                         </View>
+                        {/* <View className="flex-1 flex-row  pb-3  ">
+                            <View className="flex-column flex-1">
+                                <Text style={styles.grayColor} className="text-base">
+                                    預約充電座號碼
+                                </Text>
+
+                                <Text style={styles.greenColor} className="text-lg">
+                                    {params.connectorLabel}號充電座
+                                </Text>
+                            </View>
+                        </View> */}
                     </View>
                 </View>
 

+ 179 - 109
component/bookingMenuPage/makingBookingPageComponent.tsx

@@ -23,7 +23,7 @@ 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';
 interface AccordionItemProps {
     title: string;
     children: React.ReactNode;
@@ -44,21 +44,26 @@ const AccordionItem: React.FC<AccordionItemProps> = ({ title, children, isOpen,
 );
 
 const MakingBookingPageComponent = () => {
-    const [openDrawer, setOpenDrawer] = useState<number | null>(0);
+    const [isModalVisible, setModalVisible] = useState(false);
+    const [routerParams, setRouterParams] = useState(null);
+    const [openDrawer, setOpenDrawer] = useState<number | null>(1);
     const [selectedTime, setSelectedTime] = useState<string>('');
     const [availableTimeSlots, setAvailableTimeSlots] = useState<string[]>([]);
-    const [selectedDrawer, setSelectedDrawer] = useState<number>(0);
+    const [selectedDrawer, setSelectedDrawer] = useState<number>(1);
     const [isLoading, setIsLoading] = useState(true);
     const [selectedDate, setSelectedDate] = useState<string>('');
     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<string[]>([]);
     const [car, setCar] = useState([]);
     const [selectedCarID, setSelectedCarID] = useState('');
     const [selectedChargingGun, setSelectedChargingGun] = useState('');
-    const [chargingBasedOnWatt, setChargingBasedOnWatt] = useState(false);
+    const [chargingBasedOnWatt, setChargingBasedOnWatt] = useState(true);
     const [stopChargingUponBatteryFull, setStopChargingUponBatteryFull] = useState(false);
     const [selectedCar, setSelectedCar] = useState('');
     const [selectedDuration, setSelectedDuration] = useState('');
@@ -96,6 +101,27 @@ const MakingBookingPageComponent = () => {
         getCurrentLocation();
     }, []);
 
+    //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)}米`;
@@ -189,16 +215,15 @@ const MakingBookingPageComponent = () => {
     }, [params.chargeStationLat, params.chargeStationLng, currentLocation]);
 
     useEffect(() => {
-        const fetchChargeStation = async () => {
+        const fetchPrice = async () => {
             try {
-                const data = await chargeStationService.fetchPriceForCharging();
-                setPrice(data.data[0].price);
-                // console.log(JSON.stringify(fetchedChargeStation.data));
+                const price = await chargeStationService.fetchChargeStationPrice(chargeStationID);
+                setPrice(price);
             } catch (error) {
-                console.log(error);
+                console.error('Error fetching price:', error);
             }
         };
-        fetchChargeStation();
+        fetchPrice();
     }, []);
 
     function appendImageUrlToCarResult(carData, updatedVehicles) {
@@ -214,53 +239,53 @@ const MakingBookingPageComponent = () => {
         });
     }
 
-    useEffect(() => {
-        setCarLoadingState(true);
-        const fetchUserInfoAndCarData = async () => {
-            try {
-                const fetchedUserInfo = await authenticationService.getUserInfo();
-                const userData = fetchedUserInfo?.data;
-                // console.log('userData', userData);
-                if (userData) {
-                    setUserID(userData.id);
-
-                    const carData = userData.cars.map((car: any) => ({
-                        car_name: car.name,
-                        car_capacitance: car.capacitance,
-                        car_id: car.id,
-                        isDefault: car.id === userData.defaultCar?.id
-                    }));
-                    // console.log('carDarta', carData);
-                    setCar(carData);
-
-                    const carResult = await chargeStationService.getUserCars();
-                    let updatedVehicles = [...carResult.data];
-                    // console.log('updatedVehiaacles', updatedVehicles);
-
-                    const updatedCarResult = appendImageUrlToCarResult(carData, updatedVehicles);
-                    setCar(updatedCarResult);
-
-                    let updatedCarResultWithProcessedUrl = [...updatedCarResult];
-
-                    for (let i = 0; i < updatedCarResultWithProcessedUrl.length; i++) {
-                        const car = updatedCarResultWithProcessedUrl[i];
-                        const processedUrl = await chargeStationService.getProcessedImageUrl(car.type_image_url);
-                        updatedCarResultWithProcessedUrl[i] = {
-                            ...car,
-                            processedImageUrl: processedUrl
-                        };
-                    }
-                    // console.log(updatedCarResultWithProcessedUrl);
-                    setCar(updatedCarResultWithProcessedUrl);
-                }
-            } catch (error) {
-                console.error('Error fetching user info:', error);
-            } finally {
-                setCarLoadingState(false);
-            }
-        };
-        fetchUserInfoAndCarData();
-    }, []);
+    // useEffect(() => {
+    //     setCarLoadingState(true);
+    //     const fetchUserInfoAndCarData = async () => {
+    //         try {
+    //             const fetchedUserInfo = await authenticationService.getUserInfo();
+    //             const userData = fetchedUserInfo?.data;
+    //             // console.log('userData', userData);
+    //             if (userData) {
+    //                 setUserID(userData.id);
+
+    //                 const carData = userData.cars.map((car: any) => ({
+    //                     car_name: car.name,
+    //                     car_capacitance: car.capacitance,
+    //                     car_id: car.id,
+    //                     isDefault: car.id === userData.defaultCar?.id
+    //                 }));
+    //                 // console.log('carDarta', carData);
+    //                 setCar(carData);
+
+    //                 const carResult = await chargeStationService.getUserCars();
+    //                 let updatedVehicles = [...carResult.data];
+    //                 // console.log('updatedVehiaacles', updatedVehicles);
+
+    //                 const updatedCarResult = appendImageUrlToCarResult(carData, updatedVehicles);
+    //                 setCar(updatedCarResult);
+
+    //                 let updatedCarResultWithProcessedUrl = [...updatedCarResult];
+
+    //                 for (let i = 0; i < updatedCarResultWithProcessedUrl.length; i++) {
+    //                     const car = updatedCarResultWithProcessedUrl[i];
+    //                     const processedUrl = await chargeStationService.getProcessedImageUrl(car.type_image_url);
+    //                     updatedCarResultWithProcessedUrl[i] = {
+    //                         ...car,
+    //                         processedImageUrl: processedUrl
+    //                     };
+    //                 }
+    //                 // console.log(updatedCarResultWithProcessedUrl);
+    //                 setCar(updatedCarResultWithProcessedUrl);
+    //             }
+    //         } catch (error) {
+    //             console.error('Error fetching user info:', error);
+    //         } finally {
+    //             setCarLoadingState(false);
+    //         }
+    //     };
+    //     fetchUserInfoAndCarData();
+    // }, []);
 
     useEffect(() => {
         const fetchingAvailableTimeSlots = async () => {
@@ -400,6 +425,38 @@ const MakingBookingPageComponent = () => {
             .catch((err) => console.error('An error occurred', err));
     };
 
+    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 (
         <SafeAreaView
             style={{
@@ -454,8 +511,9 @@ const MakingBookingPageComponent = () => {
                         </View>
                     </View>
                 </View>
+
                 <View>
-                    {selectedCar !== '' ? (
+                    {/* {selectedCar !== '' ? (
                         <>
                             <Pressable
                                 onPress={() => {
@@ -520,7 +578,7 @@ const MakingBookingPageComponent = () => {
                                 </ScrollView>
                             )}
                         </AccordionItem>
-                    )}
+                    )} */}
 
                     {stopChargingUponBatteryFull === true || selectedWatt !== '' ? (
                         <Pressable
@@ -733,26 +791,7 @@ const MakingBookingPageComponent = () => {
                                     placeholder={'選擇充電座號碼'}
                                     onSelect={(value) => {
                                         setSelectedChargingGun(value);
-                                        router.push({
-                                            pathname: '/bookingConfirmationPage',
-                                            params: {
-                                                chargeStationName,
-                                                chargeStationAddress,
-                                                chargeStationID,
-                                                connectorID: value,
-                                                userID,
-                                                carCapacitance: carCapacitance,
-                                                carID: selectedCarID,
-                                                carName: selectedCar,
-                                                date: selectedDate,
-                                                bookTime: selectedTime,
-                                                chargingMethod: stopChargingUponBatteryFull
-                                                    ? 'stopChargingUponBatteryFull'
-                                                    : 'chargingBasedOnWatt',
-                                                chargingWatt: selectedWatt || '',
-                                                price: price
-                                            }
-                                        });
+                                        handleConfirmation(value);
                                     }}
                                     extendedStyle={{
                                         borderColor: '#34667c',
@@ -769,46 +808,58 @@ const MakingBookingPageComponent = () => {
                                     resizeMode="contain"
                                     source={require('../../assets/floorPlan1.png')}
                                 />
+
+                                <Modal
+                                    isVisible={isModalVisible}
+                                    // onBackdropPress={() => setModalVisible(false)}
+                                    backdropOpacity={0.5}
+                                    animationIn="fadeIn"
+                                    animationOut="fadeOut"
+                                >
+                                    <View style={styles.modalContent}>
+                                        <Text style={styles.modalText}>
+                                            若客戶逾時超過15分鐘,系統將視作自動放棄預約,客戶需要重新預約一次。
+                                            本公司有權保留全數費用,恕不退還。按下確認代表您已閱讀並同意上述條款。
+                                        </Text>
+                                        <NormalButton
+                                            title={<Text className="text-white">我確認</Text>}
+                                            onPress={handleModalConfirm}
+                                            extendedStyle={styles.confirmButton}
+                                        />
+                                    </View>
+                                </Modal>
                             </View>
                         </AccordionItem>
                     </View>
                 </View>
 
-                <View className="mx-[5%] flex-1">
-                    <View className="flex-row border-slate-300 mt-3 mb-6 rounded-2xl flex-1" style={{ borderWidth: 1 }}>
-                        <View className="flex-1 m-4">
-                            <View className="flex-1 flex-row ">
-                                <View className=" flex-1 flex-column  justify-between">
-                                    <Text className="text-xl " style={styles.text}>
-                                        收費
-                                    </Text>
-
-                                    <View className="flex-row items-center space-x-2">
-                                        <Text className="text-3xl text-[#02677D]">$20</Text>
-                                        <Text style={styles.text}>每15分鐘</Text>
-                                    </View>
-                                </View>
-                                <View className="items-center justify-center">
-                                    <View className="w-[1px] h-[60%] bg-[#CCCCCC]" />
-                                </View>
-                                <View className=" flex-1 pl-4 flex-column  justify-between">
-                                    <Text className="text-xl " style={styles.text}>
-                                        可用充電槍數目
-                                    </Text>
-
-                                    <View className="flex-row items-center space-x-2">
-                                        <Text className="text-3xl text-[#02677D]">4</Text>
-                                    </View>
-                                </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>
+                ) : (
+                    ''
+                )} */}
 
-                    <Text className="text-xl pb-2 " style={styles.text}>
+                <View className="mx-[5%] flex-1">
+                    <Text className="text-xl pb-2 mt-6" style={styles.text}>
                         充電站資訊
                     </Text>
                     <View className="h-[250px]">
-                        <ChargingStationTabView titles={['充電插頭', '其他']} />
+                        <ChargingStationTabView titles={['預約充電事項', '其他']} />
                     </View>
                 </View>
             </ScrollView>
@@ -832,6 +883,25 @@ const styles = StyleSheet.create({
         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'

+ 1 - 1
component/chargingPage/chargingFinishPageComponent.tsx

@@ -208,7 +208,7 @@ const ChargingFinishPageComponent = ({ data }: { data: any }) => {
                         <Text style={styles.grayColor} className="text-base">
                             {chargingData.total_power === null
                                 ? '充滿停機'
-                                : `按每度電結算: ${chargingData.total_power} kWh`}
+                                : `按每度電結算: ${Math.floor(chargingData.total_power)} kWh`}
                         </Text>
                         <View className="h-0.5 my-3 bg-[#f4f4f4]" />
                         {chargingData.withdraw_fee === 0 ? (

+ 3 - 10
component/chargingPage/chargingHurryUpPageComponent.tsx

@@ -20,7 +20,6 @@ const ChargingHurryUpPageComponent = ({ data = {} }) => {
     const [isLoading, setIsLoading] = useState(false);
     const plan = reservationData.book_time === reservationData.end_time ? '充滿停機' : '按每度電結算';
     const kwh = reservationData.total_power === null ? '' : `${reservationData.total_power} kWh`;
-    const walkinStatus = reservationData.book_time === reservationData.end_time ? true : false;
 
     useEffect(() => {
         setLoading(true);
@@ -77,21 +76,15 @@ const ChargingHurryUpPageComponent = ({ data = {} }) => {
             <ScrollView className="flex-1 mt-8 " nestedScrollEnabled={true} showsVerticalScrollIndicator={false}>
                 <View className="mx-[5%]">
                     <View className="">
-                        <Text className="text-5xl pt-1 pb-6">{walkinStatus ? '掃描成功' : '預約已經開始'}</Text>
-                        <Text className="text-base">
-                            {walkinStatus
-                                ? '請按下方按鈕啟動充電槍,當充電槍啟動成功後會自動跳轉至充電介面。'
-                                : '到達充電站後,按下方按鈕開始充電。'}
-                        </Text>
+                        <Text className="text-5xl pt-1 pb-6">預約已經開始</Text>
+                        <Text className="text-base">到達充電站後,按下方按鈕開始充電。</Text>
                         {loading ? (
                             <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                                 <ActivityIndicator color="#34657b" />
                             </View>
                         ) : (
                             <>
-                                <Text className="text-2xl py-4 ">
-                                    {walkinStatus ? '你的充電資訊為:' : '你的預約為:'}
-                                </Text>
+                                <Text className="text-2xl py-4 ">你的預約為:</Text>
                                 <View className="border-gray-200 border rounded-md">
                                     <View className="flex-1 mt-4 mx-[5%] ">
                                         <View className="flex-1 flex-row items-center pb-3">

+ 3 - 11
component/chargingPage/chargingPageComponent.tsx

@@ -220,17 +220,9 @@ const ChargingPageComponent = ({ data }) => {
                                     <Text style={styles.greenColor} className="font-bold text-base">
                                         {onGoingChargingData &&
                                         onGoingChargingData.VoltageA &&
-                                        onGoingChargingData.VoltageB &&
-                                        onGoingChargingData.VoltageC &&
-                                        onGoingChargingData.CurrentA &&
-                                        onGoingChargingData.CurrentB &&
-                                        onGoingChargingData.CurrentC
-                                            ? (
-                                                  (onGoingChargingData.VoltageA * onGoingChargingData.CurrentA +
-                                                      onGoingChargingData.VoltageB * onGoingChargingData.CurrentB +
-                                                      onGoingChargingData.VoltageC * onGoingChargingData.CurrentC) /
-                                                  1000
-                                              ).toFixed(2) + ' kW'
+                                        onGoingChargingData.CurrentA
+                                            ? (onGoingChargingData.VoltageA * onGoingChargingData.CurrentA).toFixed(2) +
+                                              ' kW'
                                             : '請見充電顯示螢幕'}
                                     </Text>
                                 )}

+ 0 - 1
component/chargingPage/paymentSummaryPageComponent.tsx

@@ -13,7 +13,6 @@ import { walletService } from '../../service/walletService';
 const PaymentSummaryPageComponent = () => {
     const selectedCouponName = useCouponStore((state) => state.selectedCouponName);
     const selectedCouponRedeemCode = useCouponStore((state) => state.selectedCouponRedeemCode);
-
     const selectedCouponPrice = useCouponStore((state) => state.selectedCouponPrice);
     const params = useLocalSearchParams();
     const setBookingInfo = useBookingStore((state) => state.setBookingInfo);

+ 9 - 0
component/global/SVG.tsx

@@ -27,6 +27,15 @@ export const VipCodeIconSvg = () => (
     </Svg>
 );
 
+export const VipCodeIcoonSvg = () => (
+    <Svg width="40" height="40" viewBox="0 0 24 24" fill="none">
+        <Path
+            d="M3 11V3H11V11H3ZM5 9H9V5H5V9ZM3 21V13H11V21H3ZM5 19H9V15H5V19ZM13 11V3H21V11H13ZM15 9H19V5H15V9ZM19 21V19H21V21H19ZM13 15V13H15V15H13ZM15 17V15H17V17H15ZM13 19V17H15V19H13ZM15 21V19H17V21H15ZM17 19V17H19V19H17ZM17 15V13H19V15H17ZM19 17V15H21V17H19Z"
+            fill="#004E5F"
+        />
+    </Svg>
+);
+
 export const MyVehicleIconSvg = () => (
     <Svg width="24" height="24" viewBox="0 0 24 24" fill="none">
         <Path

+ 16 - 2
component/homePage/homePage.tsx

@@ -51,7 +51,7 @@ const HomePage: React.FC<HomePageProps> = () => {
                         <View className="pl-2 flex-1 flex-column ">
                             <View className="flex-row justify-between">
                                 <Text className="text-lg pb-1">你好!</Text>
-                                <BellIconSvg />
+                                {/* <BellIconSvg /> */}
                             </View>
                             <Text className="text-4xl font-light ">{user?.nickname}</Text>
                         </View>
@@ -109,7 +109,7 @@ const HomePage: React.FC<HomePageProps> = () => {
                             />
                         </View>
                         <View className="flex-1">
-                            <NormalButton
+                            {/* <NormalButton
                                 onPress={() => router.push('myVehiclePage')}
                                 title={
                                     <View className="flex flex-row space-x-2 items-center">
@@ -121,6 +121,20 @@ const HomePage: React.FC<HomePageProps> = () => {
                                     alignItems: 'flex-start',
                                     padding: 24
                                 }}
+                            /> */}
+
+                            <NormalButton
+                                onPress={() => router.push('accountMainPage')}
+                                title={
+                                    <View className="flex flex-row space-x-2 items-center">
+                                        <MyVehicleIconSvg />
+                                        <Text className="text-white font-bold text-lg">我的帳戶</Text>
+                                    </View>
+                                }
+                                extendedStyle={{
+                                    alignItems: 'flex-start',
+                                    padding: 24
+                                }}
                             />
                         </View>
                     </View>

+ 6 - 6
component/registrationMultiStepForm/formComponent/form.tsx

@@ -17,8 +17,8 @@ import SetVehiclesTwo from '../../../app/(auth)/(tabs)/(home)/(vehicle)/setVehic
 
 type FormProps = {};
 const Form: React.FC<FormProps> = ({}) => {
-    const [screen, setScreen] = useState<number>(2);
-    const FormTitle = ['', '註冊 - 電郵驗證', '註冊 - 基本資料', '註冊 - 車輛資料', '註冊 - 設立銀包'];
+    const [screen, setScreen] = useState<number>(0);
+    const FormTitle = ['', '註冊 - 電郵驗證', '註冊 - 基本資料', '註冊 - 基本資料', '註冊 - 設立銀包'];
 
     const ScreenDisplay = () => {
         switch (screen) {
@@ -30,11 +30,11 @@ const Form: React.FC<FormProps> = ({}) => {
                 return <BasicInformation goToNextPage={goToNextPage} />;
             // case 3:
             //     return <UberDriver goToNextPage={goToNextPage} />;
+            // case 3:
+            //     return <CarInformation goToNextPage={goToNextPage} goToChooseCarPage={goToChooseCarBrandPage} />;
             case 3:
-                return <CarInformation goToNextPage={goToNextPage} goToChooseCarPage={goToChooseCarBrandPage} />;
-            case 4:
                 return <CreateWallet goToNextPage={goToNextPage} />;
-            case 5:
+            case 4:
                 return <FinishSignUp />;
             case 6:
                 return (
@@ -121,7 +121,7 @@ const Form: React.FC<FormProps> = ({}) => {
             >
                 {/* not showing title and pagination on the first and last page */}
                 {screen == 0 ||
-                    (screen < 5 && (
+                    (screen < 4 && (
                         //dismiss keyboard when user click outside of the input field to improve user experience
                         <TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
                             <View style={styles.topContainer}>

+ 21 - 6
component/registrationMultiStepForm/formComponent/formPages/basicInformation.tsx

@@ -24,7 +24,6 @@ const BasicInformation: React.FC<basicInformationProps> = ({ goToNextPage }) =>
         });
         if (
             signUpFormData.nickName === '' ||
-            signUpFormData.password === '' ||
             signUpFormData.gender === '' ||
             signUpFormData.birthDateMonth === '' ||
             signUpFormData.birthDateDay === ''
@@ -93,7 +92,7 @@ const BasicInformation: React.FC<basicInformationProps> = ({ goToNextPage }) =>
                         }}
                     />
 
-                    <NormalInput
+                    {/* <NormalInput
                         placeholder="帳戶密碼"
                         onChangeText={(text) => {
                             setSignUpFormData({
@@ -102,7 +101,7 @@ const BasicInformation: React.FC<basicInformationProps> = ({ goToNextPage }) =>
                             });
                         }}
                         secureTextEntry={true}
-                    />
+                    /> */}
 
                     <View className="">
                         {/* <TestingDropDownSelect
@@ -124,7 +123,12 @@ const BasicInformation: React.FC<basicInformationProps> = ({ goToNextPage }) =>
                                 });
                             }}
                             dropdownOptions={genderDropdownOptions}
-                            placeholder={`性別`}
+                            placeholder={
+                                signUpFormData.gender
+                                    ? genderDropdownOptions.find((option) => option.value === signUpFormData.gender)
+                                          ?.label || '性別'
+                                    : '性別'
+                            }
                             extendedStyle={{ paddingLeft: 10, paddingVertical: 18 }}
                         />
                     </View>
@@ -137,7 +141,12 @@ const BasicInformation: React.FC<basicInformationProps> = ({ goToNextPage }) =>
                                 });
                             }}
                             dropdownOptions={birthMonthOptions}
-                            placeholder={'生日月份'}
+                            // placeholder={'生日月份'}
+                            placeholder={`${
+                                signUpFormData.birthDateMonth
+                                    ? parseInt(signUpFormData.birthDateMonth).toString()
+                                    : '生日月份'
+                            }`}
                             extendedStyle={{ paddingLeft: 10, paddingVertical: 18, marginRight: 6 }}
                         />
                         <DropdownSelect
@@ -148,7 +157,12 @@ const BasicInformation: React.FC<basicInformationProps> = ({ goToNextPage }) =>
                                 });
                             }}
                             dropdownOptions={birthDayOptions}
-                            placeholder={'日期'}
+                            // placeholder={'日期'}
+                            placeholder={`${
+                                signUpFormData.birthDateDay
+                                    ? parseInt(signUpFormData.birthDateDay).toString()
+                                    : '生日日期'
+                            }`}
                             extendedStyle={{ paddingLeft: 10, paddingVertical: 18, marginLeft: 6 }}
                         />
                     </View>
@@ -157,6 +171,7 @@ const BasicInformation: React.FC<basicInformationProps> = ({ goToNextPage }) =>
                         onPress={handleNext}
                         extendedStyle={{}}
                     />
+                    <View className="mb-4" />
                 </View>
                 {error && <Text style={styles.errorMessage}>{error}</Text>}
             </View>

+ 12 - 3
component/registrationMultiStepForm/formComponent/formPages/carInformation.tsx

@@ -377,10 +377,19 @@ const CarInformation: React.FC<CarInformationProps> = ({ goToNextPage, goToChoos
                                 });
                                 // console.log(licensePlate);
                             }}
-                            // value={signUpFormData.licensePlate}
-                            placeholder="車輛牌照號碼"
+                            value={
+                                signUpFormData.licensePlate === '0000' || signUpFormData.licensePlate == ''
+                                    ? ''
+                                    : signUpFormData.licensePlate
+                            }
+                            // placeholder="車輛牌照號碼"
+                            placeholder={
+                                signUpFormData.licensePlate === '0000' || signUpFormData.licensePlate == ''
+                                    ? '車輛牌照號碼'
+                                    : signUpFormData.licensePlate
+                            }
                             autoCapitalize="characters"
-                            placeholderTextColor="#888"
+                            placeholderTextColor="#bbbbbb"
                         />
                     </View>
 

+ 23 - 5
component/registrationMultiStepForm/formComponent/formPages/createWallet.tsx

@@ -21,7 +21,7 @@ const CreateWallet: React.FC<CreateWalletProps> = ({ goToNextPage }) => {
     const { signUpFormData, setSignUpFormData } = useSignUpStore();
     const phoneFieldPlaceholder = signUpFormData.phone ? signUpFormData.phone : '輸入電話號碼';
     const [error, setError] = useState('');
-
+    const [passwordConfirm, setPasswordConfirm] = useState('');
     const {
         vehicleBrand,
         vehicleModel,
@@ -89,8 +89,10 @@ const CreateWallet: React.FC<CreateWalletProps> = ({ goToNextPage }) => {
     };
 
     const handleNext = async () => {
-        if (signUpFormData.paymentMethod === '' || signUpFormData.phone === '') {
+        if (signUpFormData.password === '' || signUpFormData.phone === '' || passwordConfirm === '') {
             setError('請確保所有資料都已填寫。');
+        } else if (signUpFormData.password !== passwordConfirm) {
+            Alert.alert('密碼錯誤', '兩次輸入的密碼不一致,請重新輸入。');
         } else {
             setError('');
             setIsLoading(true);
@@ -132,12 +134,28 @@ const CreateWallet: React.FC<CreateWalletProps> = ({ goToNextPage }) => {
                         }}
                         placeholder={phoneFieldPlaceholder}
                     />
-
-                    <SingleSelectButtonGroup
+                    <NormalInput
+                        placeholder="帳戶密碼"
+                        onChangeText={(text) => {
+                            setSignUpFormData({
+                                ...signUpFormData,
+                                password: text
+                            });
+                        }}
+                        secureTextEntry={true}
+                    />
+                    <View className="mb-4">
+                        <NormalInput
+                            placeholder="請再次輸入帳戶密碼"
+                            onChangeText={(text) => setPasswordConfirm(text)}
+                            secureTextEntry={true}
+                        />
+                    </View>
+                    {/* <SingleSelectButtonGroup
                         options={options}
                         onSelectionChange={handleSelectedChange}
                         selectedOption={selectLabelShown()}
-                    />
+                    /> */}
                 </View>
                 {error && <Text style={styles.errorMessage}>{error}</Text>}
                 <View>

+ 75 - 10
component/registrationMultiStepForm/formComponent/formPages/loginPage.tsx

@@ -3,21 +3,58 @@ import { View, Text, Image, StyleSheet, Pressable, Alert, Dimensions, ActivityIn
 import Logo from '../../../global/logo';
 import NormalButton from '../../../global/normal_button';
 import NormalInput from '../../../global/normal_input';
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
 import { useAuth } from '../../../../context/AuthProvider';
 import { useSafeAreaInsets } from 'react-native-safe-area-context';
+import AsyncStorage from '@react-native-async-storage/async-storage';
+import Checkbox from 'expo-checkbox';
 type LoginPageProps = {
     goToNextPage: () => void;
     goToForgetPassWordPage: () => void;
 };
 
 const screenHeight = Dimensions.get('window').height;
-console.log(screenHeight);
+
 const LoginPage: React.FC<LoginPageProps> = ({ goToNextPage, goToForgetPassWordPage }) => {
     const [loginEmail, setLoginEmail] = useState('');
     const [loginPassword, setLoginPassword] = useState('');
+    const [saveAccount, setSaveAccount] = useState(false);
     const [isLoading, setIsLoading] = useState(false);
     const { login } = useAuth();
+    const [isChecked, setChecked] = useState(false);
+
+    useEffect(() => {
+        loadSavedCredentials();
+    }, []);
+
+    const loadSavedCredentials = async () => {
+        try {
+            const savedEmail = await AsyncStorage.getItem('savedEmail');
+            const savedPassword = await AsyncStorage.getItem('savedPassword');
+            if (savedEmail && savedPassword) {
+                setLoginEmail(savedEmail);
+                setLoginPassword(savedPassword);
+                setSaveAccount(true);
+                setChecked(true);
+            }
+        } catch (error) {
+            console.error('Error loading saved credentials:', error);
+        }
+    };
+
+    // const _login = async (username: string, password: string) => {
+    //     setIsLoading(true);
+    //     if (username === '' || password === '') {
+    //         Alert.alert('請輸入資料', '請輸入電子郵件和密碼');
+    //     } else {
+    //         const lowerCaseUsername = username.toLowerCase();
+    //         const success = await login(lowerCaseUsername, password);
+    //         if (!success) {
+    //             Alert.alert('登入失敗', '請檢查您的電子郵件和密碼');
+    //         }
+    //     }
+    //     setIsLoading(false);
+    // };
 
     const _login = async (username: string, password: string) => {
         setIsLoading(true);
@@ -26,12 +63,21 @@ const LoginPage: React.FC<LoginPageProps> = ({ goToNextPage, goToForgetPassWordP
         } else {
             const lowerCaseUsername = username.toLowerCase();
             const success = await login(lowerCaseUsername, password);
-            if (!success) {
+            if (success) {
+                if (saveAccount) {
+                    await AsyncStorage.setItem('savedEmail', lowerCaseUsername);
+                    await AsyncStorage.setItem('savedPassword', password);
+                } else {
+                    await AsyncStorage.removeItem('savedEmail');
+                    await AsyncStorage.removeItem('savedPassword');
+                }
+            } else {
                 Alert.alert('登入失敗', '請檢查您的電子郵件和密碼');
             }
         }
         setIsLoading(false);
     };
+
     const insets = useSafeAreaInsets();
 
     return (
@@ -56,6 +102,7 @@ const LoginPage: React.FC<LoginPageProps> = ({ goToNextPage, goToForgetPassWordP
             >
                 <NormalInput
                     placeholder="電子郵件"
+                    value={loginEmail}
                     onChangeText={(email) => setLoginEmail(email)}
                     extendedStyle={{ borderRadius: 12, padding: 20 }}
                     textContentType="username"
@@ -64,6 +111,7 @@ const LoginPage: React.FC<LoginPageProps> = ({ goToNextPage, goToForgetPassWordP
                     autoCapitalize="none"
                 />
                 <NormalInput
+                    value={loginPassword}
                     placeholder="密碼"
                     onChangeText={(password) => setLoginPassword(password)}
                     secureTextEntry={true}
@@ -71,10 +119,20 @@ const LoginPage: React.FC<LoginPageProps> = ({ goToNextPage, goToForgetPassWordP
                     textContentType="password"
                     autoComplete="password"
                 />
+                <View className="flex flex-row items-center ">
+                    <Checkbox
+                        style={styles.checkbox}
+                        value={saveAccount}
+                        color={saveAccount ? '#02677D' : '#02677D'}
+                        onValueChange={(newValue) => {
+                            setSaveAccount(newValue);
+                            console.log(newValue);
+                        }}
+                    />
+
+                    <Text style={styles.text}>記住我的帳號</Text>
+                </View>
 
-                <Pressable className="self-start" onPress={() => goToForgetPassWordPage()}>
-                    <Text style={styles.text}>忘記密碼</Text>
-                </Pressable>
                 <NormalButton
                     extendedStyle={{ padding: 20 }}
                     onPress={() => _login(loginEmail, loginPassword)}
@@ -102,9 +160,14 @@ const LoginPage: React.FC<LoginPageProps> = ({ goToNextPage, goToForgetPassWordP
                         )
                     }
                 />
-                <Pressable className="self-start" onPress={goToNextPage}>
-                    <Text style={styles.text}>註冊會員</Text>
-                </Pressable>
+                <View className="flex flex-row justify-between">
+                    <Pressable className="self-start" onPress={goToNextPage}>
+                        <Text style={styles.text}>註冊會員</Text>
+                    </Pressable>
+                    <Pressable className="self-start" onPress={() => goToForgetPassWordPage()}>
+                        <Text style={styles.text}>忘記密碼</Text>
+                    </Pressable>
+                </View>
             </View>
         </View>
     );
@@ -118,7 +181,9 @@ const styles = StyleSheet.create({
         marginHorizontal: 20
     },
     topContainerForLogo: {},
-
+    checkbox: {
+        margin: 8
+    },
     text: {
         color: '#02677D',
         fontSize: 16,

+ 3 - 3
component/resultDetailPage/resultDetailPageComponent.tsx

@@ -389,7 +389,7 @@ const ResultDetailPageComponent = () => {
                                         收費
                                     </Text>
 
-                                    <View className="flex-row items-center space-x-2">
+                                <View className="flex-row items-center space-x-2">
                                         <Text className="text-3xl text-[#02677D]">${price}</Text>
                                         <Text style={styles.text}>每度電</Text>
                                     </View>
@@ -399,7 +399,7 @@ const ResultDetailPageComponent = () => {
                                 </View>
                                 <View className=" flex-1 pl-4 flex-column  justify-between">
                                     <Text className="text-xl " style={styles.text}>
-                                        可用充電槍數目
+                                        現時可用充電槍數目
                                     </Text>
 
                                     <View className="flex-row items-center space-x-2">
@@ -415,7 +415,7 @@ const ResultDetailPageComponent = () => {
                     <Text className="text-xl pb-2 mx-[5%]" style={styles.text}>
                         充電站資訊
                     </Text>
-                    <ChargingStationTabView titles={['充電插頭', '其他']} />
+                    <ChargingStationTabView titles={['預約充電事項', '其他']} />
                 </View>
             </ScrollView>
         </SafeAreaView>

+ 22 - 0
package-lock.json

@@ -39,6 +39,7 @@
         "react-native-gesture-handler": "~2.16.1",
         "react-native-keyboard-aware-scroll-view": "^0.9.5",
         "react-native-maps": "1.14.0",
+        "react-native-modal": "^13.0.1",
         "react-native-modern-datepicker": "^1.0.0-beta.91",
         "react-native-pager-view": "6.3.0",
         "react-native-reanimated": "~3.10.1",
@@ -13239,6 +13240,14 @@
         }
       }
     },
+    "node_modules/react-native-animatable": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/react-native-animatable/-/react-native-animatable-1.3.3.tgz",
+      "integrity": "sha512-2ckIxZQAsvWn25Ho+DK3d1mXIgj7tITkrS4pYDvx96WyOttSvzzFeQnM2od0+FUMzILbdHDsDEqZvnz1DYNQ1w==",
+      "dependencies": {
+        "prop-types": "^15.7.2"
+      }
+    },
     "node_modules/react-native-dotenv": {
       "version": "3.4.11",
       "resolved": "https://registry.npmjs.org/react-native-dotenv/-/react-native-dotenv-3.4.11.tgz",
@@ -13336,6 +13345,19 @@
         }
       }
     },
+    "node_modules/react-native-modal": {
+      "version": "13.0.1",
+      "resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-13.0.1.tgz",
+      "integrity": "sha512-UB+mjmUtf+miaG/sDhOikRfBOv0gJdBU2ZE1HtFWp6UixW9jCk/bhGdHUgmZljbPpp0RaO/6YiMmQSSK3kkMaw==",
+      "dependencies": {
+        "prop-types": "^15.6.2",
+        "react-native-animatable": "1.3.3"
+      },
+      "peerDependencies": {
+        "react": "*",
+        "react-native": ">=0.65.0"
+      }
+    },
     "node_modules/react-native-modern-datepicker": {
       "version": "1.0.0-beta.91",
       "resolved": "https://registry.npmjs.org/react-native-modern-datepicker/-/react-native-modern-datepicker-1.0.0-beta.91.tgz",

+ 1 - 0
package.json

@@ -40,6 +40,7 @@
     "react-native-gesture-handler": "~2.16.1",
     "react-native-keyboard-aware-scroll-view": "^0.9.5",
     "react-native-maps": "1.14.0",
+    "react-native-modal": "^13.0.1",
     "react-native-modern-datepicker": "^1.0.0-beta.91",
     "react-native-pager-view": "6.3.0",
     "react-native-reanimated": "~3.10.1",

+ 19 - 1
service/chargeStationService.tsx

@@ -66,6 +66,24 @@ class ChargeStationService {
         }
     }
 
+    async getUserDefaultCars() {
+        try {
+            const response = await axios.get(`${this.apiUrl}/clients/customer/car/all?queryDefault=true`, {
+                headers: {
+                    Authorization: `Bearer ${await SecureStore.getItemAsync('accessToken')}`
+                }
+            });
+            if (response.status === 200 || response.status === 201) {
+                // console.log(response.data.data);
+                return response.data;
+            } else {
+                console.log('invalid response');
+            }
+        } catch (error) {
+            console.log(error);
+        }
+    }
+
     async addCar(licensePlate: string, carBrandFk: string, carTypeFk: string, isDefault: boolean) {
         try {
             const response = await axios.post(
@@ -214,7 +232,7 @@ class ChargeStationService {
         try {
             const response = await axios.get(`${this.apiUrl}/clients/promotion/price?id=${stationID}`);
             if (response.status === 200 || response.status === 201) {
-                return response.data.price;
+                return response.data.originalPrice;
             } else {
                 console.log('invalid response');
             }