瀏覽代碼

before making change for scanning logic

Ian Fung 9 月之前
父節點
當前提交
e8647c6479

+ 12 - 0
app/(auth)/(tabs)/(account)/changeCarPage.tsx

@@ -0,0 +1,12 @@
+import { View, Text } from 'react-native';
+import ChangeCarPageComponent from '../../../../component/accountPages/changeCarPageComponent';
+
+const ChangeCarPage = () => {
+    return (
+        <View className='flex-1'>
+           <ChangeCarPageComponent />  
+        </View>
+    );
+};
+
+export default ChangeCarPage;

+ 105 - 0
app/(auth)/(tabs)/(account)/couponDetailPage.tsx

@@ -0,0 +1,105 @@
+import { View, Text, ScrollView, Pressable, Alert } from 'react-native';
+import React, { useContext, useEffect, useState } from 'react';
+import { SafeAreaView } from 'react-native-safe-area-context';
+import { router } from 'expo-router';
+import { CrossLogoSvg } from '../../../../component/global/SVG';
+import { AuthContext } from '../../../../context/AuthProvider';
+import NormalInput from '../../../../component/global/normal_input';
+import NormalButton from '../../../../component/global/normal_button';
+import { authenticationService } from '../../../../service/authService';
+import * as SecureStore from 'expo-secure-store';
+import { useLocalSearchParams } from 'expo-router';
+const CouponDetailPage = () => {
+    const { user, setUser } = useContext(AuthContext);
+    const [token, setToken] = useState<string | null>(null);
+    const [name, setName] = useState<string | null>(null);
+    const [isLoading, setIsLoading] = useState(false);
+    const [error, setError] = useState<string | null>(null);
+
+    const handleChangeName = async () => {
+        if (!name) {
+            setError('請輸入新的暱稱');
+            return;
+        }
+        if (!token) {
+            setError('未找到有效的登錄令牌,請重新登錄');
+            return;
+        }
+        setError(null);
+        setIsLoading(true);
+        try {
+            const success = await authenticationService.changeName(name, token);
+            if (success) {
+                if (user) {
+                    setUser({
+                        ...user,
+                        nickname: name
+                    });
+                }
+                router.replace('accountMainPage');
+            } else {
+                setError('更新暱稱失敗,請稍後再試');
+            }
+        } catch (error) {
+            console.error('Error changing name:', error);
+            setError('發生錯誤,請稍後再試');
+        } finally {
+            setIsLoading(false);
+        }
+    };
+    const { couponName, couponDescription } = useLocalSearchParams();
+    return (
+        <SafeAreaView className="flex-1 bg-white" edges={['top', 'right', 'left']}>
+            <ScrollView className="flex-1 mx-[5%]">
+                <View style={{ marginTop: 25 }}>
+                    <Pressable
+                        onPress={() => {
+                            if (router.canGoBack()) {
+                                router.back();
+                            } else {
+                                router.replace('/accountMainPage');
+                            }
+                        }}
+                    >
+                        <CrossLogoSvg />
+                    </Pressable>
+                    <Text style={{ fontSize: 45, marginVertical: 25 }}>優惠券詳情</Text>
+                    <Text className="text-2xl pb-4">{couponName}</Text>
+                    <Text className="text-lg pb-8">{couponDescription}</Text>
+                </View>
+                <NormalButton
+                    title={
+                        <Text
+                            style={{
+                                color: 'white',
+                                fontSize: 16,
+                                fontWeight: '800'
+                            }}
+                        >
+                            立即使用優惠券
+                        </Text>
+                    }
+                    onPress={() => {
+                        Alert.alert(
+                            '立即使用優惠券', // Title
+                            '按確認打開相機,掃描充電站上的二維碼以使用優惠券', // Message
+                            [
+                                {
+                                    text: '取消',
+                                    style: 'cancel'
+                                },
+                                {
+                                    text: '確認',
+                                    onPress: () => router.push('scanQrPage')
+                                }
+                            ]
+                        );
+                    }}
+                    extendedStyle={{ padding: 24 }}
+                />
+            </ScrollView>
+        </SafeAreaView>
+    );
+};
+
+export default CouponDetailPage;

+ 13 - 0
component/accountPages/accountSettingPageComponent.tsx

@@ -97,6 +97,19 @@ const AccountSettingPageComponent = () => {
                         </Pressable>
                         </Pressable>
                         <View className="h-0.5  bg-[#f4f4f4]" />
                         <View className="h-0.5  bg-[#f4f4f4]" />
                     </View>
                     </View>
+                    <View className="flex-col ">
+                        <Pressable onPress={() => router.push('changeCarPage')}>
+                            <View className="flex-row items-center justify-between">
+                                <View className="flex-col py-4">
+                                    <Text className="text-lg pb-1">車牌號碼</Text>
+
+                                    <Text style={{ color: '#555555' }}>{user?.car}</Text>
+                                </View>
+                                <RightArrowIconSvg />
+                            </View>
+                        </Pressable>
+                        <View className="h-0.5  bg-[#f4f4f4]" />
+                    </View>
                     <View className="flex-col ">
                     <View className="flex-col ">
                         {/* <Pressable onPress={() => router.push('changePhonePage')}>
                         {/* <Pressable onPress={() => router.push('changePhonePage')}>
                             <View className="flex-row items-center justify-between">
                             <View className="flex-row items-center justify-between">

+ 156 - 0
component/accountPages/changeCarPageComponent.tsx

@@ -0,0 +1,156 @@
+import { View, Text, ScrollView, Pressable, Alert } from 'react-native';
+import React, { useContext, useEffect, useState } from 'react';
+import { SafeAreaView } from 'react-native-safe-area-context';
+import { router } from 'expo-router';
+import { CrossLogoSvg } from '../global/SVG';
+import { AuthContext } from '../../context/AuthProvider';
+import NormalInput from '../global/normal_input';
+import NormalButton from '../global/normal_button';
+import { authenticationService } from '../../service/authService';
+import * as SecureStore from 'expo-secure-store';
+import { chargeStationService } from '../../service/chargeStationService';
+
+const ChangeCarPageComponent = () => {
+    const { user, setUser } = useContext(AuthContext);
+    const [token, setToken] = useState<string | null>(null);
+    const [car, setCar] = useState<string | null>(null);
+    const [isLoading, setIsLoading] = useState(false);
+    const [error, setError] = useState<string | null>(null);
+
+    useEffect(() => {
+        const getToken = async () => {
+            const storedToken = await SecureStore.getItemAsync('accessToken');
+            setToken(storedToken);
+        };
+        getToken();
+    }, []);
+
+    const validateLicensePlate = (licensePlate: string | null): string | null => {
+        if (!licensePlate || licensePlate.trim() === '') {
+            return '請輸入車牌號碼';
+        }
+
+        const trimmedPlate = licensePlate.trim();
+
+        if (trimmedPlate.length < 2) {
+            return '車牌號碼至少需要2個字符';
+        }
+
+        if (trimmedPlate.length > 10) {
+            return '車牌號碼不能超過10個字符';
+        }
+
+        // Check for special characters (allow letters, numbers, hyphen, and spaces)
+        const validFormat = /^[A-Za-z0-9-\s]+$/;
+        if (!validFormat.test(trimmedPlate)) {
+            return '車牌號碼只能包含字母、數字和空格';
+        }
+
+        return null;
+    };
+
+    const saveLicensePlate = async (licensePlate: string | null) => {
+        const validationError = validateLicensePlate(licensePlate);
+        if (validationError) {
+            setError(validationError);
+            Alert.alert('錯誤', validationError);
+            return;
+        }
+
+        try {
+            const response = await chargeStationService.addCar(
+                licensePlate!,
+                '1834d087-bfc1-4f90-8f09-805e3d9422b5',
+                'f599470d-53a5-4026-99c0-2dab34c77f39',
+                true
+            );
+            if (response === true) {
+                setError(null);
+                Alert.alert('成功', '車牌號碼保存成功', [
+                    {
+                        text: 'OK',
+                        onPress: () => router.replace('accountMainPage')
+                    }
+                ]);
+            } else {
+                setError('無法保存車牌號碼');
+                Alert.alert('錯誤', '無法保存車牌號碼,請稍後再試');
+            }
+        } catch (error) {
+            setError('暫時無法保存車牌號碼');
+            Alert.alert('錯誤', '暫時無法保存車牌號碼,請稍後再試');
+        }
+    };
+
+    const handleChangeCar = async () => {
+        if (!name) {
+            setError('請輸入新的車牌號碼');
+            return;
+        }
+        if (!token) {
+            setError('未找到有效的登錄令牌,請重新登錄');
+            return;
+        }
+        setError(null);
+        setIsLoading(true);
+        try {
+            const success = await authenticationService.changeName(name, token);
+            if (success) {
+                if (user) {
+                    setUser({
+                        ...user,
+                        nickname: name
+                    });
+                }
+                router.replace('accountMainPage');
+            } else {
+                setError('更新暱稱失敗,請稍後再試');
+            }
+        } catch (error) {
+            console.error('Error changing name:', error);
+            setError('發生錯誤,請稍後再試');
+        } finally {
+            setIsLoading(false);
+        }
+    };
+
+    return (
+        <SafeAreaView className="flex-1 bg-white" edges={['top', 'right', 'left']}>
+            <ScrollView className="flex-1 mx-[5%]">
+                <View style={{ marginTop: 25 }}>
+                    <Pressable
+                        onPress={() => {
+                            if (router.canGoBack()) {
+                                router.back();
+                            } else {
+                                router.replace('/accountMainPage');
+                            }
+                        }}
+                    >
+                        <CrossLogoSvg />
+                    </Pressable>
+                    <Text style={{ fontSize: 45, marginVertical: 25 }}>更改車牌號碼</Text>
+                    <Text className="text-xl ">請輸入新車牌號碼</Text>
+                    <View className="py-2">
+                        <NormalInput
+                            placeholder={'請輸入新車牌號碼'}
+                            onChangeText={(t) => {
+                                setCar(t);
+                                setError(null); // Clear error when user types
+                            }}
+                            editable={!isLoading}
+                        />
+                        {error && <Text className="text-red-500 mt-1">{error}</Text>}
+                    </View>
+                    <NormalButton
+                        title={<Text className="text-white">{isLoading ? '更改中...' : '確認'}</Text>}
+                        disabled={isLoading}
+                        onPress={() => saveLicensePlate(car)}
+                    />
+                </View>
+            </ScrollView>
+        </SafeAreaView>
+    );
+};
+
+export default ChangeCarPageComponent;

+ 26 - 17
component/accountPages/walletPageComponent.tsx

@@ -617,7 +617,7 @@ export const IndividualCouponComponent = ({
     title: string;
     title: string;
     price: string;
     price: string;
     detail: string;
     detail: string;
-    onCouponClick?: (clickedCoupon: string) => void;
+    onCouponClick?: (clickedCoupon: string, clickedCouponDescription: string) => void;
     date: string;
     date: string;
     setOpacity?: boolean;
     setOpacity?: boolean;
     redeem_code?: string;
     redeem_code?: string;
@@ -931,21 +931,30 @@ const WalletPageComponent = () => {
             console.log(error);
             console.log(error);
         }
         }
     };
     };
-    const handleCouponClick = async (clickedCoupon: string) => {
-        Alert.alert(
-            '立即使用優惠券', // Title
-            '按確認打開相機,掃描充電站上的二維碼以使用優惠券', // Message
-            [
-                {
-                    text: '取消',
-                    style: 'cancel'
-                },
-                {
-                    text: '確認',
-                    onPress: () => router.push('scanQrPage')
-                }
-            ]
-        );
+    // const handleCouponClick = async (clickedCoupon: string) => {
+    //     Alert.alert(
+    //         '立即使用優惠券', // Title
+    //         '按確認打開相機,掃描充電站上的二維碼以使用優惠券', // Message
+    //         [
+    //             {
+    //                 text: '取消',
+    //                 style: 'cancel'
+    //             },
+    //             {
+    //                 text: '確認',
+    //                 onPress: () => router.push('scanQrPage')
+    //             }
+    //         ]
+    //     );
+    // };
+    const handleCouponClick = async (couponName: string, couponDescription: string) => {
+        router.push({
+            pathname: '/couponDetailPage',
+            params: {
+                couponName: couponName,
+                couponDescription: couponDescription
+            }
+        });
     };
     };
     const formattedAmount = formatMoney(walletBalance);
     const formattedAmount = formatMoney(walletBalance);
 
 
@@ -1052,7 +1061,7 @@ const WalletPageComponent = () => {
                                         detail={coupon.coupon.description}
                                         detail={coupon.coupon.description}
                                         date={formatCouponDate(coupon.expire_date)}
                                         date={formatCouponDate(coupon.expire_date)}
                                         noCircle={true}
                                         noCircle={true}
-                                        onCouponClick={handleCouponClick}
+                                        onCouponClick={() => handleCouponClick(coupon.coupon.name, coupon.coupon.description)}
                                     />
                                     />
                                 ))}
                                 ))}
                         </View>
                         </View>

+ 3 - 1
component/global/bookingTabViewComponent.tsx

@@ -79,7 +79,9 @@ const processReservations = (reservations, allStations, isFuture): TabItem[] =>
                 total_fee: reservation.total_fee,
                 total_fee: reservation.total_fee,
                 withdraw_fee: reservation.withdraw_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
+                current_price: snapshot.current_price,
+                total_power: reservation.total_power,
+                
             };
             };
             // .map((reservation) => {
             // .map((reservation) => {
             // const snapshot = JSON.parse(reservation.snapshot);
             // const snapshot = JSON.parse(reservation.snapshot);

+ 15 - 18
component/global/displayedOnlyCouponTabView.tsx

@@ -43,7 +43,7 @@ const FirstRoute = ({
 }: {
 }: {
     coupons: any;
     coupons: any;
     loading: boolean;
     loading: boolean;
-    handleCouponClick: any;
+    handleCouponClick: (couponName: string, couponDescription: string) => void;
 }) => {
 }) => {
     return (
     return (
         <View className="flex-1">
         <View className="flex-1">
@@ -78,7 +78,9 @@ const FirstRoute = ({
                                         .slice(0, 30)
                                         .slice(0, 30)
                                         .map((coupon: any, index: any) => (
                                         .map((coupon: any, index: any) => (
                                             <IndividualCouponComponent
                                             <IndividualCouponComponent
-                                                onCouponClick={handleCouponClick}
+                                                onCouponClick={() =>
+                                                    handleCouponClick(coupon.coupon.name, coupon.coupon.description)
+                                                }
                                                 // key={coupon.redeem_code}
                                                 // key={coupon.redeem_code}
                                                 key={`${coupon.id}-${index}`}
                                                 key={`${coupon.id}-${index}`}
                                                 title={coupon.coupon.name}
                                                 title={coupon.coupon.name}
@@ -155,28 +157,23 @@ const DisplayedOnlyCouponTabView: React.FC<TabViewComponentProps> = ({ titles })
         fetchData();
         fetchData();
     }, []);
     }, []);
 
 
-    const handleCouponClick = async (clickedCoupon: string) => {
-        Alert.alert(
-            '立即使用優惠券', // Title
-            '按確認打開相機,掃描充電站上的二維碼以使用優惠券', // Message
-            [
-                {
-                    text: '取消',
-                    style: 'cancel'
-                },
-                {
-                    text: '確認',
-                    onPress: () => router.push('scanQrPage')
-                }
-            ]
-        );
+    const handleCouponClick = async (clickedCoupon: string, clickedCouponDescription: string) => {
+        router.push({
+            pathname: '/couponDetailPage',
+            params: {
+                couponName: clickedCoupon,
+                couponDescription: clickedCouponDescription
+            }
+        });
     };
     };
 
 
     const renderScene = useCallback(
     const renderScene = useCallback(
         ({ route }: { route: any }) => {
         ({ route }: { route: any }) => {
             switch (route.key) {
             switch (route.key) {
                 case 'firstRoute':
                 case 'firstRoute':
-                    return <FirstRoute coupons={useableCoupons} loading={loading} handleCouponClick={handleCouponClick} />;
+                    return (
+                        <FirstRoute coupons={useableCoupons} loading={loading} handleCouponClick={handleCouponClick} />
+                    );
                 case 'secondRoute':
                 case 'secondRoute':
                     return <SecondRoute coupons={coupons} />;
                     return <SecondRoute coupons={coupons} />;
                 default:
                 default:

+ 7 - 5
component/global/tabView.tsx

@@ -357,11 +357,13 @@ const TabItem = ({ item, currentLocation }: { item: TabItem; currentLocation: Lo
                                 color: '#222222'
                                 color: '#222222'
                             }}
                             }}
                         >
                         >
-                            需付金額:{' '}
-                            {item.total_fee !== undefined && item.total_fee !== null
-                                ? item.total_fee % 1 === 0
-                                    ? `$${item.total_fee}`
-                                    : `$${item.total_fee.toFixed(1)}`
+                            應付金額:{' '}
+                            {item.actual_fee !== undefined && item.actual_fee !== null
+                                ? item.actual_fee <= 0
+                                    ? '$0'
+                                    : item.actual_fee % 1 === 0
+                                    ? `$${item.actual_fee}`
+                                    : `$${item.actual_fee.toFixed(1)}`
                                 : ''}
                                 : ''}
                         </Text>
                         </Text>
                     </View>
                     </View>

+ 2 - 1
context/AuthProvider.tsx

@@ -136,7 +136,8 @@ export default function AuthProvider({ children }: { children: ReactNode }) {
                 gender: res.data.gender,
                 gender: res.data.gender,
                 email: res.data.email,
                 email: res.data.email,
                 nickname: res.data.nickname,
                 nickname: res.data.nickname,
-                phone: res.data.phone
+                phone: res.data.phone,
+                car: res.data.defaultCar.license_plate
             };
             };
             return items;
             return items;
         } catch (error) {
         } catch (error) {