Bläddra i källkod

结账页数据初始化逻辑修改

fogwind 6 dagar sedan
förälder
incheckning
568237e1ee

+ 0 - 70
src/app/(checkout)/checkout/_components/CheckoutAddressModal.tsx

@@ -1,70 +0,0 @@
-"use client";
-
-import {
-  Drawer,
-  DrawerContent,
-  DrawerHeader,
-  DrawerBody,
-  DrawerFooter
-} from "@heroui/drawer";
-
-export default function CheckoutAddressModal({ 
-    isOpen,
-    onClose,
-    // onSave,
-    body,
-    footer
-}: {
-    isOpen: boolean;
-    onClose: (e: boolean) => void;
-    // onSave: () => void;
-    body: React.ReactNode;
-    footer: React.ReactNode;
-}) {
-
-    const openChange = (e: boolean) => {
-        onClose(e);
-        console.log('address modal open change ---- ', e);
-    }
-
-    return (
-   
-            <Drawer
-                backdrop={"blur"}
-                placement="bottom"
-                isDismissable={false}
-                isKeyboardDismissDisabled={true}
-                isOpen={isOpen}
-                hideCloseButton
-                onOpenChange={(e) => openChange(e)}
-            >
-                <DrawerContent className="rounded-none h-17/20 max-h-none">
-                {(onClose) => (
-                    <>
-                        <DrawerHeader className="p-0">
-                            <div className="w-full box-border p-3.75 flex justify-between items-center">
-                                <span>Shipping Address</span>
-                                <button className="w-6 h-6" onClick={onClose}>
-                                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon" className="h-6 transition-all ease-in-out hover:scale-110"><path strokeLinecap="round" strokeLinejoin="round" d="M6 18 18 6M6 6l12 12"></path></svg>
-                                </button>
-                            </div>
-                        </DrawerHeader>
-                        <DrawerBody className="drawer-body relative p-3.75">
-                            <p className="text-ly-12 leading-ly-20">
-                                Select a billing address from your address book or enter a new address.
-                            </p>
-                            <div>
-                                {body}
-                            </div>
-                            
-                        </DrawerBody>
-                        <DrawerFooter className="box-border border-t border-gray-200 p-3.75">
-                            {footer}
-                        </DrawerFooter>
-                    </>
-                )}
-                </DrawerContent>
-            </Drawer>
-  
-    ); 
-}

+ 165 - 93
src/app/(checkout)/checkout/_components/CheckoutWrapper.tsx

@@ -1,6 +1,6 @@
 "use client";
 "use client";
 
 
-import {useState, useEffect, useRef} from "react";
+import {useState, useRef} from "react";
 
 
 import { LoadingSpinner } from "@components/common/LoadingSpinner";
 import { LoadingSpinner } from "@components/common/LoadingSpinner";
 import CheckoutShippingMethodLoading from "./CheckoutShippingMethodLoading";
 import CheckoutShippingMethodLoading from "./CheckoutShippingMethodLoading";
@@ -9,12 +9,10 @@ import { useForm, FormProvider } from "react-hook-form";
 import { 
 import { 
     Country,
     Country,
     ShipAddressFormData,
     ShipAddressFormData,
-    BillAddressFormData,
     FullAddressFormData,
     FullAddressFormData,
     CreateCheckoutAddressVariables,
     CreateCheckoutAddressVariables,
 } from "@/types/checkout/type";
 } from "@/types/checkout/type";
 import AddressResultDisplay from "./AddressResultDisplay";
 import AddressResultDisplay from "./AddressResultDisplay";
-import AddressResultDisplayLoading from "./AddressResultDisplayLoading";
 import ShippingAddressCheckout from "./ShippingAddressCheckout";
 import ShippingAddressCheckout from "./ShippingAddressCheckout";
 import BillingAddressCheckout from "./BillingAddressCheckout";
 import BillingAddressCheckout from "./BillingAddressCheckout";
 import {ShippingMethodCheckout,RefShippingMethodsHandle} from "./ShippingMethodCheckout";
 import {ShippingMethodCheckout,RefShippingMethodsHandle} from "./ShippingMethodCheckout";
@@ -22,17 +20,18 @@ import { useCustomToast } from "@/utils/hooks/useToast";
 import {useCheckoutAddress} from "@/utils/hooks/useCheckoutAddress"
 import {useCheckoutAddress} from "@/utils/hooks/useCheckoutAddress"
 import {useCheckoutPaymentMethod} from  "@/utils/hooks/useCheckoutPaymentMethod";
 import {useCheckoutPaymentMethod} from  "@/utils/hooks/useCheckoutPaymentMethod";
 import {useCheckoutShippingMethod} from  "@/utils/hooks/useCheckoutShippingMethod";
 import {useCheckoutShippingMethod} from  "@/utils/hooks/useCheckoutShippingMethod";
-import { useAppDispatch,useAppStore  } from "@/store/hooks";
+import { useAppDispatch } from "@/store/hooks";
 import { updateCart } from "@/store/slices/cart-slice";
 import { updateCart } from "@/store/slices/cart-slice";
-import { useCartDetail } from "@utils/hooks/useCartDetail";
 import {PaymentMethodCheckout,RefPaymentMethodsHandle} from "./PaymentMethodCheckout";
 import {PaymentMethodCheckout,RefPaymentMethodsHandle} from "./PaymentMethodCheckout";
 import LoadingPaymentMethod from "./LoadingPaymentMethod";
 import LoadingPaymentMethod from "./LoadingPaymentMethod";
+import LoadingCheckoutPlaceOrder from "./LoadingCheckoutPlaceOrder";
 import CheckoutPlaceOrder from "./CheckoutPlaceOrder";
 import CheckoutPlaceOrder from "./CheckoutPlaceOrder";
 import CommonModal from "@/components/theme/ui/CommonModal";
 import CommonModal from "@/components/theme/ui/CommonModal";
 import {useSaveCheckoutCart} from "@/utils/hooks/useSaveCheckoutCart";
 import {useSaveCheckoutCart} from "@/utils/hooks/useSaveCheckoutCart";
 import { formatCartDetail, shippingAddressToCartAddress, billingAddressToCartAddress } from "@/utils/cartDetailTools";
 import { formatCartDetail, shippingAddressToCartAddress, billingAddressToCartAddress } from "@/utils/cartDetailTools";
 import { overlayLoading } from "@/components/theme/ui/kernel/loading/api";
 import { overlayLoading } from "@/components/theme/ui/kernel/loading/api";
-import { CartDetail } from "@/types/cart/type";
+import { CartDetail,CartAddress } from "@/types/cart/type";
+import {normalizePhoneForForm} from "@/utils/phoneNumberTools";
 /***
 /***
  * 不用useForShipping字段了
  * 不用useForShipping字段了
  * 以shipping address 为准,根据shippingaddress 设置billing address
  * 以shipping address 为准,根据shippingaddress 设置billing address
@@ -41,6 +40,119 @@ import { CartDetail } from "@/types/cart/type";
  * @todo 购物车为空 跳转到空购物车页面
  * @todo 购物车为空 跳转到空购物车页面
  */
  */
 
 
+/**
+ * 地址表单默认值
+ */
+function getBillingAddressFormData(billingAddress:CartAddress) {
+    const billingCountry = billingAddress.country || 'US';
+    const billingPhoneNumber =  normalizePhoneForForm(billingAddress.phone, billingCountry) || "";
+    const res = {
+        "billingAddressId": billingAddress.id,
+        "billingEmail": billingAddress.email,
+        "billingFirstName": billingAddress.firstName,
+        "billingLastName": billingAddress.lastName,
+        "billingCompanyName": "",
+        "billingAddress": billingAddress.address,
+        "billingCountry": billingCountry,
+        "billingState": billingAddress.state,
+        "billingCity": billingAddress.city,
+        "billingPostcode": billingAddress.postcode,
+        "billingPhoneNumber": billingPhoneNumber,
+    };
+
+    return res;
+}
+function getShippingAddressFormData(shippingAddress:CartAddress) {
+    const shippingCountry = shippingAddress.country || 'US';
+    const shippingPhoneNumber =  normalizePhoneForForm(shippingAddress.phone, shippingCountry) || "";
+    const res = {
+        "shippingAddressId": shippingAddress.id,
+        "shippingEmail": shippingAddress.email,
+        "shippingFirstName": shippingAddress.firstName,
+        "shippingLastName": shippingAddress.lastName,
+        "shippingCompanyName": "",
+        "shippingAddress": shippingAddress.address,
+        "shippingCountry": shippingCountry,
+        "shippingState": shippingAddress.state,
+        "shippingCity": shippingAddress.city,
+        "shippingPostcode": shippingAddress.postcode,
+        "shippingPhoneNumber": shippingPhoneNumber,
+    };
+
+    return res;
+}
+function getAddressFormDataFromCart(cartDetail:CartDetail, loginEmail?: string) {
+    const shippingAddress = cartDetail.shippingAddress;
+    const billingAddress = cartDetail.billingAddress;
+    const defaultValues = {
+        "shippingAddressId": "",
+        "shippingEmail": loginEmail ?? '',
+        "shippingFirstName": "",
+        "shippingLastName": "",
+        "shippingCompanyName": "",
+        "shippingAddress": "",
+        "shippingCountry": "US",
+        "shippingState": "",
+        "shippingCity": "",
+        "shippingPostcode": "",
+        "shippingPhoneNumber": "",
+        "billingAddressId": "",
+        "billingEmail": loginEmail ?? '',
+        "billingFirstName": "",
+        "billingLastName": "",
+        "billingCompanyName": "",
+        "billingAddress": "",
+        "billingCountry": "US",
+        "billingState": "",
+        "billingCity": "",
+        "billingPostcode": "",
+        "billingPhoneNumber": "",
+        "billingSameAsShipping": true
+    }
+    if(billingAddress) {
+        const bs = getBillingAddressFormData(billingAddress);
+        defaultValues.billingAddressId = bs.billingAddressId;
+        defaultValues.billingEmail = bs.billingEmail;
+        defaultValues.billingFirstName = bs.billingFirstName;
+        defaultValues.billingLastName = bs.billingLastName;
+        defaultValues.billingCompanyName = bs.billingCompanyName;
+        defaultValues.billingAddress = bs.billingAddress;
+        defaultValues.billingCountry = bs.billingCountry;
+        defaultValues.billingState = bs.billingState;
+        defaultValues.billingCity = bs.billingCity;
+        defaultValues.billingPostcode = bs.billingPostcode;
+        defaultValues.billingPhoneNumber = bs.billingPhoneNumber;
+        // defaultValues.billingSameAsShipping = true;
+    }
+    if(shippingAddress) {
+        const ss = getShippingAddressFormData(shippingAddress);
+        defaultValues.shippingAddressId = ss.shippingAddressId;
+        defaultValues.shippingEmail = ss.shippingEmail;
+        defaultValues.shippingFirstName = ss.shippingFirstName;
+        defaultValues.shippingLastName = ss.shippingLastName;
+        defaultValues.shippingCompanyName = ss.shippingCompanyName;
+        defaultValues.shippingAddress = ss.shippingAddress;
+        defaultValues.shippingCountry = ss.shippingCountry;
+        defaultValues.shippingState = ss.shippingState;
+        defaultValues.shippingCity = ss.shippingCity;
+        defaultValues.shippingPostcode = ss.shippingPostcode;
+        defaultValues.shippingPhoneNumber = ss.shippingPhoneNumber;
+    }
+    if( defaultValues.billingEmail === defaultValues.shippingEmail &&
+        defaultValues.billingFirstName === defaultValues.shippingFirstName &&
+        defaultValues.billingLastName === defaultValues.shippingLastName &&
+        defaultValues.billingCompanyName === defaultValues.shippingCompanyName &&
+        defaultValues.billingAddress === defaultValues.shippingAddress &&
+        defaultValues.billingCountry === defaultValues.shippingCountry &&
+        defaultValues.billingState === defaultValues.shippingState &&
+        defaultValues.billingCity === defaultValues.shippingCity &&
+        defaultValues.billingPostcode === defaultValues.shippingPostcode &&
+        defaultValues.billingPhoneNumber === defaultValues.shippingPhoneNumber
+    ) {
+        defaultValues.billingSameAsShipping = true;
+    }
+    return defaultValues;
+}
 /**
 /**
  * 生成createCheckoutAddress接口的参数
  * 生成createCheckoutAddress接口的参数
  */
  */
@@ -99,13 +211,17 @@ function generateSaveCheckoutAddressParam(formData: FullAddressFormData):CreateC
 export default function CheckoutWrapper({
 export default function CheckoutWrapper({
     loginEmail,
     loginEmail,
     countries,
     countries,
+    cartDetailData,
 }: {
 }: {
     loginEmail: string;
     loginEmail: string;
-    countries: Country[],
+    countries: Country[];
+    cartDetailData: CartDetail;
 }) {
 }) {
 
 
+    const addressFormDefaultValues = getAddressFormDataFromCart(cartDetailData,loginEmail);
     const dispatch = useAppDispatch();
     const dispatch = useAppDispatch();
-    const { cartData } = useCartDetail();
+    
+    const [cartData, setCartData] = useState<CartDetail>(cartDetailData);
     const {saveCheckoutCart} = useSaveCheckoutCart();
     const {saveCheckoutCart} = useSaveCheckoutCart();
     
     
     const {
     const {
@@ -116,7 +232,8 @@ export default function CheckoutWrapper({
     } = useCheckoutShippingMethod();
     } = useCheckoutShippingMethod();
 
 
 
 
-    const selectedPaymentMethod = cartData?.paymentMethod ?? 'paypal_smart_button';
+    const selectedPaymentMethod = cartData.paymentMethod || 'paypal_smart_button';
+    const selectedShippingRate = cartData.selectedShippingRate;
    
    
     const { 
     const { 
         data: paymentMethodDatas,
         data: paymentMethodDatas,
@@ -131,73 +248,32 @@ export default function CheckoutWrapper({
     const methodShiippingRef = useRef<RefShippingMethodsHandle>(null);
     const methodShiippingRef = useRef<RefShippingMethodsHandle>(null);
     const methodPaymentRef = useRef<RefPaymentMethodsHandle>(null);
     const methodPaymentRef = useRef<RefPaymentMethodsHandle>(null);
 
 
+    
 
 
     // useForm() 只会在组件初始化时读取一次 defaultValues
     // useForm() 只会在组件初始化时读取一次 defaultValues
     const addressForm = useForm<FullAddressFormData>({
     const addressForm = useForm<FullAddressFormData>({
         mode: "onBlur",          // 或 "onChange"
         mode: "onBlur",          // 或 "onChange"
         reValidateMode: "onChange",
         reValidateMode: "onChange",
-        defaultValues: {
-            "shippingAddressId": "",
-            "shippingEmail": loginEmail,
-            "shippingFirstName": "",
-            "shippingLastName": "jack",
-            "shippingCompanyName": "",
-            "shippingAddress": "",
-            "shippingCountry": "US",
-            "shippingState": "",
-            "shippingCity": "",
-            "shippingPostcode": "",
-            "shippingPhoneNumber": "",
-            "billingAddressId": "",
-            "billingEmail": "",
-            "billingFirstName": "",
-            "billingLastName": "",
-            "billingCompanyName": "",
-            "billingAddress": "",
-            "billingCountry": "US",
-            "billingState": "",
-            "billingCity": "",
-            "billingPostcode": "",
-            "billingPhoneNumber": "",
-            "billingSameAsShipping": true
-        } 
+        defaultValues: addressFormDefaultValues 
     });
     });
 
 
     const [addressModalOpen, setAddressModalOpen] = useState(false);
     const [addressModalOpen, setAddressModalOpen] = useState(false);
-    const [loadingAddress, setLoadingAddress] = useState(true); // 初始加载地址数据时的loading状态
 
 
-    const serverShippingAddress = useRef<ShipAddressFormData | null>(null);
-    const serverBillingAddress = useRef<BillAddressFormData | null>(null);
 
 
     const [addressSaving, setAddressSaving] = useState(false); // 保存地址时的loading状态
     const [addressSaving, setAddressSaving] = useState(false); // 保存地址时的loading状态
 
 
-    // 开发环境会执行两次 https://zh-hans.react.dev/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development
-    useEffect(() => { 
-        getCheckoutAddress((shippingAddress,billingAddress)=> {
-            addressForm.reset({
-                ...shippingAddress,
-                ...billingAddress,
-            });
 
 
-            serverShippingAddress.current = shippingAddress;
-            serverBillingAddress.current = billingAddress;
-         
-            setLoadingAddress(false);
-        }, ()=> {
-            setLoadingAddress(false);
-        });
-    }, []);
 
 
 
 
     const openAddressModal = () => {
     const openAddressModal = () => {
         setAddressModalOpen(true);
         setAddressModalOpen(true);
 
 
-        if(serverShippingAddress.current && serverBillingAddress.current) {
+        if(cartData.billingAddress && cartData.shippingAddress) {
             addressForm.reset({
             addressForm.reset({
-                ...serverShippingAddress.current,
-                ...serverBillingAddress.current,
+                ...getAddressFormDataFromCart(cartData)
             });
             });
         }
         }
+
         
         
     };
     };
     const closeAddressModal = (e:boolean) => {
     const closeAddressModal = (e:boolean) => {
@@ -217,8 +293,8 @@ export default function CheckoutWrapper({
             const res = await saveCheckoutAddress(saveAddressParam);
             const res = await saveCheckoutAddress(saveAddressParam);
             console.log('CREATE_CHECKOUT_ADDRESS res ====== ',res);
             console.log('CREATE_CHECKOUT_ADDRESS res ====== ',res);
             // 地址保存成功后重新获取运输方式和支付方式 
             // 地址保存成功后重新获取运输方式和支付方式 
-            getShippingMethod();
-            getPaymentMethod();
+            await getShippingMethod();
+            await getPaymentMethod();
             
             
             // 保存完地址之后还要重新请求地址,把保存后的地址id同步到表单里;
             // 保存完地址之后还要重新请求地址,把保存后的地址id同步到表单里;
             const queryAddressRes = await getCheckoutAddress();
             const queryAddressRes = await getCheckoutAddress();
@@ -230,12 +306,17 @@ export default function CheckoutWrapper({
                     defaultValue: queryAddressRes.billingAddress.billingAddressId
                     defaultValue: queryAddressRes.billingAddress.billingAddressId
                 });
                 });
 
 
-                serverShippingAddress.current = queryAddressRes.shippingAddress;
-                serverBillingAddress.current = queryAddressRes.billingAddress;
+      
                 // 同步地址到购物车详情
                 // 同步地址到购物车详情
+                const newBillingAddress = billingAddressToCartAddress(queryAddressRes.billingAddress);
+                const newShippingAddress = shippingAddressToCartAddress(queryAddressRes.shippingAddress);
                 dispatch(updateCart({
                 dispatch(updateCart({
-                    billingAddress: billingAddressToCartAddress(queryAddressRes.billingAddress),
-                    shippingAddress: shippingAddressToCartAddress(queryAddressRes.shippingAddress)
+                    billingAddress: newBillingAddress,
+                    shippingAddress: newShippingAddress
+                }));
+                setCartData(Object.assign({...cartData},{
+                    billingAddress: {...newBillingAddress},
+                    shippingAddress: {...newShippingAddress}
                 }));
                 }));
 
 
             }
             }
@@ -252,7 +333,7 @@ export default function CheckoutWrapper({
     };
     };
 
 
 
 
-    const handlePaymentMethodChange =  async (method: string,title: string) => { 
+    const handlePaymentMethodChange =  async (method: string) => { 
         let shippingMethod = '-1';
         let shippingMethod = '-1';
         if(methodShiippingRef.current) {
         if(methodShiippingRef.current) {
             shippingMethod = methodShiippingRef.current.getSelectShipMethod();
             shippingMethod = methodShiippingRef.current.getSelectShipMethod();
@@ -268,24 +349,12 @@ export default function CheckoutWrapper({
             if(saveRes.data) {
             if(saveRes.data) {
                 const newCartDetail = formatCartDetail(saveRes.data);
                 const newCartDetail = formatCartDetail(saveRes.data);
                 dispatch(updateCart(newCartDetail));
                 dispatch(updateCart(newCartDetail));
+                setCartData({...newCartDetail});
             }
             }
         } else {
         } else {
             showToast(saveRes.msg, 'danger');
             showToast(saveRes.msg, 'danger');
         }
         }
 
 
-        /*
-        const res = await savePaymentMethod({ paymentMethod: method });
-        console.log('savePaymentMethod res ====== ',res);
-        if(!res.error) {
-            // 更新paymentMethod paymentMethodTitle
-            dispatch(updateCart({
-                paymentMethod: method,
-                paymentMethodTitle: title
-            }));
-        } else {
-            showToast(res.msg, 'danger');
-        }
-        */
     };
     };
     const handleShippingMethodChange =  async (method: string) => { 
     const handleShippingMethodChange =  async (method: string) => { 
         overlayLoading.start();
         overlayLoading.start();
@@ -299,21 +368,14 @@ export default function CheckoutWrapper({
             if(saveRes.data) {
             if(saveRes.data) {
                 const newCartDetail = formatCartDetail(saveRes.data);
                 const newCartDetail = formatCartDetail(saveRes.data);
                 dispatch(updateCart(newCartDetail));
                 dispatch(updateCart(newCartDetail));
+                setCartData({...newCartDetail});
             }
             }
             // 保存成功之后需要重新获取支付方式
             // 保存成功之后需要重新获取支付方式
             getPaymentMethod();
             getPaymentMethod();
         } else {
         } else {
             showToast(saveRes.msg, 'danger');
             showToast(saveRes.msg, 'danger');
         }
         }
-        /*
-        const res = await saveShippingMethod({ shippingMethod: method });
-        if(!res.error) {
-            // 保存成功之后需要重新获取购物车详情和支付方式
-            getCartDetail();
-            getPaymentMethod();
-        } else {
-            showToast(res.msg, 'danger');
-        }*/
+
     };
     };
 
 
     // 下单
     // 下单
@@ -354,7 +416,7 @@ export default function CheckoutWrapper({
         }
         }
         
         
         //如果购物车详情没有保存过支付方式,则需要保存支付方式
         //如果购物车详情没有保存过支付方式,则需要保存支付方式
-        if(cartData?.paymentMethod === null) {
+        if(cartData.paymentMethod === null) {
             const pmethod = methodPaymentRef?.current.getSelectPaymentMethod();
             const pmethod = methodPaymentRef?.current.getSelectPaymentMethod();
             const saveRes = await saveCheckoutCart({ 
             const saveRes = await saveCheckoutCart({ 
                 shippingMethod: '-1',
                 shippingMethod: '-1',
@@ -365,6 +427,7 @@ export default function CheckoutWrapper({
                 if(saveRes.data) {
                 if(saveRes.data) {
                     const newCartDetail = formatCartDetail(saveRes.data);
                     const newCartDetail = formatCartDetail(saveRes.data);
                     dispatch(updateCart(newCartDetail));
                     dispatch(updateCart(newCartDetail));
+                    setCartData({...newCartDetail});
                 }
                 }
             }
             }
         }
         }
@@ -397,13 +460,16 @@ export default function CheckoutWrapper({
         <section className="w-full">
         <section className="w-full">
             <div>
             <div>
             {/* <button className="w-25 h-8 bg-amber-500 flex items-center justify-center" onClick={tt}>test</button> */}
             {/* <button className="w-25 h-8 bg-amber-500 flex items-center justify-center" onClick={tt}>test</button> */}
-                    {loadingAddress ? 
+                    {/* {loadingAddress ? 
                         <AddressResultDisplayLoading /> 
                         <AddressResultDisplayLoading /> 
                     :
                     :
-                        <AddressResultDisplay shippingAddress={cartData?.shippingAddress ?? null} 
+                        <AddressResultDisplay shippingAddress={cartData.shippingAddress || null} 
                             onAddressModalOpenClick={openAddressModal}
                             onAddressModalOpenClick={openAddressModal}
                         />
                         />
-                    }
+                    } */}
+                    <AddressResultDisplay shippingAddress={cartData.shippingAddress || null} 
+                        onAddressModalOpenClick={openAddressModal}
+                    />
                     <CommonModal
                     <CommonModal
                         isOpen={addressModalOpen}
                         isOpen={addressModalOpen}
                         onClose={closeAddressModal}
                         onClose={closeAddressModal}
@@ -451,6 +517,7 @@ export default function CheckoutWrapper({
                         ref={methodShiippingRef}  
                         ref={methodShiippingRef}  
                         shipMethods={shippingMethodDatas}
                         shipMethods={shippingMethodDatas}
                         errorMsg={shippingMethodError}  
                         errorMsg={shippingMethodError}  
+                        selectedShippingRate={selectedShippingRate}
                         onShipMethodChange={handleShippingMethodChange}  
                         onShipMethodChange={handleShippingMethodChange}  
                     />
                     />
                 }
                 }
@@ -466,18 +533,23 @@ export default function CheckoutWrapper({
                         ref={methodPaymentRef}
                         ref={methodPaymentRef}
                         errorMsg={paymentMethodError}
                         errorMsg={paymentMethodError}
                         paymentMethods={paymentMethodDatas} 
                         paymentMethods={paymentMethodDatas} 
+                        selectedPaymentMethod={selectedPaymentMethod}
                         onPaymentMethodChange={handlePaymentMethodChange}
                         onPaymentMethodChange={handlePaymentMethodChange}
                     />
                     />
                 }
                 }
                 
                 
             </div>
             </div>
-            <div className="mt-3 box-border px-4">
+            <div className="mt-3 box-border px-4 pb-8">
                 <p className="text-ly-12 text-[#666666] leading-ly-20">By providing your information, you agree to Wiggins'sPrivacy Policyand Terms of Use.</p>
                 <p className="text-ly-12 text-[#666666] leading-ly-20">By providing your information, you agree to Wiggins'sPrivacy Policyand Terms of Use.</p>
                 <div className="mt-6 w-full">
                 <div className="mt-6 w-full">
-                    <CheckoutPlaceOrder 
-                        paymentMethod={selectedPaymentMethod}
-                        clickPlaceOrder={handlePlaceOrder}
-                    />
+                    { paymentMethodLoading ?
+                        <LoadingCheckoutPlaceOrder />
+                    :
+                        <CheckoutPlaceOrder 
+                            paymentMethod={selectedPaymentMethod}
+                            clickPlaceOrder={handlePlaceOrder}
+                        />
+                    }
                 </div>
                 </div>
             </div>
             </div>
 
 

+ 37 - 18
src/app/(checkout)/checkout/_components/PaymentButton/PaypalButton.tsx

@@ -4,8 +4,6 @@
 import {useRef} from "react";
 import {useRef} from "react";
 import { redirect, RedirectType } from 'next/navigation';
 import { redirect, RedirectType } from 'next/navigation';
 import { usePlaceOrder } from "@/utils/hooks/usePlaceOrder";
 import { usePlaceOrder } from "@/utils/hooks/usePlaceOrder";
-import { getCookie } from "@/utils/cookie-tools";
-import { IS_GUEST } from "@/utils/constants";
 import {
 import {
   usePayPal,
   usePayPal,
   useEligibleMethods,
   useEligibleMethods,
@@ -20,8 +18,8 @@ import {
 } from "@paypal/react-paypal-js/sdk-v6";
 } from "@paypal/react-paypal-js/sdk-v6";
 import { overlayLoading } from "@/components/theme/ui/kernel/loading/api";
 import { overlayLoading } from "@/components/theme/ui/kernel/loading/api";
 import { confirmDialog } from "@/components/theme/ui/kernel/confirm/api";
 import { confirmDialog } from "@/components/theme/ui/kernel/confirm/api";
-import { useAppDispatch } from "@/store/hooks";
-import { clearCart } from "@/store/slices/cart-slice";
+
+
 export default function PaypalButton({
 export default function PaypalButton({
     createOrder,
     createOrder,
     onPaymentClick,
     onPaymentClick,
@@ -31,9 +29,9 @@ export default function PaypalButton({
   onPaymentClick: () => Promise<boolean>;
   onPaymentClick: () => Promise<boolean>;
   isRePay?: boolean;
   isRePay?: boolean;
 }) { 
 }) { 
-    const dispatch = useAppDispatch();
-    const isGuest = getCookie(IS_GUEST) !== "false";// 'true' 是游客
-    const {createPaymentCallback,cancelOrder} = usePlaceOrder();
+
+
+    const {createPaymentCallback} = usePlaceOrder();
 
 
     const gatewayOrderIdRef = useRef("");
     const gatewayOrderIdRef = useRef("");
     const webOrderIdRef = useRef("");
     const webOrderIdRef = useRef("");
@@ -47,8 +45,8 @@ export default function PaypalButton({
         isLoading: isEligibilityLoading,
         isLoading: isEligibilityLoading,
     } = useEligibleMethods({
     } = useEligibleMethods({
         payload: {
         payload: {
-        currencyCode: "USD",
-        paymentFlow: "ONE_TIME_PAYMENT",
+            currencyCode: "USD", // @todo 需要根据用户选择的货币走
+            paymentFlow: "ONE_TIME_PAYMENT",
         },
         },
     });
     });
     const isLoading = loadingStatus === INSTANCE_LOADING_STATE.PENDING;
     const isLoading = loadingStatus === INSTANCE_LOADING_STATE.PENDING;
@@ -73,8 +71,6 @@ export default function PaypalButton({
                     noCancel: true,
                     noCancel: true,
                 }).then(() => {});
                 }).then(() => {});
             } else {
             } else {
-                //创建订单成功后清空redux 购物车
-                dispatch(clearCart());
                 gatewayOrderIdRef.current = res.orderId;
                 gatewayOrderIdRef.current = res.orderId;
                 webOrderIdRef.current = res.webOrderId;
                 webOrderIdRef.current = res.webOrderId;
                 return buttonHandlerClick();
                 return buttonHandlerClick();
@@ -113,12 +109,12 @@ export default function PaypalButton({
                     content: "Your payment was successful. Will redirect to success page.",
                     content: "Your payment was successful. Will redirect to success page.",
                     noCancel: true,
                     noCancel: true,
                 }).then(() => {
                 }).then(() => {
-                    redirect('/success?orderid=' + webOrderIdRef.current, RedirectType.replace);
+                    redirect('/paymentresult/result?orderId=' + webOrderIdRef.current + '&return_result=success', RedirectType.replace);
                 });
                 });
             } else {
             } else {
                 // callback 失败,提示错误
                 // callback 失败,提示错误
                 confirmDialog({
                 confirmDialog({
-                    title: "Payment failed",
+                    title: "Somthing Wrong",
                     content: resCallback.msg + " Please contact customer service." + " OrderId: "+ webOrderIdRef.current,
                     content: resCallback.msg + " Please contact customer service." + " OrderId: "+ webOrderIdRef.current,
                     noCancel: true,
                     noCancel: true,
                 }).then(() => { 
                 }).then(() => { 
@@ -140,13 +136,15 @@ export default function PaypalButton({
                 status: 'cancel',
                 status: 'cancel',
             });
             });
             if(!isRePay) {
             if(!isRePay) {
+                overlayLoading.stop();
+                redirect('/paymentresult/result?orderId=' + webOrderIdRef.current + '&return_result=cancel', RedirectType.replace);
+                /*
                 if(isGuest) {
                 if(isGuest) {
-                    
                     await cancelOrder({
                     await cancelOrder({
                         orderId: Number(webOrderIdRef.current),
                         orderId: Number(webOrderIdRef.current),
                     });
                     });
-                    // deleteCookie(GUEST_CART_TOKEN);
-                    // deleteCookie(GUEST_CART_ID);
+                    // 重新获取购物车
+                    dispatch(fetchCartDetail());
                     confirmDialog({
                     confirmDialog({
                         title: "Order Cancelled",
                         title: "Order Cancelled",
                         content: "Your order has been cancelled. Will redirect to home page.",
                         content: "Your order has been cancelled. Will redirect to home page.",
@@ -165,6 +163,14 @@ export default function PaypalButton({
                         redirect('/checkout/continuetopay?orderid=' + webOrderIdRef.current, RedirectType.replace);
                         redirect('/checkout/continuetopay?orderid=' + webOrderIdRef.current, RedirectType.replace);
                     });
                     });
                 }
                 }
+                */
+            } else {
+                // 登录用户在二次支付页面 
+                confirmDialog({
+                    title: "Payment cancelled",
+                    content: "You canceled the payment.",
+                    noCancel: true,
+                }).then(() => {  });
             }
             }
         },
         },
 
 
@@ -181,13 +187,16 @@ export default function PaypalButton({
                 status: 'failure',
                 status: 'failure',
             });
             });
             if(!isRePay) {
             if(!isRePay) {
+                overlayLoading.stop();
+                redirect('/paymentresult/result?orderId=' + webOrderIdRef.current + '&return_result=failure', RedirectType.replace);
+                /*
                 if(isGuest) {
                 if(isGuest) {
                     
                     
                     await cancelOrder({
                     await cancelOrder({
                         orderId: Number(webOrderIdRef.current),
                         orderId: Number(webOrderIdRef.current),
                     });
                     });
-                    // deleteCookie(GUEST_CART_TOKEN);
-                    // deleteCookie(GUEST_CART_ID);
+                    // 重新获取购物车
+                    dispatch(fetchCartDetail());
                     confirmDialog({
                     confirmDialog({
                         title: "Payment Failed",
                         title: "Payment Failed",
                         content: data.message + " Will redirect to home page.",
                         content: data.message + " Will redirect to home page.",
@@ -206,6 +215,16 @@ export default function PaypalButton({
                         redirect('/checkout/continuetopay?orderid=' + webOrderIdRef.current, RedirectType.replace);
                         redirect('/checkout/continuetopay?orderid=' + webOrderIdRef.current, RedirectType.replace);
                     });
                     });
                 }
                 }
+                */
+            } else {
+                
+                // 登录用户 二次支付页面 
+                confirmDialog({
+                    title: "Payment Failed",
+                    content: data.message,
+                    noCancel: true,
+                }).then(() => {  });
+            
             }
             }
         
         
         },
         },

+ 15 - 14
src/app/(checkout)/checkout/_components/PaymentMethodCheckout.tsx

@@ -4,7 +4,7 @@ import Image from "next/image";
 import { useEffect, Ref, useImperativeHandle, useRef } from "react";
 import { useEffect, Ref, useImperativeHandle, useRef } from "react";
 import { useForm, get } from "react-hook-form";
 import { useForm, get } from "react-hook-form";
 import {CheckoutPaymentMethod} from "@/types/checkout/type";
 import {CheckoutPaymentMethod} from "@/types/checkout/type";
-import { useAppSelector } from "@/store/hooks";
+
 
 
 /**
 /**
  * 关于如何确定默认选中哪个支付方式:
  * 关于如何确定默认选中哪个支付方式:
@@ -19,22 +19,18 @@ export function PaymentMethodCheckout({
     ref,
     ref,
     errorMsg,
     errorMsg,
     paymentMethods,
     paymentMethods,
+    selectedPaymentMethod,
     onPaymentMethodChange
     onPaymentMethodChange
 }: {
 }: {
     ref: Ref<RefPaymentMethodsHandle>;
     ref: Ref<RefPaymentMethodsHandle>;
     errorMsg: string | null;
     errorMsg: string | null;
     paymentMethods: CheckoutPaymentMethod[];
     paymentMethods: CheckoutPaymentMethod[];
-    onPaymentMethodChange: (value: string,title:string) => void;
+    selectedPaymentMethod: string;
+    onPaymentMethodChange: (value: string) => void;
 }) {
 }) {
     const selectPaymentTitle = useRef('');
     const selectPaymentTitle = useRef('');
-    const cartDetail = useAppSelector((state) => state.cartDetail);
-    const defaultPaymentMethod = cartDetail.cart?.paymentMethod ?? 'paypal_smart_button';
 
 
-    paymentMethods.forEach((item) => {
-        if(item.method === defaultPaymentMethod) {
-            selectPaymentTitle.current = item.title;
-        }
-    });
+    const defaultPaymentMethod = selectedPaymentMethod;
   
   
     const {
     const {
         register,
         register,
@@ -50,6 +46,12 @@ export function PaymentMethodCheckout({
     // 参考 https://github.com/react-hook-form/error-message/blob/master/src/ErrorMessage.tsx
     // 参考 https://github.com/react-hook-form/error-message/blob/master/src/ErrorMessage.tsx
     const paymentMethodError = get(errors, "paymentMethod");
     const paymentMethodError = get(errors, "paymentMethod");
 
 
+    const paymentMethodOnChange = (event: React.ChangeEvent<HTMLInputElement>, item: CheckoutPaymentMethod) => {
+
+        selectPaymentTitle.current = item.title;
+        onPaymentMethodChange(event.target.value);
+                                        
+    };
 
 
     useImperativeHandle(ref, () => {
     useImperativeHandle(ref, () => {
         return {
         return {
@@ -83,10 +85,11 @@ export function PaymentMethodCheckout({
                 }
                 }
             });
             });
         }
         }
-        console.log('default payment effect ===================',defaultValue);
+  
+        selectPaymentTitle.current = title;
+      
         reset({ paymentMethod: defaultValue });
         reset({ paymentMethod: defaultValue });
         
         
-        // onPaymentMethodChange(defaultValue,title);
     }, [paymentMethods,defaultPaymentMethod]);
     }, [paymentMethods,defaultPaymentMethod]);
 
 
     
     
@@ -117,9 +120,7 @@ export function PaymentMethodCheckout({
                                     {...register('paymentMethod', {
                                     {...register('paymentMethod', {
                                         required: 'Please select a payment method.',
                                         required: 'Please select a payment method.',
                                         onChange: (event) => {
                                         onChange: (event) => {
-                                            console.log(event.target.value);
-                                            selectPaymentTitle.current = item.title;
-                                            onPaymentMethodChange(event.target.value,item.title);
+                                            paymentMethodOnChange(event, item);
                                         },
                                         },
                                     })}
                                     })}
                                 />
                                 />

+ 19 - 23
src/app/(checkout)/checkout/_components/ShippingMethodCheckout.tsx

@@ -1,13 +1,13 @@
 "use client";
 "use client";
 
 
-import clsx from "clsx";
-import { use, useEffect, useMemo, useState, useImperativeHandle, Ref  } from "react";
+
+import { useEffect, useImperativeHandle, Ref  } from "react";
 import { useForm, get } from "react-hook-form";
 import { useForm, get } from "react-hook-form";
-import {useCheckoutShippingMethod} from  "@/utils/hooks/useCheckoutShippingMethod";
+
 import {CheckoutShippingRate} from "@/types/checkout/type";
 import {CheckoutShippingRate} from "@/types/checkout/type";
-import CheckoutShippingMethodLoading from "./CheckoutShippingMethodLoading";
+
 import { useAppSelector } from "@/store/hooks";
 import { useAppSelector } from "@/store/hooks";
-import { useCustomToast } from "@/utils/hooks/useToast";
+
 
 
 
 
 /**
 /**
@@ -25,23 +25,19 @@ export function ShippingMethodCheckout({
     ref,
     ref,
     errorMsg,
     errorMsg,
     shipMethods,
     shipMethods,
+    selectedShippingRate,
     onShipMethodChange
     onShipMethodChange
 }: {
 }: {
     ref: Ref<RefShippingMethodsHandle>;
     ref: Ref<RefShippingMethodsHandle>;
     errorMsg: string | null;
     errorMsg: string | null;
     shipMethods: CheckoutShippingRate[];
     shipMethods: CheckoutShippingRate[];
+    selectedShippingRate: string | null;
     onShipMethodChange: (value: string) => void;
     onShipMethodChange: (value: string) => void;
 }) {
 }) {
-    const { showToast } = useCustomToast();
 
 
 
 
-    const {cart,loading} = useAppSelector((state) => state.cartDetail);
-    const selectedShippingRate = cart?.selectedShippingRate ?? '';
-    console.log('selectedShippingRate ============================================ ', selectedShippingRate);
-
     const {
     const {
         register,
         register,
-        handleSubmit,
         reset,
         reset,
         getValues,
         getValues,
         trigger,
         trigger,
@@ -69,29 +65,29 @@ export function ShippingMethodCheckout({
     },[]);
     },[]);
     // 根据cartDetail 的loading判断,是true 返回,是false时才往下执行
     // 根据cartDetail 的loading判断,是true 返回,是false时才往下执行
     useEffect(() => { 
     useEffect(() => { 
-        if(shipMethods.length > 0 && !loading) {
+        if(shipMethods.length > 0 ) {
             let defaultValue = '';
             let defaultValue = '';
-            let findItem = shipMethods.find((item) => item.method === selectedShippingRate);
-            if(findItem !== undefined) {
-                defaultValue = selectedShippingRate;
-            } else {
+            let findItem: CheckoutShippingRate | undefined;
+            if(selectedShippingRate) {
+                findItem = shipMethods.find((item) => item.method === selectedShippingRate);
+                if(findItem !== undefined) {
+                    defaultValue = selectedShippingRate;
+                }
+            }
+            if(!defaultValue) {
                 findItem = shipMethods.find((item) => item.price === 0) || shipMethods[0];
                 findItem = shipMethods.find((item) => item.price === 0) || shipMethods[0];
-                // shipMethods.forEach((method) => {
-                //     if(method.price === 0) {
-                //         defaultValue = method.method;
-                //     }
-                // });
                 defaultValue = findItem.method;
                 defaultValue = findItem.method;
             }
             }
 
 
-            console.log('ShippingMethodCheckout' + ' useEffect ++++++++++++++++++++++++++++ ', shipMethods,selectedShippingRate);
+
+            console.log('ShippingMethodCheckout' + ' useEffect ++++++++++++++++++++++++++++ ', defaultValue,selectedShippingRate);
             reset({ shippingMethod: defaultValue });
             reset({ shippingMethod: defaultValue });
             if(defaultValue !== selectedShippingRate) {
             if(defaultValue !== selectedShippingRate) {
                 onShipMethodChange(defaultValue);
                 onShipMethodChange(defaultValue);
             }
             }
         }
         }
         
         
-    }, [shipMethods,selectedShippingRate,loading]);
+    }, [shipMethods,selectedShippingRate]);
     
     
     return (
     return (
         <form name="shipping method" className="w-full mt-3">
         <form name="shipping method" className="w-full mt-3">

+ 6 - 5
src/app/(checkout)/checkout/page.tsx

@@ -21,20 +21,21 @@ export const dynamic = "force-dynamic";
 
 
 export default async function CheckoutPage() {
 export default async function CheckoutPage() {
 
 
-    const session = await getServerSession(authOptions);
+    const session = await getServerSession(authOptions); // 游客是null
 
 
     const {data: countriesData} = await fullCacheGraphqlRequest<{
     const {data: countriesData} = await fullCacheGraphqlRequest<{
         countries: Country[] 
         countries: Country[] 
     }>(GET_COUNTRIES);
     }>(GET_COUNTRIES);
 
 
-    const cartDetails = await authorizationGraphqlMutate<GetCartItemData>(GET_CART_ITEM,{},'GetCartItem');
-
-    if(cartDetails.data === null || cartDetails.data.createReadCart === null) {
+    const cartDetailsRes = await authorizationGraphqlMutate<GetCartItemData>(GET_CART_ITEM,{},'GetCartItem');
+    if(cartDetailsRes.data === null || cartDetailsRes.data.createReadCart === null) {
         redirect('/', RedirectType.replace);
         redirect('/', RedirectType.replace);
     }
     }
+    const cartDetails = cartDetailsRes.data.createReadCart.readCart;
     return (
     return (
         <>
         <>
-            <CheckoutWrapper 
+            <CheckoutWrapper key={cartDetails.id}
+                cartDetailData={cartDetails}
                 loginEmail={session?.user?.email || ""}
                 loginEmail={session?.user?.email || ""}
                 countries={countriesData?.countries || []}
                 countries={countriesData?.countries || []}
             />
             />

+ 0 - 163
src/utils/hooks/useCheckout.ts

@@ -1,163 +0,0 @@
-import { useMutation } from "@apollo/client/react";
-import { useRouter } from "next/navigation";
-import { useCustomToast } from "./useToast";
-import { useDispatch } from "react-redux";
-import {
-  clearCart,
-} from "@/store/slices/cart-slice";
-import { getCartToken } from "@utils/getCartToken";
-
-import { getCookie, setCookie } from "@/utils/cookie-tools";
-import { useGuestCartToken } from "./useGuestCartToken";
-import { ORDER_ID, IS_GUEST } from "@utils/constants";
-import { useCartDetail } from "./useCartDetail";
-import {
-  CREATE_CHECKOUT_ADDRESS,
-  CREATE_CHECKOUT_ORDER,
-  CREATE_CHECKOUT_PAYMENT_METHODS,
-  CREATE_CHECKOUT_SHIPPING_METHODS,
-  GET_CHECKOUT_ADDRESSES,
-} from "@/graphql";
-import { CreateCheckoutShippingMethodData, CreateCheckoutPaymentMethodResponse, CreateCheckoutOrderData } from "@/types/checkout/type";
-
-export const useCheckout = () => {
-  const router = useRouter();
-  const { resetGuestToken } = useGuestCartToken();
-  const { showToast } = useCustomToast();
-  const dispatch = useDispatch();
-  const { getCartDetail } = useCartDetail();
-
-  const handleMutationError = (error: any) => {
-    showToast(error?.message || "An error occurred", "danger");
-  };
-
-  const [saveAddressToCheckout, { loading: isLoadingToSave }] = useMutation(
-    CREATE_CHECKOUT_ADDRESS,
-    {
-      refetchQueries: [{ query: GET_CHECKOUT_ADDRESSES }],
-      awaitRefetchQueries: true,
-      onCompleted: () => {
-        showToast("Address saved successfully", "success");
-        router.replace("/checkout?step=shipping");
-      },
-      onError: handleMutationError,
-    },
-  );
-
-  const saveCheckoutAddress = async (input: any) => {
-    const token = getCartToken();
-    await saveAddressToCheckout({
-      variables: {
-        token: token || "",
-        ...input,
-      },
-    });
-  };
-
-  const [saveShipping, { loading: isSaving }] = useMutation<CreateCheckoutShippingMethodData>(
-    CREATE_CHECKOUT_SHIPPING_METHODS,
-    {
-      onCompleted: (response) => {
-        const responseData =
-          response?.createCheckoutShippingMethod?.checkoutShippingMethod;
-        if (responseData?.success) {
-          getCartDetail();
-          showToast("Shipping method saved successfully", "success");
-          router.replace("/checkout?step=payment");
-        } else {
-          showToast(
-            responseData?.message || "Failed to save shipping method",
-            "warning",
-          );
-        }
-      },
-      onError: handleMutationError,
-    },
-  );
-
-  const saveCheckoutShipping = async (shippingMethod: string) => {
-    const token = getCartToken();
-    await saveShipping({
-      variables: {
-        token: token || "",
-        shippingMethod,
-      },
-    });
-  };
-
-  const [savePayment, { loading: isPaymentLoading }] = useMutation<CreateCheckoutPaymentMethodResponse>(
-    CREATE_CHECKOUT_PAYMENT_METHODS,
-    {
-      onCompleted: (response) => {
-        const responseData =
-          response?.createCheckoutPaymentMethod?.checkoutPaymentMethod;
-        if (responseData?.success) {
-          showToast("Payment method saved successfully", "success");
-          router.replace("/checkout?step=review");
-        } else {
-          showToast(
-            responseData?.message || "Failed to save payment method",
-            "warning",
-          );
-        }
-      },
-      onError: handleMutationError,
-    },
-  );
-
-  const saveCheckoutPayment = async (paymentMethod: string) => {
-    const token = getCartToken();
-    await savePayment({
-      variables: {
-        token: token || "",
-        paymentMethod,
-      },
-    });
-  };
-
-  const [placeOrder, { loading: isPlaceOrder }] = useMutation<CreateCheckoutOrderData>(
-    CREATE_CHECKOUT_ORDER,
-    {
-      onCompleted: (response) => {
-        const responseData = response?.createCheckoutOrder?.checkoutOrder;
-        if (responseData) {
-          showToast("Order placed successfully!", "success");
-          setCookie(ORDER_ID, responseData?.orderId);
-          dispatch(clearCart());
-          router.replace("/success");
-        } else {
-          showToast(
-            "Failed to place order",
-            "warning",
-          );
-        }
-      },
-      onError: handleMutationError,
-    },
-  );
-
-  const savePlaceOrder = async () => {
-    const token = getCartToken();
-    await placeOrder({
-      variables: {
-        token: token || "",
-      },
-    });
-
-    const isGuest = getCookie(IS_GUEST);
-    if (isGuest === "true") {
-      await resetGuestToken();
-    }
-  };
-
-  return {
-    isLoadingToSave,
-    saveCheckoutAddress,
-    isSaving,
-    saveCheckoutShipping,
-    isPaymentLoading,
-    saveCheckoutPayment,
-    isPlaceOrder,
-    savePlaceOrder,
-  };
-};