Просмотр исходного кода

结账页地址板块 -- v4 国家与电话区号不一致时的联动问题

fogwind 1 день назад
Родитель
Сommit
372f642b28

+ 3 - 0
src/app/(checkout)/checkout/_components/BillingAddressCheckout.tsx

@@ -135,6 +135,9 @@ export default function BillingAddressCheckout ({
             let phone = preBillingPhoneNum;
             if(codePart) {
                 phone = preBillingPhoneNum.replace(codePart[0], '');
+            }
+             if(codePart && newPhoneCode !== codePart[0].trim()) {
+                return;
             }
             setValue('billingPhoneNumber',formatFinalPhoneValue(newPhoneCode, phone))
         }

+ 19 - 85
src/app/(checkout)/checkout/_components/CheckoutWrapper.tsx

@@ -1,25 +1,26 @@
 "use client";
 
 import {useState, useEffect} from "react";
-import {useApolloClient} from "@apollo/client/react";
-import { GET_CHECKOUT_ADDRESSES,CREATE_CHECKOUT_ADDRESS } from "@/graphql";
+
 import { LoadingSpinner } from "@components/common/LoadingSpinner";
 // import { useForm, SubmitHandler } from "react-hook-form"
 import { useForm, FormProvider } from "react-hook-form"
-import {normalizePhoneForForm} from "@/utils/phoneNumberTools";
+
 import { 
-    CheckoutAddressNode, 
     Country,
     ShipAddressFormData,
     BillAddressFormData,
-    FullAddressFormData
+    FullAddressFormData,
+    CreateCheckoutAddressVariables
 } from "@/types/checkout/type";
 import AddressResultDisplay from "./AddressResultDisplay";
 import AddressResultDisplayLoading from "./AddressResultDisplayLoading";
 import CheckoutAddressModal from "./CheckoutAddressModal";
 import ShippingAddressCheckout from "./ShippingAddressCheckout";
 import BillingAddressCheckout from "./BillingAddressCheckout";
+// import CheckoutShippingMethod from "./CheckoutShippingMethod";
 import { useCustomToast } from "@/utils/hooks/useToast";
+import {useCheckoutAddress} from "@/utils/hooks/useCheckoutAddress"
 /***
  * 不用useForShipping字段了
  * 以shipping address 为准,根据shippingaddress 设置billing address
@@ -29,7 +30,7 @@ import { useCustomToast } from "@/utils/hooks/useToast";
 /**
  * 生成createCheckoutAddress接口的参数
  */
-function generateSaveCheckoutAddressParam(formData: FullAddressFormData) {
+function generateSaveCheckoutAddressParam(formData: FullAddressFormData):CreateCheckoutAddressVariables {
     const formDataShippingAddress:ShipAddressFormData = {
         shippingAddressId: formData.shippingAddressId,
         shippingEmail: formData.shippingEmail,
@@ -83,7 +84,7 @@ function generateSaveCheckoutAddressParam(formData: FullAddressFormData) {
 /**
  * 对比shipping address与billing address的数据是否完全相同
  */
-function addressIsSame(billAddress: BillAddressFormData | null, shipAddress: ShipAddressFormData | null) {
+/*function addressIsSame(billAddress: BillAddressFormData | null, shipAddress: ShipAddressFormData | null) {
     if(billAddress === null && shipAddress === null) {
         return true;
     } else if(billAddress !== null && shipAddress !== null) {
@@ -102,8 +103,7 @@ function addressIsSame(billAddress: BillAddressFormData | null, shipAddress: Shi
     } else {
         return false;
     }
-}
-
+}*/
 export default function CheckoutWrapper({
     loginEmail,
     countries
@@ -112,7 +112,7 @@ export default function CheckoutWrapper({
     countries: Country[]
 }) {
     const { showToast } = useCustomToast();
-    const apolloClient = useApolloClient();
+    const { getCheckoutAddress, saveCheckoutAddress } = useCheckoutAddress(loginEmail);
 
 
     // useForm() 只会在组件初始化时读取一次 defaultValues
@@ -128,75 +128,6 @@ export default function CheckoutWrapper({
 
     const [addressSaving, setAddressSaving] = useState(false);
 
-    // 获取checkout address
-    function getCheckoutAddress(
-        resolveCallback: (shippingAddress:ShipAddressFormData, billingAddress: BillAddressFormData) => void = () => {}, 
-        rejectCallback: () => void = ()=> {}
-    ) {
-        return apolloClient.query({
-            query: GET_CHECKOUT_ADDRESSES,
-            fetchPolicy: "no-cache",
-            context: {
-                fetchOptions: {
-                    cache: 'no-store',
-                },
-        
-            }
-        }).then((res) => {
-            const address =  res.data?.collectionGetCheckoutAddresses?.edges?.map(
-                    (edge: { node: CheckoutAddressNode }) => edge.node
-                ) || [];
-            const billAddress = address.find((a: CheckoutAddressNode) => a?.addressType === "cart_billing") || null;
-            const shipAddress = address.find((a: CheckoutAddressNode) => a?.addressType === "cart_shipping") || null;
-            const shipCountry = shipAddress?.country || "US";
-            const billCountry = billAddress?.country || "US";
-
-            const defaultBillAddress: BillAddressFormData = {
-                billingAddressId: billAddress?._id || "",
-                billingEmail: billAddress?.email ?? loginEmail ?? "",
-                billingFirstName: billAddress?.firstName || "",
-                billingLastName: billAddress?.lastName || "",
-                billingCompanyName: billAddress?.companyName || "",
-                billingAddress: billAddress?.address || "",
-                billingCountry: billCountry,
-                billingState: billAddress?.state || "",
-                billingCity: billAddress?.city || "",
-                billingPostcode: billAddress?.postcode || "",
-                billingPhoneNumber: normalizePhoneForForm(billAddress?.phone, billCountry) || "",
-                billingSameAsShipping: true, // 这个字段前端维护,不传给后端(bagisto是以billing address为准,但是运营要以shipping address为准,所以前端自己维护billing address与shipping address 同步)
-            };
-
-            const defaultShipAddress: ShipAddressFormData = {
-                shippingAddressId: shipAddress?._id || "",
-                shippingEmail: shipAddress?.email ?? loginEmail ?? "",
-                shippingFirstName: shipAddress?.firstName || "",
-                shippingLastName: shipAddress?.lastName || "",
-                shippingCompanyName: shipAddress?.companyName || "",
-                shippingAddress: shipAddress?.address || "",
-                shippingCountry: shipCountry,
-                shippingState: shipAddress?.state || "",
-                shippingCity: shipAddress?.city || "",
-                shippingPostcode: shipAddress?.postcode || "",
-                shippingPhoneNumber: normalizePhoneForForm(shipAddress?.phone, shipCountry) || "",
-            };
-
-            const billSameAsShip = addressIsSame(defaultBillAddress, defaultShipAddress);
-            defaultBillAddress.billingSameAsShipping = billSameAsShip;
-            console.log('GET_CHECKOUT_ADDRESSES res ---- ',defaultShipAddress,defaultBillAddress);
-            resolveCallback(defaultShipAddress,defaultBillAddress);
-            return {
-                shippingAddress: defaultShipAddress,
-                billingAddress: defaultBillAddress
-            }
-     
-        }).catch((err) => {
-            console.error('GET_CHECKOUT_ADDRESSES error ---- ',err);
-            // setLoadingData(false);
-            rejectCallback();
-            return null;
-        });
-    }
-
     // 开发环境会执行两次 https://zh-hans.react.dev/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development
     useEffect(() => { 
         getCheckoutAddress((shippingAddress,billingAddress)=> {
@@ -240,10 +171,8 @@ export default function CheckoutWrapper({
         const saveAddressParam = generateSaveCheckoutAddressParam(formData);
         setAddressSaving(true);
         try {
-            const res = await apolloClient.mutate({
-                mutation: CREATE_CHECKOUT_ADDRESS,
-                variables:saveAddressParam,
-            });
+
+            const res = await saveCheckoutAddress(saveAddressParam);
             console.log('CREATE_CHECKOUT_ADDRESS res ====== ',res);
             const queryAddressRes = await getCheckoutAddress();
             if(queryAddressRes !== null) {
@@ -291,7 +220,8 @@ export default function CheckoutWrapper({
     //     });
     // }
     return (
-        <section className="flex flex-col items-start justify-between lg:flex-row lg:justify-between">
+        <section className="w-full">
+            <div>
             {/* <button className="w-25 h-8 bg-amber-500 flex items-center justify-center" onClick={tt}>test</button> */}
                     {loadingData ? <AddressResultDisplayLoading /> 
                     :<AddressResultDisplay shippingAddress={serverShippingAddress} 
@@ -318,8 +248,12 @@ export default function CheckoutWrapper({
                             </button>
                         }
                     />
+            </div>
+            <div className="mt-6 box-border px-4">
+                {/* <CheckoutShippingMethod /> */}
+            </div>
            
-        </section>
+        </section> 
         
     );
 };

+ 0 - 73
src/app/(checkout)/checkout/_components/ProceedToCheckout.tsx

@@ -1,73 +0,0 @@
-"use client";
-import LoadingDots from "@components/common/icons/LoadingDots";
-import clsx from "clsx";
-
-function SubmitButton({
-  availableForSale,
-  buttonName,
-  className,
-  pending,
-}: {
-  availableForSale: boolean;
-  buttonName: string;
-  className?: string;
-  pending: boolean;
-}) {
-  const buttonClasses =
-    "relative text-base w-fit cursor-pointer rounded-full px-8 py-3 font-bold border-white items-center justify-center bg-blue-600 tracking-wide text-white";
-  const disabledClasses = "cursor-wait opacity-60 hover:opacity-60";
-
-  if (!availableForSale) {
-    return (
-      <button 
-        type="button"
-        aria-disabled 
-        disabled
-        className={clsx(buttonClasses, disabledClasses)}
-      >
-        Processing...
-      </button>
-    );
-  }
-
-  return (
-    <button
-      type="submit"
-      disabled={pending}
-      aria-disabled={pending}
-      aria-label="Proceed to checkout"
-      className={clsx(
-        buttonClasses,
-        {
-          "hover:opacity-90": !pending,
-          [disabledClasses]: pending,
-        },
-        className,
-      )}
-    >
-      <div className="absolute left-0 ml-4">
-        {pending && <LoadingDots className="mb-3 bg-white" />}
-      </div>
-      {buttonName}
-    </button>
-  );
-}
-
-export function ProceedToCheckout({
-  buttonName,
-  className,
-  pending = false,
-}: {
-  buttonName: string;
-  className?: string;
-  pending: boolean;
-}) {
-  return (
-    <SubmitButton
-      availableForSale={!pending}
-      buttonName={buttonName}
-      className={className}
-      pending={pending}
-    />
-  );
-}

+ 3 - 0
src/app/(checkout)/checkout/_components/ShippingAddressCheckout.tsx

@@ -81,6 +81,9 @@ export default function ShippingAddressCheckout ({
             if(codePart) {
                 phone = preShippingPhoneNum.replace(codePart[0], '');
             }
+            if(codePart && newPhoneCode !== codePart[0].trim()) {
+                return;
+            }
             setValue('shippingPhoneNumber',formatFinalPhoneValue(newPhoneCode, phone))
         }
         

+ 7 - 3
src/components/theme/ui/PhoneNumberInput/PhoneNumberInput.tsx

@@ -6,7 +6,8 @@ import clsx from "clsx";
 import {
     COUNTRY_PHONECODE,
     formatFinalPhoneValue,
-    getPhoneCodeBycountryCode
+    getPhoneCodeBycountryCode,
+    getCountryCodeByPhoneCode
 } from "./phoneCodeMetaData";
 import { IS_VALID_PHONECODE } from "@utils/constants";
 
@@ -61,6 +62,7 @@ function PhoneNumberInput({
     onHasUserSelectedPhoneCode,
     hasUserSelectedPhoneCode
 }: PhoneNumberInputProps) {
+    console.log(' value ===========', value);
     // const DEFAULT_COUNTRY_CODE = "US";
     const phoneCodeList = COUNTRY_PHONECODE;
 
@@ -69,8 +71,10 @@ function PhoneNumberInput({
     }, [value]);
 
 
-    const [selectedPhoneCode, setSelectedPhoneCode] = useState<string | null>(null);
-    const [selectedCountryForPhoneCode, setSelectedCountryForPhoneCode] = useState<string | null>(null);
+    const [selectedPhoneCode, setSelectedPhoneCode] = useState<string | null>(phoneCode);
+    const [selectedCountryForPhoneCode, setSelectedCountryForPhoneCode] = useState<string | null>(() => {
+        return getCountryCodeByPhoneCode(phoneCode);
+    });
     
     const displayPhonecode = useMemo(() => {
         return selectedPhoneCode ?? getPhoneCodeBycountryCode(countryCode);

+ 4 - 0
src/types/checkout/type.ts

@@ -162,6 +162,8 @@ export interface CheckoutShippingRate {
   method: string;
   price: number;
   label: string;
+  _id: string;
+  formattedPrice: string;
 }
 
 export interface GetCheckoutShippingRatesData {
@@ -255,6 +257,7 @@ export interface CreateCheckoutAddressData {
 }
 export interface CreateCheckoutAddressVariables {
   // Billing
+  billingAddressId?: string | number;
   billingFirstName: string;
   billingLastName: string;
   billingEmail: string;
@@ -270,6 +273,7 @@ export interface CreateCheckoutAddressVariables {
   useForShipping?: boolean;
 
   // Shipping (optional when useForShipping === true)
+  shippingAddressId?: string | number;
   shippingFirstName?: string;
   shippingLastName?: string;
   shippingEmail?: string;

+ 112 - 26
src/utils/hooks/useCheckoutAddress.ts

@@ -1,36 +1,122 @@
 "use client";
 
-import { GET_CHECKOUT_ADDRESSES } from "@/graphql";
-import { CheckoutAddressNode } from "@/types/checkout/type";
-import { useQuery } from "@apollo/client/react";
-
-export const useCheckoutAddress = () => {
-    const { data, loading, error } = useQuery(
-        GET_CHECKOUT_ADDRESSES,
-        {
+import { GET_CHECKOUT_ADDRESSES, CREATE_CHECKOUT_ADDRESS } from "@/graphql";
+import {useApolloClient} from "@apollo/client/react";
+import { 
+    CheckoutAddressNode, 
+    ShipAddressFormData,
+    BillAddressFormData,
+    CreateCheckoutAddressVariables
+} from "@/types/checkout/type";
+import {normalizePhoneForForm} from "@/utils/phoneNumberTools";
+/**
+ * 对比shipping address与billing address的数据是否完全相同
+ */
+export function addressIsSame(billAddress: BillAddressFormData | null, shipAddress: ShipAddressFormData | null) {
+    if(billAddress === null && shipAddress === null) {
+        return true;
+    } else if(billAddress !== null && shipAddress !== null) {
+        return (
+           billAddress.billingEmail === shipAddress.shippingEmail &&
+           billAddress.billingFirstName === shipAddress.shippingFirstName &&
+           billAddress.billingLastName === shipAddress.shippingLastName &&
+           billAddress.billingCompanyName === shipAddress.shippingCompanyName &&
+           billAddress.billingAddress === shipAddress.shippingAddress &&
+           billAddress.billingCity === shipAddress.shippingCity &&
+           billAddress.billingState === shipAddress.shippingState &&
+           billAddress.billingCountry === shipAddress.shippingCountry &&
+           billAddress.billingPostcode === shipAddress.shippingPostcode &&
+           billAddress.billingPhoneNumber === shipAddress.shippingPhoneNumber 
+        );
+    } else {
+        return false;
+    }
+}
+
+
+export function useCheckoutAddress(loginEmail: string) {
+    const apolloClient = useApolloClient();
+
+
+    const getCheckoutAddress = (
+        resolveCallback: (shippingAddress:ShipAddressFormData, billingAddress: BillAddressFormData) => void = () => {}, 
+        rejectCallback: () => void = ()=> {}
+    ) => {
+        return apolloClient.query({
+            query: GET_CHECKOUT_ADDRESSES,
             fetchPolicy: "no-cache",
             context: {
-        
                 fetchOptions: {
                     cache: 'no-store',
-                    // next: { 
-                    //     revalidate: 60 
-                    // }, // Revalidate every 60 seconds
                 },
         
             }
-        }
-    );
-    const address =  data?.collectionGetCheckoutAddresses?.edges?.map(
-            (edge: { node: CheckoutAddressNode }) => edge.node
-        ) || [];
-    const billingAddress = address.find((a: CheckoutAddressNode) => a?.addressType === "cart_billing");
-    const shippingAddress = address.find((a: CheckoutAddressNode) => a?.addressType === "cart_shipping");
-
-    return {
-        billingAddress,
-        shippingAddress, 
-        loading, 
-        error
+        }).then((res) => {
+            const address =  res.data?.collectionGetCheckoutAddresses?.edges?.map(
+                    (edge: { node: CheckoutAddressNode }) => edge.node
+                ) || [];
+            const billAddress = address.find((a: CheckoutAddressNode) => a?.addressType === "cart_billing") || null;
+            const shipAddress = address.find((a: CheckoutAddressNode) => a?.addressType === "cart_shipping") || null;
+            const shipCountry = shipAddress?.country || "US";
+            const billCountry = billAddress?.country || "US";
+
+            const defaultBillAddress: BillAddressFormData = {
+                billingAddressId: billAddress?._id || "",
+                billingEmail: billAddress?.email ?? loginEmail ?? "",
+                billingFirstName: billAddress?.firstName || "",
+                billingLastName: billAddress?.lastName || "",
+                billingCompanyName: billAddress?.companyName || "",
+                billingAddress: billAddress?.address || "",
+                billingCountry: billCountry,
+                billingState: billAddress?.state || "",
+                billingCity: billAddress?.city || "",
+                billingPostcode: billAddress?.postcode || "",
+                billingPhoneNumber: normalizePhoneForForm(billAddress?.phone, billCountry) || "",
+                billingSameAsShipping: true, // 这个字段前端维护,不传给后端(bagisto是以billing address为准,但是运营要以shipping address为准,所以前端自己维护billing address与shipping address 同步)
+            };
+
+            const defaultShipAddress: ShipAddressFormData = {
+                shippingAddressId: shipAddress?._id || "",
+                shippingEmail: shipAddress?.email ?? loginEmail ?? "",
+                shippingFirstName: shipAddress?.firstName || "",
+                shippingLastName: shipAddress?.lastName || "",
+                shippingCompanyName: shipAddress?.companyName || "",
+                shippingAddress: shipAddress?.address || "",
+                shippingCountry: shipCountry,
+                shippingState: shipAddress?.state || "",
+                shippingCity: shipAddress?.city || "",
+                shippingPostcode: shipAddress?.postcode || "",
+                shippingPhoneNumber: normalizePhoneForForm(shipAddress?.phone, shipCountry) || "",
+            };
+
+            const billSameAsShip = addressIsSame(defaultBillAddress, defaultShipAddress);
+            defaultBillAddress.billingSameAsShipping = billSameAsShip;
+            console.log('GET_CHECKOUT_ADDRESSES res ---- ',defaultShipAddress,defaultBillAddress);
+            resolveCallback(defaultShipAddress,defaultBillAddress);
+            return {
+                shippingAddress: defaultShipAddress,
+                billingAddress: defaultBillAddress
+            }
+        
+        }).catch((err) => {
+            console.error('GET_CHECKOUT_ADDRESSES error ---- ',err);
+            rejectCallback();
+            return null;
+        });
     };
-};
+
+    const saveCheckoutAddress = (saveAddressParam: CreateCheckoutAddressVariables) => {
+        return apolloClient.mutate({
+            mutation: CREATE_CHECKOUT_ADDRESS,
+            variables:saveAddressParam,
+        });
+    }
+
+  return {
+    getCheckoutAddress,
+    saveCheckoutAddress,
+  };
+}
+
+
+