| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- "use client";
- import {useState} from "react";
- import { useRouter } from 'next/navigation';
- import type {CartDetail} from "@/types/cart/type";
- import { useCustomToast } from "@/utils/hooks/useToast";
- import {useSaveCheckoutCart} from "@/utils/hooks/useSaveCheckoutCart";
- import { formatCartDetail } from "@/utils/cartDetailTools";
- import { overlayLoading } from "@/components/theme/ui/kernel/loading/api";
- import { confirmDialog } from "@/components/theme/ui/kernel/confirm/api";
- import CommonModal from "@/components/theme/ui/CommonModal";
- import {useLoginModal} from "@/providers/LoginModalProvider";
- // 问题: plus金额,折扣,时间是前端写死的。写死金额只显示美金数字,不随货币改变;
- const staticVipData = {
- price: '$19.90',
- linePrice: '$29.90',
- days: '180',
- discount: '5%'
- };
- export default function CheckoutVipPlus({
- cartData,
- onSwitchVip
- }:{
- cartData: CartDetail
- onSwitchVip: (payload:CartDetail) => void;
- }) {
- const router = useRouter();
- const {openLoginModal} = useLoginModal();
- const { showToast } = useCustomToast();
- const {saveCheckoutCart} = useSaveCheckoutCart();
- const [isShow, setIsShow] = useState(false);
- const [vipChecked, setVipChecked] = useState(cartData.vipPlusAmount > 0);
-
- const toggleModal = (bool: boolean) => {
- setIsShow(bool);
- };
- const clickSwitchButton = (value: boolean) => {
- if(!cartData.isGuest) { // 登录用户
- const status = value;
- switchVipPlus(status);
- } else { // 游客
- // 弹出登录弹窗
- // 登录注册
- // 登录成功 -> 合并购物车
- //
- // 重新获取 数据
- //
- guestUserClick();
- }
- };
- const switchVipPlus = async (checked:boolean) => {
- const paramVip = checked ? '1' : ''; // '' 表示取消应用
- const res = await setVipPlus(paramVip);
- if(res) {
- setVipChecked(checked);
- }
- };
- const setVipPlus = async (paramVip: string) => {
- overlayLoading.start();
- const saveRes = await saveCheckoutCart({
- // shippingMethod: '-1',
- // paymentMethod: '-1',
- // couponCode: '-1',
- // giftCardNumber: '-1',
- memberDiscount: paramVip
- });
- overlayLoading.stop();
- if(!saveRes.error) {
- if(saveRes.data) {
- const newCartDetail = formatCartDetail(saveRes.data);
- onSwitchVip(newCartDetail);
- }
- return true;
- } else {
- showToast(saveRes.msg, 'danger');
- return false;
- }
- };
- const guestUserClick = () => {
- openLoginModal({
- onFinalResult: async (res) => {
- if(res.mergeCartSuccess) { // 购物车合并成功
- if(!res.cartAfterMerge?.isVip && res.cartAfterMerge?.vipPlusAmount === 0) {
- await switchVipPlus(true);
- }
- } else { // 购物车合并失败
- // 三种 情况: 登录成功; 注册成功;
- if(res.loginSuccess) {
- // 登录成功 需要刷新页面
- showToast('You have logged in successfully. But something wrong','danger');
- await confirmDialog({
- title: "Warning",
- content: "You have logged in successfully. But something wrong. You must refresh the page.",
- noCancel: true,
- });
- window.location.reload();
- }
- if(res.registerSuccess && !res.loginSuccess) {
- // 注册成功,去登录
- await confirmDialog({
- title: "Warning",
- content: "You have registered successfully. But something wrong. You have to login.",
- noCancel: true,
- });
- router.replace('/customer/login');
- }
- }
- }
- });
- };
-
- // vipExpireDate: "2027-01-30T22:40:33-08:00"
- const getVipLeftDays = (vipExpireDate: string) => {
- const target = new Date(vipExpireDate);
- const now = new Date(); // 当前时间
- // 计算相差的毫秒数
- let diffMs = target.getTime() - now.getTime();
- if(diffMs <= 0) {
- diffMs = 0;
- }
- // 转换为天数 1.1天 显示2天
- const diffDays = Math.ceil(diffMs / (1000 * 60 * 60 * 24));
- return diffDays;
- }
- return (<>
- {cartData.isVip &&
- <div className="relative box-border w-full ly-vipplus-bg p-3.75">
- <div className="flex items-center justify-between">
- <div className="flex items-center text-ly-12 font-bold">
- Asteriahair Plus
- <button className="flex-none ml-1.5 w-3.5 h-3.5" onClick={() => toggleModal(true)}>
- <svg className="block w-full h-full" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" width="14.533447265625" height="14.533340454101562" viewBox="0 0 14.533447265625 14.533340454101562" fill="none"><path d="M7.26676 13.9333C9.1077 13.9333 10.7744 13.1871 11.9808 11.9807C13.1872 10.7743 13.9334 9.10761 13.9334 7.26667C13.9334 5.42574 13.1872 3.75907 11.9808 2.55263C10.7744 1.3462 9.1077 0.600006 7.26676 0.600006C5.42583 0.600006 3.75916 1.3462 2.55272 2.55263C1.34629 3.75907 0.600098 5.42574 0.600098 7.26667C0.600098 9.10761 1.34629 10.7743 2.55272 11.9807C3.75916 13.1871 5.42583 13.9333 7.26676 13.9333Z" stroke="rgba(0, 0, 0, 1)" strokeWidth="1.2" strokeLinejoin="round" ></path><path stroke="rgba(0, 0, 0, 1)" strokeWidth="1.2" strokeLinejoin="round" strokeLinecap="round" d="M7.26807 8.8081L7.26807 7.47476C8.37263 7.47476 9.26807 6.57933 9.26807 5.47476C9.26807 4.3702 8.37263 3.47476 7.26807 3.47476C6.1635 3.47476 5.26807 4.3702 5.26807 5.47476"></path><path d="M7.26937 11.8082C7.7296 11.8082 8.1027 11.4351 8.1027 10.9748C8.1027 10.5146 7.7296 10.1415 7.26937 10.1415C6.80913 10.1415 6.43604 10.5146 6.43604 10.9748C6.43604 11.4351 6.80913 11.8082 7.26937 11.8082Z" fillRule="evenodd" fill="#000000" ></path></svg>
- </button>
- </div>
- <div className="text-ly-12">
- {getVipLeftDays(cartData.vipExpireDate)} days left
- </div>
- </div>
- <div className="flex justify-between items-center mt-3.75">
- <span className="text-ly-12">Plus Save</span>
- <span className="text-ly-16 font-bold">-{cartData.formattedVipDiscountAmount}</span>
- </div>
- </div>
- }
- {!cartData.isVip &&
- <div className="relative box-border w-full ly-vipplus-bg p-3.75">
-
- <div className="flex items-center text-ly-12 font-bold">
- Asteriahair Plus
- <button className="flex-none ml-1.5 w-3.5 h-3.5" onClick={() => toggleModal(true)}>
- <svg className="block w-full h-full" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" width="14.533447265625" height="14.533340454101562" viewBox="0 0 14.533447265625 14.533340454101562" fill="none"><path d="M7.26676 13.9333C9.1077 13.9333 10.7744 13.1871 11.9808 11.9807C13.1872 10.7743 13.9334 9.10761 13.9334 7.26667C13.9334 5.42574 13.1872 3.75907 11.9808 2.55263C10.7744 1.3462 9.1077 0.600006 7.26676 0.600006C5.42583 0.600006 3.75916 1.3462 2.55272 2.55263C1.34629 3.75907 0.600098 5.42574 0.600098 7.26667C0.600098 9.10761 1.34629 10.7743 2.55272 11.9807C3.75916 13.1871 5.42583 13.9333 7.26676 13.9333Z" stroke="rgba(0, 0, 0, 1)" strokeWidth="1.2" strokeLinejoin="round" ></path><path stroke="rgba(0, 0, 0, 1)" strokeWidth="1.2" strokeLinejoin="round" strokeLinecap="round" d="M7.26807 8.8081L7.26807 7.47476C8.37263 7.47476 9.26807 6.57933 9.26807 5.47476C9.26807 4.3702 8.37263 3.47476 7.26807 3.47476C6.1635 3.47476 5.26807 4.3702 5.26807 5.47476"></path><path d="M7.26937 11.8082C7.7296 11.8082 8.1027 11.4351 8.1027 10.9748C8.1027 10.5146 7.7296 10.1415 7.26937 10.1415C6.80913 10.1415 6.43604 10.5146 6.43604 10.9748C6.43604 11.4351 6.80913 11.8082 7.26937 11.8082Z" fillRule="evenodd" fill="#000000" ></path></svg>
- </button>
- </div>
- <div className="mt-3.75 text-ly-16 font-bold">
- <span>Pay {staticVipData.price} save</span>
- <span className="ml-1 text-ly-middlegreen">extra {staticVipData.discount} off every order</span>
- </div>
- <div className="mt-2 text-ly-12">
- {staticVipData.days} days validity
- </div>
- <div className="flex items-center mt-3">
- <span className="text-ly-16 font-bold">{staticVipData.price}</span>
- <span className="ml-2.5 text-ly-12 text-ly-deepgray line-through">{staticVipData.linePrice}</span>
- </div>
-
- <div className="absolute bottom-3.75 right-3.75 flex justify-between items-center">
- <label className="group relative w-9 h-5.5 flex-none">
- <input className="absolute w-full h-full top-0 left-0 opacity-0 z-10"
- type="checkbox" checked={vipChecked}
- onChange={(e) => clickSwitchButton(e.target.checked)}
- />
- <span className="transition-all box-border block px-0.5 w-full h-full rounded-full flex items-center bg-[#c2c2c2] group-has-[:checked]:bg-[#363636]">
- <span className="transition-all block w-4.5 h-4.5 rounded-full bg-white group-has-[:checked]:ml-3.5"></span>
- </span>
- </label>
-
- </div>
- </div>
- }
- <CommonModal
- destroyOnClose={true}
- isOpen={isShow}
- onClose={toggleModal}
- contentClassName={"left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-80 rounded-xl bg-white px-3"}
- body={
- <div className="relative py-4">
- <div className="text-center text-ly-16 font-bold">Asteriahair Plus</div>
- <p className="text-center text-ly-12 leading-5 mt-3">
- In order to provide customers with a better shopping experience, Asteriahair has specially launched Asteriahair PLUS membership (no refund after purchase). PLUS members can enjoy unlimited free shipping, 5% off on each order within 180 days, exclusive instant discounts, exclusive customer service, free returns and other membership privileges.
- </p>
- <p className="text-center text-ly-12 leading-5 mt-2">
- We hope to establish a long-term win-win relationship with you through PLUS. At the same time, we will continue to enrich the rights and interests of PLUS members and provide you with more complete services.
- </p>
- <p className="text-center text-ly-12 leading-5 mt-2">
- Thank you for choosing Asteriahair. We are dedicated to creating the ultimate shopping experience for you!(The final right of interpretation belongs to Asteriahair)
- </p>
- <p className="text-center text-ly-12 leading-5 mt-2">
- Note: PLUS membership can't be used for wholesale orders, clearance and flash sale products.
- </p>
- <button className="absolute top-4 right-4 w-6 h-6" onClick={() => toggleModal(false)}>
- <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>
- }
- />
- </>);
- }
|