Browse Source

before prebuild

Ian Fung 1 year ago
parent
commit
c364b155c4

+ 21 - 2
app.json

@@ -15,7 +15,11 @@
         "assetBundlePatterns": ["**/*"],
         "ios": {
             "supportsTablet": false,
-            "bundleIdentifier": "hk.com.crazycharge"
+            "bundleIdentifier": "hk.com.crazycharge",
+            "infoPlist": {
+                "LSApplicationQueriesSchemes": ["whatsapp"],
+                "UIBackgroundModes": ["remote-notification"]
+            }
         },
         "android": {
             "adaptiveIcon": {
@@ -23,7 +27,12 @@
                 "backgroundColor": "#000000"
             },
             "icon": "./assets/CC_Logo.png",
-            "permissions": ["android.permission.CAMERA", "android.permission.RECORD_AUDIO"],
+            "permissions": [
+                "android.permission.CAMERA",
+                "android.permission.RECORD_AUDIO",
+                "android.permission.ACCESS_COARSE_LOCATION",
+                "android.permission.ACCESS_FINE_LOCATION"
+            ],
             "package": "hk.com.crazycharge",
             "versionCode": 21
         },
@@ -57,6 +66,16 @@
             "eas": {
                 "projectId": "536e7cff-9f65-4894-ad9f-e4ef8de57e4e"
             }
+        },
+        "runtimeVersion": {
+            "policy": "appVersion"
+        },
+        "updates": {
+            "url": "https://u.expo.dev/536e7cff-9f65-4894-ad9f-e4ef8de57e4e",
+            "enabled": true,
+            "checkAutomatically": "ON_LOAD",
+            "fallbackToCacheTimeout": 0,
+            "channel": "production"
         }
     }
 }

+ 5 - 2
app/(auth)/(tabs)/(home)/scanQrPage.tsx

@@ -1015,7 +1015,8 @@ const ScanQrPage = () => {
                     <View style={styles.overlay}>
                         <View style={styles.topOverlay}>
                             <Pressable
-                                className="absolute top-20 left-10 z-10 "
+                                className="absolute top-20 left-10 z-10 p-4"
+                                hitSlop={{ top: 20, bottom: 20, left: 20, right: 20 }} // Added hitSlop
                                 onPress={() => {
                                     if (router.canGoBack()) {
                                         router.back();
@@ -1024,7 +1025,9 @@ const ScanQrPage = () => {
                                     }
                                 }}
                             >
-                                <CrossLogoWhiteSvg />
+                                <View style={{ transform: [{ scale: 1.5 }] }}>
+                                    <CrossLogoWhiteSvg />
+                                </View>
                             </Pressable>
                         </View>
                         <View style={styles.centerRow}>

+ 5 - 3
app/_layout.tsx

@@ -5,14 +5,16 @@ import { GestureHandlerRootView } from 'react-native-gesture-handler';
 import { useEffect, useState } from 'react';
 import { checkVersion } from '../component/checkVersion';
 import { authenticationService } from '../service/authService';
-// import { usePushNotifications } from './hooks/usePushNotifications';
+import { usePushNotifications } from './hooks/usePushNotifications';
 
 export default function RootLayout() {
     const [isLoading, setIsLoading] = useState(true);
     const { user } = useAuth();
-    // const { expoPushToken, notification } = usePushNotifications();
+    const { expoPushToken, notification } = usePushNotifications();
 
-    // const data = JSON.stringify(notification, undefined, 2);
+    console.log('notification', notification);
+    console.log('expoPushToken', expoPushToken);
+    const data = JSON.stringify(notification, undefined, 2);
 
     useEffect(() => {
         const fetchVersion = async () => {

+ 76 - 76
app/hooks/usePushNotifications.ts

@@ -1,88 +1,88 @@
-// import { useEffect, useState, useRef } from 'react';
-// import * as Notifications from 'expo-notifications';
-// import * as Device from 'expo-device';
-// import Constants from 'expo-constants';
-// import { Platform } from 'react-native';
+import { useEffect, useState, useRef } from 'react';
+import * as Notifications from 'expo-notifications';
+import * as Device from 'expo-device';
+import Constants from 'expo-constants';
+import { Platform } from 'react-native';
 
-// export interface PushNotificationState {
-//     notification?: Notifications.Notification;
-//     expoPushToken?: Notifications.ExpoPushToken;
-// }
+export interface PushNotificationState {
+    notification?: Notifications.Notification;
+    expoPushToken?: Notifications.ExpoPushToken;
+}
 
-// export const usePushNotifications = (): PushNotificationState => {
-//     const [expoPushToken, setExpoPushToken] = useState<Notifications.ExpoPushToken | undefined>();
-//     const [notification, setNotification] = useState<Notifications.Notification | undefined>();
+export const usePushNotifications = (): PushNotificationState => {
+    const [expoPushToken, setExpoPushToken] = useState<Notifications.ExpoPushToken | undefined>();
+    const [notification, setNotification] = useState<Notifications.Notification | undefined>();
 
-//     const notificationListener = useRef<Notifications.Subscription>();
-//     const responseListener = useRef<Notifications.Subscription>();
+    const notificationListener = useRef<Notifications.Subscription>();
+    const responseListener = useRef<Notifications.Subscription>();
 
-//     useEffect(() => {
-//         Notifications.setNotificationHandler({
-//             handleNotification: async () => ({
-//                 shouldPlaySound: true,
-//                 shouldShowAlert: true,
-//                 shouldSetBadge: true,
-//                 icon: './assets/images/cc.png',
-//                 vibrate: true,
-//                 shouldShowWhenInForeground: true
-//             })
-//         });
+    useEffect(() => {
+        Notifications.setNotificationHandler({
+            handleNotification: async () => ({
+                shouldPlaySound: true,
+                shouldShowAlert: true,
+                shouldSetBadge: true,
+                icon: './assets/images/cc.png',
+                vibrate: true,
+                shouldShowWhenInForeground: true
+            })
+        });
 
-//         registerForPushNotificationAsync().then((token) => {
-//             setExpoPushToken(token);
-//         });
+        registerForPushNotificationAsync().then((token) => {
+            setExpoPushToken(token);
+        });
 
-//         notificationListener.current = Notifications.addNotificationReceivedListener((notification) => {
-//             setNotification(notification);
-//         });
+        notificationListener.current = Notifications.addNotificationReceivedListener((notification) => {
+            setNotification(notification);
+        });
 
-//         responseListener.current = Notifications.addNotificationResponseReceivedListener((response) => {
-//             console.log(response);
-//         });
+        responseListener.current = Notifications.addNotificationResponseReceivedListener((response) => {
+            console.log(response);
+        });
 
-//         return () => {
-//             Notifications.removeNotificationSubscription(notificationListener.current!);
-//             Notifications.removeNotificationSubscription(responseListener.current!);
-//         };
-//     }, []);
+        return () => {
+            Notifications.removeNotificationSubscription(notificationListener.current!);
+            Notifications.removeNotificationSubscription(responseListener.current!);
+        };
+    }, []);
 
-//     return {
-//         expoPushToken,
-//         notification
-//     };
-// };
+    return {
+        expoPushToken,
+        notification
+    };
+};
 
-// async function registerForPushNotificationAsync() {
-//     let token;
+async function registerForPushNotificationAsync() {
+    let token;
 
-//     // Check if the device is a physical device, because this only works for physical devices not simulators
-//     if (Device.isDevice) {
-//         const { status: existingStatus } = await Notifications.getPermissionsAsync();
-//         let finalStatus = existingStatus;
-//         if (existingStatus !== 'granted') {
-//             const { status } = await Notifications.requestPermissionsAsync();
-//             finalStatus = status;
-//         }
-//         if (finalStatus !== 'granted') {
-//             alert('Failed to get push token for push notification!');
-//             return;
-//         }
-//         //if we have permission, then get the token
-//         token = await Notifications.getExpoPushTokenAsync({
-//             projectId: Constants.expoConfig?.extra?.eas?.projectId
-//         });
+    // Check if the device is a physical device, because this only works for physical devices not simulators
+    if (Device.isDevice) {
+        const { status: existingStatus } = await Notifications.getPermissionsAsync();
+        let finalStatus = existingStatus;
+        if (existingStatus !== 'granted') {
+            const { status } = await Notifications.requestPermissionsAsync();
+            finalStatus = status;
+        }
+        if (finalStatus !== 'granted') {
+            alert('Failed to get push token for push notification!');
+            return;
+        }
+        //if we have permission, then get the token
+        token = await Notifications.getExpoPushTokenAsync({
+            projectId: Constants.expoConfig?.extra?.eas?.projectId
+        });
 
-//         if (Platform.OS === 'android') {
-//             Notifications.setNotificationChannelAsync('default', {
-//                 name: 'default',
-//                 importance: Notifications.AndroidImportance.MAX,
-//                 vibrationPattern: [0, 250, 250, 250],
-//                 lightColor: '#FF231F7C'
-//             });
-//         }
-//         console.log('token', token);
-//         return token;
-//     } else {
-//         console.log('Must use physical device for Push Notifications');
-//     }
-// }
+        if (Platform.OS === 'android') {
+            Notifications.setNotificationChannelAsync('default', {
+                name: 'default',
+                importance: Notifications.AndroidImportance.MAX,
+                vibrationPattern: [0, 250, 250, 250],
+                lightColor: '#FF231F7C'
+            });
+        }
+        console.log('token', token);
+        return token;
+    } else {
+        console.log('Must use physical device for Push Notifications');
+    }
+}

BIN
assets/uber20008.png


BIN
assets/uber2008.png


+ 0 - 23
component/accountPages/abc.html

@@ -1,23 +0,0 @@
-[{"car_brand": {"createdAt": "2024-06-27T17:19:04.518Z", "id": "1834d087-bfc1-4f90-8f09-805e3d9422b5", "img_url":
-"tesla-logo.png", "name": "Tesla", "updatedAt": "2024-06-27T17:19:04.518Z"}, "car_type": {"capacitance": 75,
-"capacitance_unit": "kwh", "createdAt": "2024-06-28T17:11:08.949Z", "id": "e6c4b6ca-1170-4ca3-9360-cb42a859cb84",
-"name": "Tesla Model Y", "type_image_url": "tesla-model-Y.png", "updatedAt": "2024-06-28T17:11:08.949Z"}, "createdAt":
-"2024-08-09T07:58:25.126Z", "id": "d8f41a6a-29b6-4d9b-a274-f036eb8e647f", "license_plate": "554554", "updatedAt":
-"2024-08-09T07:58:25.126Z"}, {"car_brand": {"createdAt": "2024-06-27T17:19:04.518Z", "id":
-"1834d087-bfc1-4f90-8f09-805e3d9422b5", "img_url": "tesla-logo.png", "name": "Tesla", "updatedAt":
-"2024-06-27T17:19:04.518Z"}, "car_type": {"capacitance": 75, "capacitance_unit": "kwh", "createdAt":
-"2024-06-28T17:11:08.949Z", "id": "e6c4b6ca-1170-4ca3-9360-cb42a859cb84", "name": "Tesla Model Y", "type_image_url":
-"tesla-model-Y.png", "updatedAt": "2024-06-28T17:11:08.949Z"}, "createdAt": "2024-08-15T04:38:46.682Z", "id":
-"a8b5e3b6-62c8-461c-837c-ecabf44440e3", "license_plate": "987654", "updatedAt": "2024-08-15T04:38:46.682Z"},
-{"car_brand": {"createdAt": "2024-06-27T17:19:04.518Z", "id": "1834d087-bfc1-4f90-8f09-805e3d9422b5", "img_url":
-"tesla-logo.png", "name": "Tesla", "updatedAt": "2024-06-27T17:19:04.518Z"}, "car_type": {"capacitance": 75,
-"capacitance_unit": "kwh", "createdAt": "2024-06-28T17:11:08.949Z", "id": "e6c4b6ca-1170-4ca3-9360-cb42a859cb84",
-"name": "Tesla Model Y", "type_image_url": "tesla-model-Y.png", "updatedAt": "2024-06-28T17:11:08.949Z"}, "createdAt":
-"2024-08-15T04:38:59.798Z", "id": "b088d7f5-c761-4bfa-a30e-2fa8eab7292a", "license_plate": "123456", "updatedAt":
-"2024-08-15T04:38:59.798Z"}, {"car_brand": {"createdAt": "2024-06-27T17:19:04.518Z", "id":
-"1834d087-bfc1-4f90-8f09-805e3d9422b5", "img_url": "tesla-logo.png", "name": "Tesla", "updatedAt":
-"2024-06-27T17:19:04.518Z"}, "car_type": {"capacitance": 75, "capacitance_unit": "kwh", "createdAt":
-"2024-06-28T17:11:08.949Z", "id": "e6c4b6ca-1170-4ca3-9360-cb42a859cb84", "name": "Tesla Model Y", "type_image_url":
-"tesla-model-Y.png", "updatedAt": "2024-06-28T17:11:08.949Z"}, "createdAt": "2024-08-17T04:13:16.682Z", "id":
-"a42ee0d3-3d63-449a-b415-89b645131de7", "license_plate": "12", "updatedAt": "2024-08-17T04:13:16.682Z"}]
-LOG --------------------------------------------------

+ 2 - 2
component/accountPages/accountMainPageComponent.tsx

@@ -53,14 +53,14 @@ const AccountMainPageComponent = () => {
                         onPress={() => router.push('/activityRecordPage')}
                     >
                         <ActivitySvg />
-                        <Text>活動記錄</Text>
+                        <Text>充電記錄</Text>
                     </Pressable>
                 </View>
                 <View className="my-4  ">
                     <Pressable onPress={() => router.push('uberVerificationPage')}>
                         <Image
                             resizeMode="contain"
-                            source={require('../../assets/uber208.png')}
+                            source={require('../../assets/uber20008.png')}
                             style={{ width: imageWidth, height: imageWidth * aspectRatio, alignSelf: 'center' }}
                         />
                     </Pressable>

+ 1 - 1
component/accountPages/activityRecordPageComponent.tsx

@@ -25,7 +25,7 @@ const ActivityRecordPageComponent = () => {
                     >
                         <CrossLogoSvg />
                     </Pressable>
-                    <Text style={{ fontSize: 45, marginVertical: 25 }}>活動記錄</Text>
+                    <Text style={{ fontSize: 45, marginVertical: 25 }}>充電記錄</Text>
                 </View>
                 <View className="flex-1 ">
                     <BookingTabViewComponent titles={['已預約', '已完成']} />

+ 24 - 12
component/accountPages/paymentRecordPageComponent.tsx

@@ -13,16 +13,23 @@ interface TransactionRecordItem {
     description: string;
     amount: number;
     actual_total_power: string | number;
+    current_price?: number;
 }
-const TransactionRow: React.FC<TransactionRecordItem> = ({ date, description, amount, actual_total_power }) => (
+const TransactionRow: React.FC<TransactionRecordItem> = ({
+    date,
+    description,
+    amount,
+    actual_total_power,
+    current_price
+}) => (
     <View className="flex flex-row w-full py-4 border-b border-[#CCCCCC]">
-        <Text className="flex-[0.25] text-sm">{date}</Text>
-        <Text className="flex-[0.25] text-sm">{description}</Text>
-        <Text className="flex-[0.25] text-sm text-right">
+        <Text className="flex-[0.2] text-sm">{date}</Text>
+        <Text className="flex-[0.2] text-center text-sm">{description}</Text>
+        <Text className="flex-[0.2] text-sm text-right ">
             {actual_total_power !== '-' ? Number(actual_total_power).toFixed(1) : actual_total_power}
         </Text>
-        {/* <Text className="flex-[0.25] text-sm text-right">${Number.isInteger(amount) ? amount : amount.toFixed(1)}</Text> */}
-        <Text className="flex-[0.25] text-sm text-right">${amount}</Text>
+        <Text className="flex-[0.2] text-sm text-right">{current_price ? `$${current_price}` : '-'}</Text>
+        <Text className="flex-[0.2] text-sm text-right">${amount}</Text>
     </View>
 );
 const PaymentRecordPageComponent = () => {
@@ -122,9 +129,10 @@ const PaymentRecordPageComponent = () => {
                         return {
                             date: convertToHKTime(item.createdAt).hkDate,
                             description: description,
-                            // amount: item.amount,
                             amount:
                                 item.type === 'qfpay'
+                                    ? item.amount
+                                    : item.goods_name === 'Penalty'
                                     ? item.amount
                                     : item.current_price && item.actual_total_power
                                     ? (item.current_price * item.actual_total_power).toFixed(1)
@@ -135,7 +143,9 @@ const PaymentRecordPageComponent = () => {
                                 item.actual_total_power !== '' &&
                                 !isNaN(Number(item.actual_total_power))
                                     ? item.actual_total_power
-                                    : '-'
+                                    : '-',
+                            current_price:
+                                item.type === 'qfpay' || item.goods_name === 'Penalty' ? '-' : item.current_price
                         };
                     });
                 setTransactionRecord(formattedData.slice(0, 10));
@@ -189,10 +199,11 @@ const PaymentRecordPageComponent = () => {
                     </ImageBackground>
                 </View>
                 <View className="flex flex-row w-full py-2 border-b border-[#CCCCCC]">
-                    <Text className="flex-[0.25] text-sm text-[#888888]">日期</Text>
-                    <Text className="flex-[0.25] text-sm text-[#888888]">內容</Text>
-                    <Text className="flex-[0.25] text-sm text-right text-[#888888]">實際充電量</Text>
-                    <Text className="flex-[0.25] text-sm text-right text-[#888888]">金額</Text>
+                    <Text className="flex-[0.2] text-sm text-[#888888]">日期</Text>
+                    <Text className="flex-[0.2] text-sm text-center text-[#888888]">內容</Text>
+                    <Text className="flex-[0.2] text-sm text-right text-[#888888]">實際充電量</Text>
+                    <Text className="flex-[0.2] text-sm text-right text-[#888888]">電價</Text>
+                    <Text className="flex-[0.2] text-sm text-right text-[#888888]">金額</Text>
                 </View>
                 <View className="border-t  border-[#CCCCCC]" />
 
@@ -204,6 +215,7 @@ const PaymentRecordPageComponent = () => {
                             description={item.description}
                             amount={item.amount}
                             actual_total_power={item.actual_total_power}
+                            current_price={item.current_price}
                         />
                     )}
                     estimatedItemSize={10}

+ 23 - 6
component/accountPages/walletPageComponent.tsx

@@ -528,7 +528,12 @@ import { reloadAppAsync } from 'expo';
 import sha256 from 'crypto-js/sha256';
 
 const AmountInputModal = ({ visible, onClose, onConfirm }) => {
-    const amounts = [200, 500, 1000, 2000];
+    const amounts = [
+        { amount: 200, percentage: 0 },
+        { amount: 500, percentage: 8 },
+        { amount: 1000, percentage: 13 },
+        { amount: 2000, percentage: 18 }
+    ];
 
     const getFontSize = () => {
         const { width } = Dimensions.get('window');
@@ -567,8 +572,8 @@ const AmountInputModal = ({ visible, onClose, onConfirm }) => {
                     >
                         {amounts.map((amount) => (
                             <Pressable
-                                key={amount}
-                                onPress={() => onConfirm(amount)}
+                                key={amount.amount}
+                                onPress={() => onConfirm(amount.amount)}
                                 style={{
                                     backgroundColor: '#02677D',
                                     padding: 10,
@@ -578,10 +583,14 @@ const AmountInputModal = ({ visible, onClose, onConfirm }) => {
                                     marginBottom: 10
                                 }}
                             >
-                                <Text style={{ color: 'white', fontSize: getFontSize() }}>${amount}</Text>
+                                <Text style={{ color: 'white', fontSize: getFontSize() }}>
+                                    ${amount.amount}
+                                    {amount.percentage > 0 ? ` (+${amount.percentage}%) ` : ''}
+                                </Text>
                             </Pressable>
                         ))}
                     </View>
+                    <Text>*括號為回贈比例</Text>
                     <Pressable onPress={onClose} style={{ padding: 10, alignItems: 'center', marginTop: 10 }}>
                         <Text style={{ color: 'red' }}>取消</Text>
                     </Pressable>
@@ -757,8 +766,16 @@ const WalletPageComponent = () => {
         if (typeof amount !== 'number') {
             amount = Number(amount);
         }
-        console.log('amount in formatMoney', amount);
-        return amount.toLocaleString('en-US');
+
+        // Check if the number is a whole number
+        if (Number.isInteger(amount)) {
+            return amount.toLocaleString('en-US');
+        }
+
+        // For decimal numbers, show one decimal place
+        return Number(amount)
+            .toFixed(1)
+            .replace(/\B(?=(\d{3})+(?!\d))/g, ',');
     };
 
     const filterPaymentOptions = (options, allowedKeys) => {

+ 33 - 0
component/bindingMultiStepForm/bindingMultiStepForm.tsx

@@ -0,0 +1,33 @@
+import { Text, View, StyleSheet } from 'react-native';
+import { StatusBar } from 'expo-status-bar';
+import { useEffect, useState } from 'react';
+import BindingPhoneNumberForm from './formComponent/form';
+
+const BindingMultiStepForm: React.FC = () => {
+    const [bindingFormData, setBindingFormData] = useState({
+        email: '',
+        password: '',
+        phone: '',
+        otp: ''
+    });
+
+    // useEffect(() => {
+    //     console.log(forgetPasswordFormData);
+    // }, [forgetPasswordFormData]);
+
+    return (
+        <View style={styles.container}>
+            <BindingPhoneNumberForm bindingFormData={bindingFormData} setBindingFormData={setBindingFormData} />
+            <StatusBar style="auto" />
+        </View>
+    );
+};
+
+const styles = StyleSheet.create({
+    container: {
+        flex: 1,
+        backgroundColor: '#FFFFFF'
+    }
+});
+
+export default BindingMultiStepForm;

+ 101 - 0
component/bindingMultiStepForm/formComponent/form.tsx

@@ -0,0 +1,101 @@
+import { useState } from 'react';
+import { View, Text, StyleSheet, TouchableWithoutFeedback, Keyboard, Pressable } from 'react-native';
+import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
+import PaginationIndicator from '../../global/PaginationIndicator';
+import { router } from 'expo-router';
+import BindingPhoneNumberPage from './formPages/bindingPhoneNumberPage';
+import BindingPhoneNumberPageStepTwo from './formPages/bindingPhoneNumberPageStepTwo';
+
+const BindingPhoneNumberForm = ({ bindingFormData, setBindingFormData }: any) => {
+    const [screen, setScreen] = useState<number>(0);
+    const FormTitle = ['舊用戶手機號碼綁定 (1/2)', '舊用戶手機號碼綁定 (2/2)'];
+    // const goToPreviousPage = () => {
+    //         router.back();
+    // };
+
+    const goToPreviousPage = () => {
+        if (screen == 1) {
+            setScreen((prevState) => prevState - 1);
+        } else {
+            router.replace('/(public)/login');
+        }
+    };
+
+    const ScreenDisplay = () => {
+        switch (screen) {
+            case 0:
+                return (
+                    <BindingPhoneNumberPage
+                        bindingFormData={bindingFormData}
+                        setBindingFormData={setBindingFormData}
+                        setScreen={setScreen}
+                    />
+                );
+            case 1:
+                return <BindingPhoneNumberPageStepTwo />;
+
+            default:
+                return <></>;
+        }
+    };
+    return (
+        <>
+            <KeyboardAwareScrollView
+                enableOnAndroid={true}
+                resetScrollToCoords={{ x: 0, y: 0 }}
+                style={{
+                    height: '100%'
+                }}
+            >
+                {screen !== 2 && (
+                    <View style={styles.topContainer}>
+                        <Text style={styles.text}>{FormTitle[screen]}</Text>
+                        <View style={styles.breakline} />
+                        <View className="w-full flex-row justify-between items-center">
+                            <Pressable onPress={goToPreviousPage} className="flex-1">
+                                <Text
+                                    style={{
+                                        color: '#888888',
+                                        fontSize: 18,
+                                        paddingLeft: 25
+                                    }}
+                                >{`<  上一步`}</Text>
+                            </Pressable>
+                            <PaginationIndicator totalPages={2} currentPage={0} />
+                            <View className="flex-1" />
+                        </View>
+                    </View>
+                )}
+                <View style={styles.bottomContainer}>{ScreenDisplay()}</View>
+            </KeyboardAwareScrollView>
+        </>
+    );
+};
+
+const styles = StyleSheet.create({
+    topContainer: {
+        flex: 1,
+        alignItems: 'center',
+        justifyContent: 'center',
+        paddingBottom: '25%',
+        paddingTop: '15%'
+    },
+    previouspageAndPaginationWrapper: {
+        display: 'flex',
+        width: '100%',
+        flexDirection: 'row',
+        justifyContent: 'space-between',
+        alignItems: 'center',
+        paddingHorizontal: 25
+    },
+    bottomContainer: { flex: 1 },
+    breakline: {
+        width: 24,
+        height: 1,
+        backgroundColor: '#000000',
+        marginVertical: 17
+    },
+    text: { fontSize: 24, fontWeight: '300' }
+});
+
+export default BindingPhoneNumberForm;

+ 188 - 0
component/bindingMultiStepForm/formComponent/formPages/bindingPhoneNumberPage.tsx

@@ -0,0 +1,188 @@
+import { View, Text, StyleSheet, Pressable } from 'react-native';
+import { useState } from 'react';
+
+import NumberInput from '../../../global/number_input';
+import NormalButton from '../../../global/normal_button';
+import NormalInput from '../../../global/normal_input';
+import { authenticationService } from '../../../../service/authService';
+
+const BindingPhoneNumberPage = ({ bindingFormData, setBindingFormData, setScreen }: any) => {
+    const [authError, setAuthError] = useState('');
+    const [error, setError] = useState('');
+    const [canSendOtp, setCanSendOtp] = useState(true);
+    const [lockEmailInput, setLockEmailInput] = useState(false);
+    const [data, setData] = useState('');
+    const [isLoading, setIsLoading] = useState(false);
+    const [isLoading2, setIsLoading2] = useState(false);
+
+    // const handleVerification = async () => {
+    //     setIsLoading(true);
+    //     if (!forgetPasswordFormData.otp && !forgetPasswordFormData.email) {
+    //         setAuthError('請確保所有資料都已填寫');
+    //     } else if (!forgetPasswordFormData.otp) {
+    //         setAuthError('請輸入OTP驗證碼');
+    //     } else {
+    //         setAuthError('');
+    //         try {
+    //             const result = await authenticationService.verifyingOtpForgetPassword(
+    //                 forgetPasswordFormData.email.toLowerCase(),
+    //                 forgetPasswordFormData.otp,
+    //                 setForgetPasswordFormData,
+    //                 setData
+    //             );
+    //             if (result === false) {
+    //                 setAuthError('驗證OTP時發生錯誤,請稍後再試');
+    //             }
+    //         } catch (error) {
+    //             console.error('Error verifying OTP:', error);
+    //             setAuthError('驗證OTP時發生錯誤,請稍後再試');
+    //         }
+    //     }
+    //     setIsLoading(false);
+    // };
+
+    // const handleFormDataChange: HandleForgetPasswordFormDataChange = (field, value) => {
+    //     setForgetPasswordFormData((prevFormData) => ({
+    //         ...prevFormData,
+    //         [field]: value
+    //     }));
+    // };
+
+    // const handleSubmitOtp = () => {
+    //     if (forgetPasswordFormData.email) {
+    //         if (canSendOtp) {
+    //             const lowerCaseEmail = forgetPasswordFormData.email.toLowerCase();
+    //             setCanSendOtp(false);
+    //             setLockEmailInput(true);
+    //             authenticationService.sendForgetPasswordOtp(lowerCaseEmail);
+    //             //can only request otp every 60 seconds
+    //             setTimeout(() => {
+    //                 setCanSendOtp(true);
+    //                 setLockEmailInput(false);
+    //             }, 60000);
+    //             setAuthError('');
+    //         } else {
+    //             setAuthError('請等待一分鐘後再重新發送。');
+    //         }
+    //     } else {
+    //         setAuthError('請確保所有資料都已填寫。');
+    //     }
+    // };
+
+    // const handleFinishResetPassword = async () => {
+    //     if (forgetPasswordFormData.newPassword !== forgetPasswordFormData.confirmedNewPassword) {
+    //         setError('請確保新密碼和確認密碼相同');
+    //     } else {
+    //         setError('');
+    //         setIsLoading2(true);
+    //         try {
+    //             const success = await authenticationService.changePassword(
+    //                 forgetPasswordFormData.confirmedNewPassword,
+    //                 data
+    //             );
+    //             if (success) {
+    //                 setScreen(1);
+    //             } else {
+    //                 setError('密碼重置失敗,請稍後再試');
+    //             }
+    //         } catch (error) {
+    //             console.error('Error changing password:', error);
+    //             setError('發生錯誤,請稍後再試');
+    //         } finally {
+    //             setIsLoading2(false);
+    //         }
+    //     }
+    // };
+
+    const handleVerification = () => {
+        console.log('handleVerification');
+        setScreen(1);
+    };
+    return (
+        <>
+            <View style={styles.container}>
+                <View style={styles.bottomContainer}>
+                    <Text style={styles.text}>請輸入現有電子郵件</Text>
+                    <View className="pb-2">
+                        <NormalInput
+                            placeholder="請填寫您的電子郵件"
+                            onChangeText={(email) =>
+                                setBindingFormData({
+                                    ...bindingFormData,
+                                    email: email
+                                })
+                            }
+                        />
+                    </View>
+                    <View className="pb-2">
+                        <NormalInput
+                            placeholder="請填寫您的密碼"
+                            onChangeText={(password) =>
+                                setBindingFormData({
+                                    ...bindingFormData,
+                                    password: password
+                                })
+                            }
+                        />
+                    </View>
+
+                    <NormalButton
+                        title={<Text style={{ color: '#fff' }}>{isLoading ? '處理中...' : '下一步'}</Text>}
+                        onPress={handleVerification}
+                        // extendedStyle={
+                        //     forgetPasswordFormData.otpAuthCompleted == true ? { backgroundColor: '#70787C' } : {}
+                        // }
+                    />
+
+                    {error && <Text style={styles.errorMessage}>{error}</Text>}
+                </View>
+            </View>
+        </>
+    );
+};
+
+const styles = StyleSheet.create({
+    container: {
+        flex: 1,
+        marginHorizontal: 20
+    },
+
+    titleText: {
+        fontSize: 24,
+        fontWeight: '300'
+    },
+    bottomContainer: {
+        flex: 3,
+        paddingBottom: 100
+    },
+    breakline: {
+        width: 24,
+        height: 1,
+        backgroundColor: '#000000',
+        marginVertical: 17
+    },
+    text: {
+        fontSize: 18,
+        paddingBottom: 10
+    },
+    hiddenPasswordFields: {
+        gap: 10,
+        paddingTop: 10
+    },
+    opacityZero: {
+        opacity: 0
+    },
+    opacityFull: {
+        opacity: 1
+    },
+    errorMessage: {
+        fontSize: 14,
+        color: '#ff0033',
+        fontWeight: '400',
+        marginLeft: 10,
+        marginTop: 10
+    },
+    footer: { color: '#02677D', fontSize: 16, paddingVertical: 10 }
+});
+
+export default BindingPhoneNumberPage;

+ 273 - 0
component/bindingMultiStepForm/formComponent/formPages/bindingPhoneNumberPageStepTwo.tsx

@@ -0,0 +1,273 @@
+import { View, Text, StyleSheet, Pressable } from 'react-native';
+import { useState } from 'react';
+import { forgetPasswordFormData, HandleForgetPasswordFormDataChange } from '../../../../types/signup';
+import NumberInput from '../../../global/number_input';
+import NormalButton from '../../../global/normal_button';
+import NormalInput from '../../../global/normal_input';
+import { authenticationService } from '../../../../service/authService';
+import PhoneInput from '../../../global/phone_input';
+
+const BindingPhoneNumberPageStepTwo = ({ bindingFormData, setBindingFormData, setScreen }: any) => {
+    const [authError, setAuthError] = useState('');
+    const [error, setError] = useState('');
+    const [canSendOtp, setCanSendOtp] = useState(true);
+    const [lockEmailInput, setLockEmailInput] = useState(false);
+    const [data, setData] = useState('');
+    const [isLoading, setIsLoading] = useState(false);
+    const [isLoading2, setIsLoading2] = useState(false);
+
+    // const handleVerification = async () => {
+    //     setIsLoading(true);
+    //     if (!forgetPasswordFormData.otp && !forgetPasswordFormData.email) {
+    //         setAuthError('請確保所有資料都已填寫');
+    //     } else if (!forgetPasswordFormData.otp) {
+    //         setAuthError('請輸入OTP驗證碼');
+    //     } else {
+    //         setAuthError('');
+    //         try {
+    //             const result =
+    //                 await authenticationService.verifyingOtpForgetPassword(
+    //                     forgetPasswordFormData.email.toLowerCase(),
+    //                     forgetPasswordFormData.otp,
+    //                     setForgetPasswordFormData,
+    //                     setData
+    //                 );
+    //             if (result === false) {
+    //                 setAuthError('驗證OTP時發生錯誤,請稍後再試');
+    //             }
+    //         } catch (error) {
+    //             console.error('Error verifying OTP:', error);
+    //             setAuthError('驗證OTP時發生錯誤,請稍後再試');
+    //         }
+    //     }
+    //     setIsLoading(false);
+    // };
+
+    // const handleFormDataChange: HandleForgetPasswordFormDataChange = (
+    //     field,
+    //     value
+    // ) => {
+    //     setForgetPasswordFormData((prevFormData) => ({
+    //         ...prevFormData,
+    //         [field]: value
+    //     }));
+    // };
+
+    // const handleSubmitOtp = () => {
+    //     if (forgetPasswordFormData.email) {
+    //         if (canSendOtp) {
+    //             const lowerCaseEmail =
+    //                 forgetPasswordFormData.email.toLowerCase();
+    //             setCanSendOtp(false);
+    //             setLockEmailInput(true);
+    //             authenticationService.sendForgetPasswordOtp(lowerCaseEmail);
+    //             //can only request otp every 60 seconds
+    //             setTimeout(() => {
+    //                 setCanSendOtp(true);
+    //                 setLockEmailInput(false);
+    //             }, 60000);
+    //             setAuthError('');
+    //         } else {
+    //             setAuthError('請等待一分鐘後再重新發送。');
+    //         }
+    //     } else {
+    //         setAuthError('請確保所有資料都已填寫。');
+    //     }
+    // };
+
+    // const handleFinishResetPassword = async () => {
+    //     if (
+    //         forgetPasswordFormData.newPassword !==
+    //         forgetPasswordFormData.confirmedNewPassword
+    //     ) {
+    //         setError('請確保新密碼和確認密碼相同');
+    //     } else {
+    //         setError('');
+    //         setIsLoading2(true);
+    //         try {
+    //             const success = await authenticationService.changePassword(
+    //                 forgetPasswordFormData.confirmedNewPassword,
+    //                 data
+    //             );
+    //             if (success) {
+    //                 setScreen(1);
+    //             } else {
+    //                 setError('密碼重置失敗,請稍後再試');
+    //             }
+    //         } catch (error) {
+    //             console.error('Error changing password:', error);
+    //             setError('發生錯誤,請稍後再試');
+    //         } finally {
+    //             setIsLoading2(false);
+    //         }
+    //     }
+    // };
+
+    return (
+        <>
+            <View style={styles.container}>
+                <View style={styles.bottomContainer}>
+                    <Text style={styles.text}>請輸入手機號碼,此手機號碼將用作登入用途</Text>
+                    <PhoneInput
+                        value={bindingFormData?.phoneNumber || ''}
+                        onChangeText={(phoneNumber) => {
+                            setBindingFormData({
+                                ...bindingFormData,
+                                phoneNumber: phoneNumber
+                            });
+                        }}
+                        placeholder="請輸入手機號碼"
+                    />
+                    {/* <NormalInput
+                        placeholder="請輸入手機號碼"
+                        onChangeText={(phoneNumber) =>
+                            setBindingFormData({
+                                ...bindingFormData,
+                                phoneNumber: phoneNumber
+                            })
+                        }
+                        // editable={!lockEmailInput}
+                        // extendedStyle={{ opacity: !lockEmailInput ? 1 : 0.5 }}
+                    /> */}
+                    <View
+                        style={{
+                            display: 'flex',
+                            flexDirection: 'row',
+                            paddingVertical: 10,
+                            gap: 10
+                        }}
+                    >
+                        <NumberInput
+                            placeholder="OTP驗證碼"
+                            onChangeText={(t) => setBindingFormData({ ...bindingFormData, otp: t })}
+                            // editable={!forgetPasswordFormData.otpAuthCompleted}
+                            extendedStyle={{
+                                flex: 1
+                                // opacity: !forgetPasswordFormData.otpAuthCompleted ? 1 : 0.5
+                            }}
+                        />
+                        <NormalButton
+                            title={<Text style={{ color: '#fff' }}>{lockEmailInput ? '已發送' : '發送'}</Text>}
+                            // onPress={handleSubmitOtp}
+                            onPress={() => {
+                                console.log('hi');
+                            }}
+                            extendedStyle={{ flex: 1 / 2 }}
+                        />
+                    </View>
+                    <NormalButton
+                        title={
+                            <Text style={{ color: '#fff' }}>
+                                {/* {isLoading
+                                    ? '驗證中...'
+                                    : forgetPasswordFormData.otpAuthCompleted == true
+                                    ? '已驗證'
+                                    : '驗證'} */}
+                                確認
+                            </Text>
+                        }
+                        // onPress={handleVerification}
+                        onPress={() => console.log('abc')}
+                        // extendedStyle={
+                        //     forgetPasswordFormData.otpAuthCompleted == true ? { backgroundColor: '#70787C' } : {}
+                        // }
+                    />
+
+                    {/* {authError && <Text style={styles.errorMessage}>{authError}</Text>}
+                    {lockEmailInput && (
+                        <Pressable
+                            disabled={forgetPasswordFormData.otpAuthCompleted}
+                            onPress={() => setLockEmailInput(false)}
+                        >
+                            <Text
+                                style={[
+                                    styles.footer,
+                                    forgetPasswordFormData.otpAuthCompleted && {
+                                        opacity: 0.5
+                                    }
+                                ]}
+                            >
+                                修改電子郵件
+                            </Text>
+                        </Pressable>
+                    )}
+
+                    {forgetPasswordFormData.otpAuthCompleted && (
+                        <View
+                            style={[
+                                styles.hiddenPasswordFields,
+                                forgetPasswordFormData.otpAuthCompleted ? styles.opacityFull : styles.opacityZero
+                            ]}
+                        >
+                            <NormalInput
+                                placeholder="新密碼"
+                                onChangeText={(t) => handleFormDataChange('newPassword', t)}
+                                secureTextEntry={true}
+                                textContentType={'oneTimeCode'}
+                            />
+                            <NormalInput
+                                placeholder="確認密碼"
+                                onChangeText={(t) => handleFormDataChange('confirmedNewPassword', t)}
+                                secureTextEntry={true}
+                                textContentType={'oneTimeCode'}
+                            />
+                            <NormalButton
+                                title={<Text style={{ color: '#fff' }}>{isLoading ? '重置中...' : '重置'}</Text>}
+                                onPress={handleFinishResetPassword}
+                                extendedStyle={{}}
+                            />
+                        </View>
+                    )} */}
+
+                    {error && <Text style={styles.errorMessage}>{error}</Text>}
+                </View>
+            </View>
+        </>
+    );
+};
+
+const styles = StyleSheet.create({
+    container: {
+        flex: 1,
+        marginHorizontal: 20
+    },
+
+    titleText: {
+        fontSize: 24,
+        fontWeight: '300'
+    },
+    bottomContainer: {
+        flex: 3,
+        paddingBottom: 100
+    },
+    breakline: {
+        width: 24,
+        height: 1,
+        backgroundColor: '#000000',
+        marginVertical: 17
+    },
+    text: {
+        fontSize: 18,
+        paddingBottom: 10
+    },
+    hiddenPasswordFields: {
+        gap: 10,
+        paddingTop: 10
+    },
+    opacityZero: {
+        opacity: 0
+    },
+    opacityFull: {
+        opacity: 1
+    },
+    errorMessage: {
+        fontSize: 14,
+        color: '#ff0033',
+        fontWeight: '400',
+        marginLeft: 10,
+        marginTop: 10
+    },
+    footer: { color: '#02677D', fontSize: 16, paddingVertical: 10 }
+});
+
+export default BindingPhoneNumberPageStepTwo;

+ 10 - 20
component/forgetPasswordMultiStepForm/formComponent/form.tsx

@@ -1,12 +1,5 @@
 import { useState } from 'react';
-import {
-    View,
-    Text,
-    StyleSheet,
-    TouchableWithoutFeedback,
-    Keyboard,
-    Pressable
-} from 'react-native';
+import { View, Text, StyleSheet, TouchableWithoutFeedback, Keyboard, Pressable } from 'react-native';
 
 import ResetSuccessful from './formPages/resetSuccessful';
 import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
@@ -17,9 +10,7 @@ import { router } from 'expo-router';
 
 type ForgetPasswordFormProps = {
     forgetPasswordFormData: forgetPasswordFormData;
-    setForgetPasswordFormData: React.Dispatch<
-        React.SetStateAction<forgetPasswordFormData>
-    >;
+    setForgetPasswordFormData: React.Dispatch<React.SetStateAction<forgetPasswordFormData>>;
 };
 const ForgetPasswordForm: React.FC<ForgetPasswordFormProps> = ({
     forgetPasswordFormData,
@@ -27,11 +18,16 @@ const ForgetPasswordForm: React.FC<ForgetPasswordFormProps> = ({
 }) => {
     const [screen, setScreen] = useState<number>(0);
     const FormTitle = ['忘記密碼 - 電話驗證'];
+    // const goToPreviousPage = () => {
+
+    //         router.back();
+
+    // };
     const goToPreviousPage = () => {
         if (router.canGoBack()) {
             router.back();
         } else {
-            router.replace('/');
+            router.replace('/(public)/login');
         }
     };
     const ScreenDisplay = () => {
@@ -65,10 +61,7 @@ const ForgetPasswordForm: React.FC<ForgetPasswordFormProps> = ({
                         <Text style={styles.text}>{FormTitle[screen]}</Text>
                         <View style={styles.breakline} />
                         <View className="w-full flex-row justify-between items-center">
-                            <Pressable
-                                onPress={goToPreviousPage}
-                                className="flex-1"
-                            >
+                            <Pressable onPress={goToPreviousPage} className="flex-1">
                                 <Text
                                     style={{
                                         color: '#888888',
@@ -77,10 +70,7 @@ const ForgetPasswordForm: React.FC<ForgetPasswordFormProps> = ({
                                     }}
                                 >{`<  上一步`}</Text>
                             </Pressable>
-                            <PaginationIndicator
-                                totalPages={2}
-                                currentPage={0}
-                            />
+                            <PaginationIndicator totalPages={2} currentPage={0} />
                             <View className="flex-1" />
                         </View>
                     </View>

+ 12 - 2
component/global/bookingTabViewComponent.tsx

@@ -40,13 +40,22 @@ const processReservations = (reservations, allStations, isFuture): TabItem[] =>
         .slice(0, 10)
         .map((reservation) => {
             const snapshot = JSON.parse(reservation.snapshot);
+            console.log(
+                'reservationreservationreservationreservationreservationreservationreservationreservationreservationreservationreservation',
+                snapshot.current_price
+            );
             let stationInfo;
+            // console.log(
+            //     'snapshotsnapshotsnapshotsnapshotsnapshotsnapshotsnapshotsnapshotsnapshotsnapshotsnapshotsnapshotsnapshotsnapshotsnapshotsnapshotsnapshot',
+            //     snapshot
+            // );
 
             if (snapshot.stationID) {
                 stationInfo = allStations.find((station) => station.id === snapshot.stationID);
             } else if (snapshot.connector) {
                 stationInfo = findStationByConnectorId(allStations, snapshot.connector);
             }
+
             //this is book_time, not end_time
             const endDate = new Date(reservation.book_time);
             //this is actual_end_time for record
@@ -69,7 +78,8 @@ const processReservations = (reservations, allStations, isFuture): TabItem[] =>
                 actual_total_power: reservation.actual_total_power,
                 total_fee: reservation.total_fee,
                 withdraw_fee: reservation.withdraw_fee,
-                actual_fee: reservation.total_fee - reservation.withdraw_fee
+                actual_fee: reservation.total_fee - reservation.withdraw_fee,
+                current_price: snapshot.current_price
             };
             // .map((reservation) => {
             // const snapshot = JSON.parse(reservation.snapshot);
@@ -114,7 +124,7 @@ const BookingTabViewComponentInner: React.FC<BookingTabViewComponentProps> = ({
     }
 
     const tabItems = processReservations(data.reservations, data.stations, true);
-    // console.log('tabItem', tabItems);
+    console.log('tabItem', tabItems);
 
     const completedReservationTabItems = processReservations(data.reservations, data.stations, false);
     console.log('completedReservationTabItems', completedReservationTabItems);

+ 16 - 5
component/global/tabView.tsx

@@ -335,12 +335,12 @@ const TabItem = ({ item, currentLocation }: { item: TabItem; currentLocation: Lo
         >
             <View style={styles.container}>
                 <Image style={styles.image} source={item.imgURL} />
-                <View className="flex flex-col gap-2 mt-5 mr-2">
+                <View className="flex flex-col gap-2 mt-2 mr-2">
                     <Text
                         style={{
                             fontWeight: '700',
                             color: '#02677D',
-                            fontSize: 20
+                            fontSize: 16
                         }}
                     >
                         {`${item.date}日 - ${item.time}至${item.actual_end_time}`}
@@ -349,7 +349,7 @@ const TabItem = ({ item, currentLocation }: { item: TabItem; currentLocation: Lo
                         <Text
                             style={{
                                 fontWeight: '400',
-                                fontSize: 16,
+                                fontSize: 14,
                                 color: '#222222'
                             }}
                         >
@@ -363,7 +363,7 @@ const TabItem = ({ item, currentLocation }: { item: TabItem; currentLocation: Lo
                         <Text
                             style={{
                                 fontWeight: '400',
-                                fontSize: 16,
+                                fontSize: 14,
                                 color: '#222222'
                             }}
                         >
@@ -375,10 +375,21 @@ const TabItem = ({ item, currentLocation }: { item: TabItem; currentLocation: Lo
                                 : ''}
                         </Text>
                     </View>
+                    <View className="flex flex-row  space-x-2">
+                        <Text
+                            style={{
+                                fontWeight: '400',
+                                fontSize: 14,
+                                color: '#222222'
+                            }}
+                        >
+                            每度電金額: ${item.current_price}
+                        </Text>
+                    </View>
                     <Text
                         style={{
                             fontWeight: '400',
-                            fontSize: 16,
+                            fontSize: 14,
                             color: '#888888'
                         }}
                     >

+ 3 - 2
component/homePage/homePage.tsx

@@ -1,4 +1,4 @@
-import { View, Text, ScrollView, FlatList, Pressable, ActivityIndicator, Image, Modal } from 'react-native';
+import { View, Text, ScrollView, FlatList, Pressable, ActivityIndicator, Image, Modal, Alert } from 'react-native';
 import NormalButton from '../global/normal_button';
 import { SafeAreaView } from 'react-native-safe-area-context';
 import { router } from 'expo-router';
@@ -170,7 +170,8 @@ const HomePage: React.FC<HomePageProps> = () => {
                     <View className="flex-1 flex-row justify-between gap-6">
                         <View className="flex-1">
                             <NormalButton
-                                onPress={() => router.push('bookingMenuPage')}
+                                // onPress={() => router.push('bookingMenuPage')}
+                                onPress={() => Alert.alert('即將推出', '此功能即將推出,敬請期待!')}
                                 title={
                                     <View className="flex flex-row space-x-2 items-center ">
                                         <MyBookingIconSvg />

+ 17 - 1
component/registrationMultiStepForm/formComponent/form.tsx

@@ -15,6 +15,8 @@ import ForgetPasswordForm from '../../forgetPasswordMultiStepForm/forgetPassword
 import SetVehiclesOne from '../../../app/(auth)/(tabs)/(home)/(vehicle)/setVehiclesOne';
 import SetVehiclesTwo from '../../../app/(auth)/(tabs)/(home)/(vehicle)/setVehiclesTwo';
 
+import BindingMultiStepForm from '../../bindingMultiStepForm/bindingMultiStepForm';
+
 type FormProps = {};
 const Form: React.FC<FormProps> = ({}) => {
     const [screen, setScreen] = useState<number>(0);
@@ -23,7 +25,13 @@ const Form: React.FC<FormProps> = ({}) => {
     const ScreenDisplay = () => {
         switch (screen) {
             case 0:
-                return <LoginPage goToNextPage={goToNextPage} goToForgetPassWordPage={goToForgetPassWordPage} />;
+                return (
+                    <LoginPage
+                        goToNextPage={goToNextPage}
+                        goToForgetPassWordPage={goToForgetPassWordPage}
+                        goToBindingPhoneNumberPage={goToBindingPhoneNumberPage}
+                    />
+                );
             case 1:
                 return <Verification setScreen={setScreen} />;
             case 2:
@@ -61,6 +69,8 @@ const Form: React.FC<FormProps> = ({}) => {
             case 9:
                 return <ForgetPasswordForm />;
 
+            case 11:
+                return <BindingMultiStepForm />;
             default:
                 return <></>;
         }
@@ -113,6 +123,12 @@ const Form: React.FC<FormProps> = ({}) => {
             return 9;
         });
     };
+
+    const goToBindingPhoneNumberPage = () => {
+        setScreen((prevState) => {
+            return 11;
+        });
+    };
     return (
         <>
             <KeyboardAwareScrollView

+ 30 - 12
component/registrationMultiStepForm/formComponent/formPages/loginPage.tsx

@@ -1,5 +1,5 @@
 import { View, Text, Image, StyleSheet, Pressable, Alert, Dimensions, ActivityIndicator } from 'react-native';
-
+import { Ionicons } from '@expo/vector-icons';
 import Logo from '../../../global/logo';
 import NormalButton from '../../../global/normal_button';
 import NormalInput from '../../../global/normal_input';
@@ -11,17 +11,19 @@ import Checkbox from 'expo-checkbox';
 type LoginPageProps = {
     goToNextPage: () => void;
     goToForgetPassWordPage: () => void;
+    goToBindingPhoneNumberPage: () => void;
 };
 
 const screenHeight = Dimensions.get('window').height;
 
-const LoginPage: React.FC<LoginPageProps> = ({ goToNextPage, goToForgetPassWordPage }) => {
+const LoginPage: React.FC<LoginPageProps> = ({ goToNextPage, goToForgetPassWordPage, goToBindingPhoneNumberPage }) => {
     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);
+    const [showPassword, setShowPassword] = useState(false);
 
     useEffect(() => {
         loadSavedCredentials();
@@ -110,15 +112,24 @@ const LoginPage: React.FC<LoginPageProps> = ({ goToNextPage, goToForgetPassWordP
                     keyboardType="email-address"
                     autoCapitalize="none"
                 />
-                <NormalInput
-                    value={loginPassword}
-                    placeholder="密碼"
-                    onChangeText={(password) => setLoginPassword(password)}
-                    secureTextEntry={true}
-                    extendedStyle={{ borderRadius: 12, padding: 20 }}
-                    textContentType="password"
-                    autoComplete="password"
-                />
+                <View className="relative">
+                    <NormalInput
+                        value={loginPassword}
+                        placeholder="密碼"
+                        onChangeText={(password) => setLoginPassword(password)}
+                        secureTextEntry={!showPassword}
+                        extendedStyle={{ borderRadius: 12, padding: 20, paddingRight: 50 }}
+                        textContentType="password"
+                        autoComplete="password"
+                    />
+                    <Pressable
+                        className="absolute right-4 top-0 bottom-0 justify-center"
+                        onPress={() => setShowPassword(!showPassword)}
+                    >
+                        <Ionicons name={showPassword ? 'eye-outline' : 'eye-off-outline'} size={24} color="#02677D" />
+                    </Pressable>
+                </View>
+
                 <View className="flex flex-row items-center ">
                     <Checkbox
                         style={styles.checkbox}
@@ -160,13 +171,20 @@ const LoginPage: React.FC<LoginPageProps> = ({ goToNextPage, goToForgetPassWordP
                         )
                     }
                 />
-                <View className="flex flex-row justify-between">
+                <View className="flex flex-row justify-between relative">
                     <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 className="flex flex-row justify-center absolute top-16 left-0 right-0 items-center">
+                        <View className="flex-1 h-[1px] bg-[#888888] mx-2" />
+                        <Pressable onPress={goToBindingPhoneNumberPage}>
+                            <Text className="text-[#888888] text-lg">舊用戶/已有用戶/手機號碼綁定</Text>
+                        </Pressable>
+                        <View className="flex-1 h-[1px] bg-[#888888] mx-2" />
+                    </View>
                 </View>
             </View>
         </View>

+ 7 - 3
eas.json

@@ -5,12 +5,16 @@
   "build": {
     "development": {
       "developmentClient": true,
-      "distribution": "internal"
+      "distribution": "internal",
+      "channel": "development"
     },
     "preview": {
-      "distribution": "internal"
+      "distribution": "internal",
+      "channel": "preview"
     },
-    "production": {}
+    "production": {
+      "channel": "production"
+    }
   },
   "submit": {
     "production": {}

File diff suppressed because it is too large
+ 317 - 338
package-lock.json


+ 68 - 64
package.json

@@ -1,66 +1,70 @@
 {
-    "name": "template",
-    "version": "1.0.0",
-    "main": "expo-router/entry",
-    "scripts": {
-        "start": "expo start -c",
-        "android": "expo run:android",
-        "ios": "expo run:ios",
-        "web": "expo start --web"
-    },
-    "dependencies": {
-        "@gorhom/bottom-sheet": "^4.6.3",
-        "@react-native-async-storage/async-storage": "^1.23.1",
-        "@shopify/flash-list": "1.6.4",
-        "@tanstack/react-query": "^5.51.23",
-        "@types/lodash": "^4.17.7",
-        "@types/react-native-datepicker": "^1.7.6",
-        "axios": "^1.6.7",
-        "crypto-js": "^4.2.0",
-        "date-fns": "^3.6.0",
-        "date-fns-tz": "^3.1.3",
-        "dotenv": "^16.4.5",
-        "expo": "~51.0.7",
-        "expo-camera": "~15.0.13",
-        "expo-checkbox": "~3.0.0",
-        "expo-constants": "~16.0.2",
-        "expo-env": "^1.1.1",
-        "expo-file-system": "~17.0.1",
-        "expo-image-picker": "~15.0.7",
-        "expo-linking": "~6.3.1",
-        "expo-location": "~17.0.1",
-        "expo-router": "~3.5.14",
-        "expo-secure-store": "~13.0.1",
-        "expo-status-bar": "~1.12.1",
-        "lodash": "^4.17.21",
-        "nativewind": "^2.0.11",
-        "react": "18.2.0",
-        "react-native": "0.74.1",
-        "react-native-element-dropdown": "^2.12.1",
-        "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",
-        "react-native-responsive-screen": "^1.4.2",
-        "react-native-safe-area-context": "^4.10.1",
-        "react-native-screens": "3.31.1",
-        "react-native-svg": "^15.3.0",
-        "react-native-tab-view": "^3.5.2",
-        "react-native-webview": "^13.11.1",
-        "react-query": "^3.39.3",
-        "zustand": "^4.5.2"
-    },
-    "devDependencies": {
-        "@babel/core": "^7.20.0",
-        "@types/crypto-js": "^4.2.2",
-        "@types/react": "~18.2.79",
-        "@types/react-native-modern-datepicker": "^1.0.5",
-        "react-native-dotenv": "^3.4.11",
-        "tailwindcss": "^3.3.2",
-        "typescript": "~5.3.3"
-    },
-    "private": true
+  "name": "template",
+  "version": "1.0.0",
+  "main": "expo-router/entry",
+  "scripts": {
+    "start": "expo start -c",
+    "android": "expo run:android",
+    "ios": "expo run:ios",
+    "web": "expo start --web"
+  },
+  "dependencies": {
+    "@gorhom/bottom-sheet": "^4.6.3",
+    "@react-native-async-storage/async-storage": "^1.23.1",
+    "@shopify/flash-list": "1.6.4",
+    "@tanstack/react-query": "^5.51.23",
+    "@types/lodash": "^4.17.7",
+    "@types/react-native-datepicker": "^1.7.6",
+    "axios": "^1.6.7",
+    "crypto-js": "^4.2.0",
+    "date-fns": "^3.6.0",
+    "date-fns-tz": "^3.1.3",
+    "dotenv": "^16.4.5",
+    "expo": "~51.0.7",
+    "expo-camera": "~15.0.13",
+    "expo-checkbox": "~3.0.0",
+    "expo-constants": "~16.0.2",
+    "expo-env": "^1.1.1",
+    "expo-file-system": "~17.0.1",
+    "expo-image-picker": "~15.0.7",
+    "expo-linking": "~6.3.1",
+    "expo-location": "~17.0.1",
+    "expo-router": "~3.5.14",
+    "expo-secure-store": "~13.0.1",
+    "expo-status-bar": "~1.12.1",
+    "lodash": "^4.17.21",
+    "nativewind": "^2.0.11",
+    "react": "18.2.0",
+    "react-native": "0.74.1",
+    "react-native-element-dropdown": "^2.12.1",
+    "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",
+    "react-native-responsive-screen": "^1.4.2",
+    "react-native-safe-area-context": "^4.10.1",
+    "react-native-screens": "3.31.1",
+    "react-native-svg": "^15.3.0",
+    "react-native-tab-view": "^3.5.2",
+    "react-native-webview": "^13.11.1",
+    "react-query": "^3.39.3",
+    "zustand": "^4.5.2",
+    "expo-dev-client": "~4.0.29",
+    "expo-updates": "~0.25.27",
+    "expo-notifications": "~0.28.19",
+    "expo-device": "~6.0.2"
+  },
+  "devDependencies": {
+    "@babel/core": "^7.20.0",
+    "@types/crypto-js": "^4.2.2",
+    "@types/react": "~18.2.79",
+    "@types/react-native-modern-datepicker": "^1.0.5",
+    "react-native-dotenv": "^3.4.11",
+    "tailwindcss": "^3.3.2",
+    "typescript": "~5.3.3"
+  },
+  "private": true
 }

Some files were not shown because too many files changed in this diff