"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 } 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 { useCustomToast } from "@/utils/hooks/useToast"; /*** * 不用useForShipping字段了 * 以shipping address 为准,根据shippingaddress 设置billing address * 保存完地址之后要重新获取运输方式和支付方式 */ /** * 生成createCheckoutAddress接口的参数 */ function generateSaveCheckoutAddressParam(formData: FullAddressFormData) { const formDataShippingAddress:ShipAddressFormData = { shippingAddressId: formData.shippingAddressId, shippingEmail: formData.shippingEmail, shippingFirstName: formData.shippingFirstName, shippingLastName: formData.shippingLastName, shippingCompanyName: formData.shippingCompanyName, shippingAddress: formData.shippingAddress, shippingCountry: formData.shippingCountry, shippingState: formData.shippingState, shippingCity: formData.shippingCity, shippingPostcode: formData.shippingPostcode, shippingPhoneNumber: formData.shippingPhoneNumber, }; let formDataBillingAddress = { billingAddressId: formData.billingAddressId, billingEmail: formData.billingEmail, billingFirstName: formData.billingFirstName, billingLastName: formData.billingLastName, billingCompanyName : formData.billingCompanyName, billingAddress: formData.billingAddress, billingCountry: formData.billingCountry, billingState: formData.billingState, billingCity: formData.billingCity, billingPostcode: formData.billingPostcode, billingPhoneNumber: formData.billingPhoneNumber, useForShipping: false, }; if(formData.billingSameAsShipping) { formDataBillingAddress = { billingAddressId: formData.billingAddressId, billingEmail: formDataShippingAddress.shippingEmail, billingFirstName: formDataShippingAddress.shippingFirstName, billingLastName: formDataShippingAddress.shippingLastName, billingCompanyName : formDataShippingAddress.shippingCompanyName, billingAddress: formDataShippingAddress.shippingAddress, billingCountry: formDataShippingAddress.shippingCountry, billingState: formDataShippingAddress.shippingState, billingCity: formDataShippingAddress.shippingCity, billingPostcode: formDataShippingAddress.shippingPostcode, billingPhoneNumber: formDataShippingAddress.shippingPhoneNumber, useForShipping: true, } } return { ...formDataShippingAddress, ...formDataBillingAddress }; } /** * 对比shipping address与billing address的数据是否完全相同 */ 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 default function CheckoutWrapper({ loginEmail, countries }: { loginEmail: string; countries: Country[] }) { const { showToast } = useCustomToast(); const apolloClient = useApolloClient(); // useForm() 只会在组件初始化时读取一次 defaultValues const addressForm = useForm({ mode: "onBlur", // 或 "onChange" reValidateMode: "onChange", }); const [addressModalOpen, setAddressModalOpen] = useState(false); const [loadingData, setLoadingData] = useState(true); const [serverShippingAddress, setServerShippingAddress] = useState(null); const [serverBillingAddress, setServerBillingAddress] = useState(null); 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)=> { addressForm.reset({ ...shippingAddress, ...billingAddress, }); setServerShippingAddress({ ...shippingAddress }); setServerBillingAddress({ ...billingAddress }); setLoadingData(false); }, ()=> { setLoadingData(false); }); }, []); const openAddressModal = () => { setAddressModalOpen(true); if(serverShippingAddress && serverBillingAddress) { addressForm.reset({ ...serverShippingAddress, ...serverBillingAddress, }); } }; const closeAddressModal = (e:boolean) => { setAddressModalOpen(e); // 重置表单 addressForm.reset(); }; const addressFormOnSubmit = async (formData: FullAddressFormData) => { // console.log('addressFormOnSubmit ---- ',formData); return; // 保存完地址之后还要重新请求地址,把保存后的地址id同步到表单里;还需要获取运输方式 const saveAddressParam = generateSaveCheckoutAddressParam(formData); setAddressSaving(true); try { const res = await apolloClient.mutate({ mutation: CREATE_CHECKOUT_ADDRESS, variables:saveAddressParam, }); console.log('CREATE_CHECKOUT_ADDRESS res ====== ',res); const queryAddressRes = await getCheckoutAddress(); if(queryAddressRes !== null) { addressForm.resetField('shippingAddressId',{ defaultValue: queryAddressRes.shippingAddress.shippingAddressId }); addressForm.resetField('billingAddressId',{ defaultValue: queryAddressRes.billingAddress.billingAddressId }); setServerShippingAddress({ ...queryAddressRes.shippingAddress }); setServerBillingAddress({ ...queryAddressRes.billingAddress }); } setAddressModalOpen(false); } catch(err) { // 错误处理 console.error("save address error", err); showToast('Save address failed. Please try again.', 'danger'); } finally { setAddressSaving(false); } }; // function tt() { // const arr = [ // ['US','3803800217'], // ['CH','+4186043315'], // ['US','334-208-6177'], // ['AU','+61 404103617'], // ['US','1-9166705105'], // ['CA','819 384 3221'], // ['US','(409) 239-9482'], // ['US','1-(651) 421-2762'], // ['CH', '1-792930242'], // ['US', '+1 4570438892'], // ['US', '1-+1 (404) 276-1068'], // ['CH', '+49 015164340021'], // 国家与phonecode不一致情况 // ]; // arr.forEach((item) => { // normalizePhoneForForm(item[1], item[0]); // }); // } return (
{/* */} {loadingData ? :}
} footer={ } />
); };