ソースを参照

支付结果页样式

fogwind 3 日 前
コミット
7ae55707f3

+ 4 - 1
README.md

@@ -357,7 +357,10 @@ useEffect 只会在客户端执行,具体是在浏览器绘制后执行,服
     ---- 47 条 请求方法已经扩展是否携带token的参数,但是不再添加对于token的有效性校验,以后端接口返回的错误为依据判断有效性
 48. 游客token的创建优化(想在服务端创建写入token)--- 已完成
 49. 结账页地址模块未登录用户输入邮箱后判断是否注册过,如果注册过让用户登录;--- 已完成
-50. 注册接口,提交的phone是 +1 345345345 格式,但是保存到数据库后成了+1345345345
+50. 注册接口,提交的phone是 +1 345345345 格式,但是保存到数据库后成了+1345345345 --- 后端已处理
+51. 关于分享组件:https://chatgpt.com/share/6aa351f3-5ed0-83ea-8320-43d34d66f10a
+    -- 可以参考https://github.com/nygardk/react-share
+    -- 接着SocialShare写
 
 
 > 39,40 参考 https://chatgpt.com/share/6a4f0637-ad28-83ea-ad44-423740795054

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

@@ -323,7 +323,7 @@ export default function CheckoutWrapper({
             console.log('checkoutWrapper unmout==========================');
         }
     },[]);
-    return (<div className="w-full flex justify-center items-start mt-6 mb-30">
+    return (<div className="w-full flex max-w-300 mx-auto items-start mt-6 mb-30">
         <section className="flex-initial w-164 mr-16">
             <div className="pb-6 border-b-1">
                 <CheckoutAddress 

+ 228 - 53
src/app/(public)/paymentresult/_components/CancelOrFailure.tsx

@@ -1,13 +1,18 @@
 "use client";
 
 import {use, useEffect, useRef} from "react";
+import { redirect, RedirectType } from 'next/navigation';
+import Image from "next/image";
 import Link from "next/link";
 import type {FetchGraphqlResult} from "@/types/graphqlFetch/type";
 import type { OrderDetailsData } from "@/types/customer/type";
 import { usePlaceOrder } from "@/utils/hooks/usePlaceOrder";
-import Faqs from "./Faqs";
+import { useConfig } from "@utils/hooks/useConfig";
+import {getAddressFromOrderDetailAddressList,getProductAdditionalInfo} from "@/utils/orderDetailTools";
 import { useAppDispatch } from "@/store/hooks";
 import { fetchCartDetail } from "@/store/slices/cart-slice";
+import Faqs from "./Faqs";
+
 export default function CancelOrFailure({
     orderDetailPromise,
     paymentStatus,
@@ -18,14 +23,28 @@ export default function CancelOrFailure({
     isGuest: boolean
 }) { 
     const dispatch = useAppDispatch();
+    const {currencies} = useConfig();
     const promiseRes = use(orderDetailPromise);
     const orderDetailRes = promiseRes.data?.customerOrder ?? null;
+    if(!orderDetailRes) {
+        redirect('/', RedirectType.replace);
+    }
     const {cancelOrder} = usePlaceOrder();
     const orderId = orderDetailRes?._id;
     const orderStatus = orderDetailRes?.status;
     const cancelOrderRef = useRef(false);
+    const shippingAddress = getAddressFromOrderDetailAddressList(orderDetailRes.addresses,"order_shipping");
+    const currency = currencies.find(item => item.code === orderDetailRes.orderCurrencyCode) || null;
+    let orderCreatedAt = 'Can\'t get order date';
+    if(orderDetailRes?.createdAt) {
+        const localDate = new Date(orderDetailRes.createdAt);
+        orderCreatedAt = localDate.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
+    }
+    const productItems = orderDetailRes?.items?.edges.map(item => {
+        return item.node;
+    }) || [];
+
     useEffect(() => {
-        console.log('isGuest ---- ',isGuest);
         if(isGuest && !cancelOrderRef.current) {
             
             // 如果是游客,取消订单
@@ -42,60 +61,216 @@ export default function CancelOrFailure({
             cancelOrderRef.current = true;
         }
     }, [orderId,orderStatus]);
-    return (
-        <div className="box-border px-4 w-full">
-            {paymentStatus === 'failure' &&
-                <>
-                    <div className="w-12 h-12 mx-auto mt-9">
-                        <svg className="failure block w-full h-full" width="200" height="200" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="27635"><path d="M736 332.8L556.8 512l179.2 179.2-44.8 44.8L512 556.8l-179.2 179.2-44.8-44.8L467.2 512 288 332.8l44.8-44.8L512 467.2l179.2-179.2 44.8 44.8z" p-id="27636"></path><path d="M512 0a512 512 0 1 1 0 1024A512 512 0 0 1 512 0z m0 64a448 448 0 1 0 0 896A448 448 0 0 0 512 64z" p-id="27637"></path></svg>
-                    </div>
-                    <p className="text-ly-16 text-center mt-6">
-                        {isGuest ?
-                            "payment Failed! Your order has been cancelled."
-                        :
-                            "Payment Failed! You can try again."
-                        }
-                        
-                    </p>
-                </>
-            }
-            {paymentStatus === 'cancel' &&
-                <>
-                    <div className="w-12 h-12 mx-auto mt-9">
-                        <svg className="warning block w-full h-full" width="200" height="200" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="24135"><path d="M874.6496 512a362.6496 362.6496 0 1 0-725.3504 0 362.6496 362.6496 0 0 0 725.3504 0z m64 0a426.6496 426.6496 0 1 1-853.3504 0 426.6496 426.6496 0 0 1 853.3504 0z m-458.7008 85.3504V298.7008h64v298.6496h-64z m0 128v-64h64v64h-64z" p-id="24136"></path></svg>
-                    </div>
-                    <p className="text-ly-16 text-center mt-6">
-                        {isGuest ?
-                            "payment cancelled! Your order has been cancelled."
-                        :
-                            "Payment cancelled! You can continue to pay."
-                        }
-                        
-                    </p>
-                </>
-            }
 
-            <p className="text-ly-16 mt-4 flex justify-center">
-                <span className="px-3.75 leading-ly-28 bg-[#f0fffa]">Order #{orderId}</span>
-            </p>
-            {/* <p className="text-ly-12 text-center mt-4 leading-ly-22">
-                Hello, {orderDetailRes?.customerFirstName} <br/>
-                Thank you so much for your order !<br/>
-                We’ll get started on your order very soon!<br/>
-                You’ll receive an order confirmation email with details of your order in an hour, please be sure to check your email and confirm your order, that is very important for us to process your order.
-            </p> */}
-            {isGuest ?
-                <Link href="/" className="mt-6 flex justify-center items-center h-12 border-1 rounded-3xl text-ly-14">
-                    Continue Shopping
-                </Link>
-            :
-                <Link href={`/checkout/continuetopay?orderid=${orderId}`} className="mt-6 flex justify-center items-center h-12 border-1 rounded-3xl text-ly-14">
-                    Continue To Pay
-                </Link>
-            }
+
+
+    return (<div className="flex max-w-300 mx-auto items-start">
+        <div className="flex-initial w-164 mr-16">
+            <div className="box-border w-full">
+                {paymentStatus === 'failure' &&
+                    <>
+                        <div className="w-12 h-12 mx-auto">
+                            <svg className="failure block w-full h-full" width="200" height="200" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="27635"><path d="M736 332.8L556.8 512l179.2 179.2-44.8 44.8L512 556.8l-179.2 179.2-44.8-44.8L467.2 512 288 332.8l44.8-44.8L512 467.2l179.2-179.2 44.8 44.8z" p-id="27636"></path><path d="M512 0a512 512 0 1 1 0 1024A512 512 0 0 1 512 0z m0 64a448 448 0 1 0 0 896A448 448 0 0 0 512 64z" p-id="27637"></path></svg>
+                        </div>
+                        <p className="text-ly-16 text-center mt-4">
+                            {isGuest ?
+                                "payment Failed! Your order has been cancelled."
+                            :
+                                "Payment Failed! You can try again."
+                            }
+                            
+                        </p>
+                    </>
+                }
+                {paymentStatus === 'cancel' &&
+                    <>
+                        <div className="w-12 h-12 mx-auto">
+                            <svg className="warning block w-full h-full" width="200" height="200" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="24135"><path d="M874.6496 512a362.6496 362.6496 0 1 0-725.3504 0 362.6496 362.6496 0 0 0 725.3504 0z m64 0a426.6496 426.6496 0 1 1-853.3504 0 426.6496 426.6496 0 0 1 853.3504 0z m-458.7008 85.3504V298.7008h64v298.6496h-64z m0 128v-64h64v64h-64z" p-id="24136"></path></svg>
+                        </div>
+                        <p className="text-ly-16 text-center mt-4">
+                            {isGuest ?
+                                "payment cancelled! Your order has been cancelled."
+                            :
+                                orderStatus !=='canceled' ? "Payment cancelled! You can continue to pay." : "Order has been cancelled!"
+                            }
+                            
+                        </p>
+                    </>
+                }
+                <p className="text-ly-16 mt-4 flex justify-center">
+                    <span className="px-3.75 leading-ly-28 bg-[#f0fffa]">Order #{orderId}</span>
+                </p>
+                {isGuest || orderStatus ==='canceled' ?
+                    <Link href="/" className="mt-6 flex justify-center items-center h-12 border-1 rounded-3xl text-ly-16">
+                        Continue Shopping
+                    </Link>
+                :
+                    <Link href={`/checkout/continuetopay?orderid=${orderId}`} className="mt-6 flex justify-center items-center h-12 border-1 rounded-3xl text-ly-16">
+                        Continue To Pay
+                    </Link>
+                }
+            </div>
             <div className="box-border w-full mt-6">
                 <Faqs />
             </div>
+            <div className="box-border w-full mt-6">
+                <h4 className="text-ly-20 font-medium">Order Information</h4>
+                <div className="mt-6 flex justify-between">
+                    <div className="text-ly-14 flex-none">Ship to:</div>
+                    <div className="w-117.5 text-ly-14 leading-ly-20 text-[#666666] flex-none">
+                        {
+                            shippingAddress.firstName 
+                            + ' ' + shippingAddress.lastName 
+                            + ' ' + shippingAddress.phone
+                            + ' ' + shippingAddress.address
+                            + ' ' + shippingAddress.city
+                            + ' ' + shippingAddress.state 
+                            + ' ' + shippingAddress.postcode
+                            + ' ' + shippingAddress.country
+                        }
+                    </div>
+                </div>
+                <div className="mt-4 flex justify-between">
+                    <div className="text-ly-14 flex-none">Order number:</div>
+                    <div className="w-117.5 text-ly-14 leading-ly-20 text-[#666666] flex-none">
+                        {orderDetailRes?._id}
+                    </div>
+                </div>
+                <div className="mt-4 flex justify-between">
+                    <div className="text-ly-14 flex-none">Status:</div>
+                    <div className="w-117.5 text-ly-14 leading-ly-20 text-[#666666] flex-none capitalize">
+                        {orderDetailRes?.status}
+                    </div>
+                </div>
+                <div className="mt-4 flex justify-between">
+                    <div className="text-ly-14 flex-none">Order date:</div>
+                    <div className="w-117.5 text-ly-14 leading-ly-20 text-[#666666] flex-none">
+                        {orderCreatedAt}
+                    </div>
+                </div>
+                <div className="mt-4 flex justify-between">
+                    <div className="text-ly-14 flex-none">Shipping method:</div>
+                    <div className="w-117.5 text-ly-14 leading-ly-20 text-[#666666] flex-none">
+                        {orderDetailRes?.shippingTitle}
+                    </div>
+                </div>
+                <div className="mt-4 flex justify-between">
+                    <div className="text-ly-14 flex-none">Payment method:</div>
+                    <div className="w-117.5 text-ly-14 leading-ly-20 text-[#666666] flex-none">
+                        {orderDetailRes?.payment?.methodTitle}
+                    </div>
+                </div>
+
+            </div>
+        </div>
+
+        <div className="flex-none box-border w-120 border-1 p-4">
+            <h3 className="text-ly-20">My Bag({orderDetailRes.totalQtyOrdered})</h3>
+            <div className="box-border w-full max-h-130 overflow-y-auto scrollbar-thin scrollbar-thumb-gray-200">
+                {productItems.map((product) => {
+                    const baseImage = JSON.parse(product.baseImage);
+                    return (
+                        <div key={product.id} className="w-full flex gap-2 mt-4 pt-4 border-t-1 border-[#f0f0f0]">
+                            <div className="relative w-33 h-44 flex-none">
+                                <Image className="object-cover" fill
+                                    src={baseImage.medium_image_url}
+                                    alt={product.name} 
+                                />
+                            </div>
+                            <div className="flex-initial w-77 flex flex-col justify-between">
+                                <div>
+                                    <p className="text-ly-14 leading-ly-20 line-clamp-2">
+                                        {product.name}
+                                    </p>
+                                    {
+                                        product.additional && 
+                                        <p className="mt-2 text-ly-14 leading-ly-20 text-ly-placeholder">
+                                            {getProductAdditionalInfo(JSON.parse(product.additional))}
+                                        </p>
+                                    }
+                                    
+                                </div>
+                                <div className="flex justify-between">
+                                    <p className="text-ly-14 leading-ly-20 font-bold">
+                                        {currency && currency.symbol}{product.price}
+                                    </p>
+                                    <p className="text-ly-14 leading-ly-20">x {product.qtyOrdered}</p>
+                                </div>
+                            </div>
+                        </div>
+                    );
+                })}
+                
+            </div>
+
+            <div className="box-border w-full mt-8">
+                <div className="flex justify-between items-center mt-4 first:mt-0">
+                    <p className="text-ly-14">Subtotal</p>
+                    <p className="text-ly-14">{currency && currency.symbol}{orderDetailRes?.subTotal}</p>
+                </div>
+                {orderDetailRes?.rewardPointsEarned > 0 &&
+                    <div className="flex justify-between items-center mt-4 first:mt-0">
+                        <p className="text-ly-14">You Earn</p>
+                        <p className="text-ly-14">{orderDetailRes?.rewardPointsEarned} Reward Points</p>
+                    </div>
+                }
+                {!!orderDetailRes?.shippingAmount &&
+                    <div className="flex justify-between items-center mt-4 first:mt-0">
+                        <p className="text-ly-14">Shipping</p>
+                        <p className="text-ly-14">{currency && currency.symbol}{orderDetailRes?.shippingAmount}</p>
+                    </div>
+                }
+                {Number(orderDetailRes?.shippingInsuranceAmount) > 0 &&
+                    <div className="flex justify-between items-center mt-4 first:mt-0">
+                        <p className="text-ly-14">Insurance of Lost</p>
+                        <p className="text-ly-14">{currency && currency.symbol}{Number(orderDetailRes?.shippingInsuranceAmount)}</p>
+                    </div>
+                }
+                {!!orderDetailRes?.taxAmount &&
+                    <div className="flex justify-between items-center mt-4 first:mt-0">
+                        <p className="text-ly-14">Tax</p>
+                        <p className="text-ly-14">{currency && currency.symbol}{orderDetailRes?.taxAmount}</p>
+                    </div>
+                }
+                {(orderDetailRes?.vipPlusAmount !== '0.00' && orderDetailRes?.vipPlusAmount !== null) &&
+                    <div className="flex justify-between items-center mt-4 first:mt-0">
+                        <p className="text-ly-14">Plus Fee</p>
+                        <p className="text-ly-14">{currency && currency.symbol}{orderDetailRes?.vipPlusAmount}</p>
+                    </div>
+                }
+                {orderDetailRes?.rewardPointsAmount > 0 &&
+                    <div className="flex justify-between items-center mt-4 first:mt-0">
+                        <p className="text-ly-14">You Redeem ({orderDetailRes.rewardPointsUsed} Reward Points)</p>
+                        <p className="flex items-center h-6 px-2 bg-ly-gold text-ly-14">-{currency && currency.symbol}{orderDetailRes?.rewardPointsAmount}</p>
+                    </div>
+                }
+                {!!orderDetailRes?.discountAmount &&
+                    <div className="flex justify-between items-center mt-4 first:mt-0">
+                        <p className="text-ly-14">Discount</p>
+                        <p className="flex items-center h-6 px-2 bg-ly-gold text-ly-14">-{currency && currency.symbol}{orderDetailRes?.discountAmount}</p>
+                    </div>
+                }
+                {(orderDetailRes?.giftcardAmount !== '0.00' && orderDetailRes?.giftcardAmount !== null) &&
+                    <div className="flex justify-between items-center mt-4 first:mt-0">
+                        <p className="text-ly-14">Gift Card</p>
+                        <p className="flex items-center h-6 px-2 bg-ly-gold text-ly-14">-{currency && currency.symbol}{orderDetailRes?.giftcardAmount}</p>
+                    </div>
+                }
+                {(orderDetailRes?.vipDiscountAmount !== '0.00' && orderDetailRes?.vipDiscountAmount !== null) &&
+                    <div className="flex justify-between items-center mt-4 first:mt-0">
+                        <p className="text-ly-14">Plus Save</p>
+                        <p className="flex items-center h-6 px-2 bg-ly-gold text-ly-14">-{currency && currency.symbol}{orderDetailRes?.vipDiscountAmount}</p>
+                    </div>
+                }
+                {/* <div className="flex justify-between mt-3 first:mt-0">
+                    <p className="text-ly-12 leading-ly-20">You Earn</p>
+                    <p className="text-ly-12 leading-ly-20">600 Reward Points</p>
+                </div> */}
+                <div className="flex justify-between mt-4 first:mt-0 pt-6 border-t-1 border-[#f0f0f0]">
+                    <p className="text-ly-14">Total</p>
+                    <p className="text-ly-24 font-bold">{currency && currency.symbol}{orderDetailRes?.grandTotal}</p>
+                </div>
+            </div>
         </div>
-    );
+    </div>);
 }

ファイルの差分が大きいため隠しています
+ 10 - 6
src/app/(public)/paymentresult/_components/Faqs.tsx


ファイルの差分が大きいため隠しています
+ 74 - 90
src/app/(public)/paymentresult/_components/OrderDetailWrapper.tsx


+ 1 - 1
src/app/(public)/paymentresult/result/page.tsx

@@ -44,7 +44,7 @@ export default async function SuccessPage({searchParams}: {
 
     return (
         
-        <div className="w-full">
+        <div className="w-full mt-6 mb-30">
             {paymentResult === 'success' && 
                 <Suspense fallback={<SuccessPageSkeleton />}>
                     <OrderDetailWrapper orderDetailPromise={orderDetailPromise} 

+ 11 - 0
src/components/common/SocialShare/SocialShare.tsx

@@ -0,0 +1,11 @@
+"use client";
+
+export function SocialShare() {
+
+    return <>
+    
+    
+    </>;
+}
+
+

+ 124 - 0
src/providers/SocialShareProvider.tsx

@@ -0,0 +1,124 @@
+"use client";
+
+import Script from "next/script";
+import {
+  createContext,
+  useContext,
+  useState
+} from "react";
+
+type ShareToSocialName = "pinterest" | "facebook" | "teitter" | "X" | "x" | "whatsapp" | "email" | "copy_link";
+type SDKLoadState = "none" | "loading" | "error" | "success";
+type SDKName = "pinterest";
+interface SocialSDKState {
+  pinterest: SDKLoadState;
+}
+
+interface SocialShareContextProps {
+  loadSDKState: SocialSDKState;
+}
+
+const pinterestShareSDKURL = "https://assets.pinterest.com/js/pinit.js";
+
+
+const SocialShareContext = createContext<SocialShareContextProps|null>(null);
+
+
+export function SocialShareProvider({
+    children
+}: {
+    children:React.ReactNode;
+}){
+
+    const [loadSDKState, setLoadSDKState] = useState<SocialSDKState>({
+        pinterest: 'none', // none loading error success
+    });
+
+    const onSdkLoad = (m:SDKName) => {
+        setLoadSDKState((prev) => {
+            return {
+                ...prev,
+                [m]: 'success'
+            }
+        });
+    };
+    const onSdkError = (m:SDKName) => {
+        setLoadSDKState((prev) => {
+            return {
+                ...prev,
+                [m]: 'error'
+            }
+        });
+    };
+
+    const handlerShare = ({
+        name,
+        url,
+        title,
+        pic,
+    }:{
+        name: ShareToSocialName;
+        url: string;
+        title: string;
+        pic: string;
+    }) => {
+        var facebook = 'https://www.facebook.com/sharer/sharer.php?u={url}&t={title}&pic={pic}';
+	
+        if(name === 'pinterest') {
+            if(loadSDKState.pinterest === 'success') {
+                // window.PinUtils.pinOne({
+                //     url: url,
+                //     media: pic,
+                //     description: title,
+                // });
+
+            }
+            
+        } else if(name === 'facebook') {
+            // shareModal.simpleShare.facebook();
+            var facebook = 'https://www.facebook.com/sharer/sharer.php?u={url}&t={title}&pic={pic}';
+        } else if(name === 'teitter') {
+            var twitter = 'https://twitter.com/intent/tweet?text={title}&url={url}';
+        } else if(name === 'whatsapp') {
+            var waShareUrl = 'https://wa.me/?text=' + window.encodeURIComponent(title + url)
+            window.open(waShareUrl,'t','toolbar=0,resizable=1,status=0,width=640,height=528,left=500,top=300');
+        }else if(name === 'email') {
+            // var aDom = jQuery('<a></a>');
+            // aDom.attr('href', 'mailto:?subject=' + window.encodeURIComponent('Share Alipearlhair Product') + '&body=' + window.encodeURIComponent(shareModal.shareInfo.url));
+            // aDom[0].click();
+        } else if(name === 'copy_link') {
+            // shareModal.copyText(shareModal.shareInfo.url);
+        }
+    };
+
+    return (
+        <SocialShareContext.Provider
+            value={{
+                loadSDKState
+            }}
+        >
+            
+            <Script id="pinterest-share-sdk" strategy="lazyOnload"
+                src={pinterestShareSDKURL}
+                onLoad={() => onSdkLoad('pinterest')}
+                onError={() => {
+                    onSdkError('pinterest');
+                }}
+            />
+            
+
+            {children}
+
+        </SocialShareContext.Provider>
+    )
+
+}
+
+export const useSocialShareContext = () => {
+    const context = useContext(SocialShareContext);
+    if (!context) {
+        throw new Error("useSocialShareContext must be used within a SocialShareProvider");
+    }
+
+    return context;
+};

+ 20 - 1
src/utils/orderDetailTools.ts

@@ -1,4 +1,4 @@
-import type { AddressListInOrderDetails,OrderAddressType } from "@/types/customer/type";
+import type { AddressListInOrderDetails,OrderAddressType,ProductItemAdditional } from "@/types/customer/type";
 import type { CartAddress } from "@/types/cart/type";
 
 export function getAddressFromOrderDetailAddressList(addressList: AddressListInOrderDetails, addressType: OrderAddressType) {
@@ -25,6 +25,25 @@ export function getAddressFromOrderDetailAddressList(addressList: AddressListInO
     return res;
 
 }
+
+export function getProductAdditionalInfo(productItem: ProductItemAdditional) { 
+    const attributeKeys = Object.keys(productItem.attributes);
+    let res = '';
+    attributeKeys.forEach(key => {
+      const attribute = productItem.attributes[key];
+      if (attribute) {
+        // option_label value_label
+
+        if(res) {
+            res = res + ', ' + attribute.option_label + ': ' + attribute.value_label;
+        } else {
+            res = attribute.option_label + ': ' + attribute.value_label;
+        }
+      }
+    });
+    return res;
+}
+
 export function getProductFinalPriceInOrder() {}
 // export function getProductAdditinalInOrder(itemAdditional: string) {
 //     const additinal = JSON.parse(itemAdditional);