Jelajahi Sumber

头部显示搜索框 个人中心和购物车按钮,删除暗黑模式按钮

fogwind 1 Minggu lalu
induk
melakukan
3eebd6b758

+ 9 - 173
src/components/cart/CartModal.tsx

@@ -66,14 +66,14 @@ export default function CartModal({
     ? cartDetail?.cart?.items?.edges
     : [];
   const cartObj: any = cartDetail?.cart ?? {};
-  const isDesktop = useMediaQuery("(min-width: 1024px)");
+
   const mounted = useSyncExternalStore(
     () => () => { },
     () => true,
     () => false,
   );
 
-  useBodyScrollLock(finalIsOpen && !isDesktop);
+  useBodyScrollLock(finalIsOpen);
 
   const handleOpenChange = (open: boolean) => {
     if (!open) {
@@ -100,171 +100,7 @@ export default function CartModal({
         )}
       </button>
 
-      {isDesktop ? (
-        <Drawer
-          backdrop="blur"
-          hideCloseButton={true}
-          classNames={{ backdrop: "bg-white/50 dark:bg-black/50" }}
-          isOpen={finalIsOpen}
-          radius="none"
-          onOpenChange={handleOpenChange}
-        >
-          <DrawerContent>
-            {() => (
-              <>
-                <DrawerHeader className="flex flex-col gap-1">
-                  <div className="flex items-center justify-between">
-                    <p className="text-lg font-semibold">My Cart</p>
-                    <button
-                      aria-label="Close cart"
-                      className="cursor-pointer"
-                      onClick={finalOnClose}
-                    >
-                      <CloseCart />
-                    </button>
-                  </div>
-                </DrawerHeader>
-
-                <DrawerBody className="py-0">
-                  {cart?.length === 0 ? (
-                    <div className="mt-20 flex w-full flex-col items-center justify-center overflow-hidden">
-                      <ShoppingCartIcon className="h-16" />
-                      <p className="mt-6 text-center text-2xl font-bold">
-                        Your cart is empty.
-                      </p>
-                    </div>
-                  ) : (
-                    <div className="flex h-full flex-col justify-between overflow-hidden">
-                      <ul className="my-0 flex-grow overflow-auto py-0">
-                        {Array.isArray(cart) &&
-                          cart?.map((item: any, i: number) => {
-                            const merchandiseSearchParams =
-                              {} as MerchandiseSearchParams;
-                            const merchandiseUrl = createUrl(
-                              `/product/${item?.node.productUrlKey}`,
-                              new URLSearchParams(merchandiseSearchParams),
-                            );
-                            const baseImage: any = safeParse(
-                              item?.node?.baseImage,
-                            );
-
-                            return (
-                              <li key={i} className="flex w-full flex-col">
-                                <div className="flex w-full flex-row justify-between gap-3 px-1 py-4">
-                                  <Link
-                                    className="z-30 flex flex-row space-x-4"
-                                    aria-label={`${item?.node?.name}`}
-                                    href={merchandiseUrl}
-                                    onClick={finalOnClose}
-                                  >
-                                    <div className="relative h-16 w-16 cursor-pointer overflow-hidden rounded-md border border-neutral-300 bg-neutral-300 dark:border-neutral-700 dark:bg-neutral-900 dark:hover:bg-neutral-800">
-                                      <Image
-                                        alt={
-                                          item?.node?.baseImage ||
-                                          item?.product?.name
-                                        }
-                                        className="h-full w-full object-cover"
-                                        height={64}
-                                        src={baseImage?.small_image_url || ""}
-                                        width={74}
-                                        onError={(e) =>
-                                          (e.currentTarget.src = NOT_IMAGE)
-                                        }
-                                      />
-                                    </div>
-
-                                    <div className="flex flex-1 flex-col text-base">
-                                      <span className="line-clamp-1 font-outfit text-base font-medium">
-                                        {item?.node?.name}
-                                      </span>
-                                      {item.name !== DEFAULT_OPTION && (
-                                        <p className="text-sm lowercase line-clamp-1 text-black dark:text-neutral-400">
-                                          {item?.node?.sku}
-                                        </p>
-                                      )}
-                                    </div>
-                                  </Link>
-
-                                  <div className="flex h-16 flex-col justify-between">
-                                    <Price
-                                      amount={item?.node?.price}
-                                      className="flex justify-end space-y-2 text-right font-outfit text-base font-medium"
-                                      currencyCode={"USD"}
-                                    />
-                                    <div className="flex items-center gap-x-2">
-                                      <DeleteItemButton item={item} />
-                                      <div className="ml-auto flex h-9 flex-row items-center rounded-full border border-neutral-200 dark:border-neutral-700">
-                                        <EditItemQuantityButton
-                                          item={item}
-                                          type="minus"
-                                        />
-                                        <p className="w-6 text-center">
-                                          <span className="w-full text-sm">
-                                            {item?.node?.quantity}
-                                          </span>
-                                        </p>
-                                        <EditItemQuantityButton
-                                          item={item}
-                                          type="plus"
-                                        />
-                                      </div>
-                                    </div>
-                                  </div>
-                                </div>
-                              </li>
-                            );
-                          })}
-                      </ul>
-
-                      <div className="border-0 border-t border-solid border-neutral-200 dark:border-dark-grey py-4 text-sm text-neutral-500 dark:text-neutral-400">
-                        {(cartDetail as any)?.cart?.taxAmount > 0 && (
-                          <div className="mb-3 flex items-center justify-between">
-                            <p className="text-base font-normal text-black/[60%] dark:text-white">
-                              Taxes
-                            </p>
-                            <Price
-                              amount={(cartDetail as any)?.cart?.taxAmount}
-                              className="text-right text-base font-medium text-black dark:text-white"
-                              currencyCode={"USD"}
-                            />
-                          </div>
-                        )}
-                        <div className="mb-3 flex items-center justify-between pb-1">
-                          <p className="text-base font-normal text-black/[60%] dark:text-white">
-                            Total
-                          </p>
-                          <Price
-                            amount={(cartDetail as any)?.cart?.grandTotal}
-                            className="text-right text-base font-medium text-black dark:text-white"
-                            currencyCode={"USD"}
-                          />
-                        </div>
-                      </div>
-
-                      <form action={redirectToCheckout}>
-                        <CheckoutButton
-                          cartDetails={cartObj?.items?.edges ?? []}
-                          isGuest={cartObj?.isGuest}
-                          isEmail={
-                            cartObj?.customerEmail ?? getLocalStorage(EMAIL)
-                          }
-                          isSelectShipping={
-                            cartObj?.selectedShippingRate != null
-                          }
-                          isSeclectAddress={isObject(billingAddress)}
-                          isSelectPayment={cartObj?.paymentMethod != null}
-                        />
-                      </form>
-                    </div>
-                  )}
-                </DrawerBody>
-
-                <DrawerFooter className="flex flex-col gap-1" />
-              </>
-            )}
-          </DrawerContent>
-        </Drawer>
-      ) : (
+      
         <AnimatePresence>
           {finalIsOpen && (
             <>
@@ -301,12 +137,12 @@ export default function CartModal({
                     <p
                       className={clsx(
                         "font-semibold",
-                        isDesktop ? "text-lg" : "text-xl",
+                        "text-xl",
                       )}
                     >
                       My Cart
                     </p>
-                    {isDesktop && (
+                 
                       <button
                         aria-label="Close cart"
                         className="cursor-pointer"
@@ -314,14 +150,14 @@ export default function CartModal({
                       >
                         <CloseCart />
                       </button>
-                    )}
+                    
                   </div>
                 </div>
 
                 <div
                   className={clsx(
                     "flex-1 overflow-y-auto px-4 py-0 drawer-scrollbar-hidden",
-                    !isDesktop && "!px-2",
+                    "!px-2",
                   )}
                 >
                   {cart?.length === 0 ? (
@@ -351,7 +187,7 @@ export default function CartModal({
                                 <div
                                   className={clsx(
                                     "flex w-full flex-row justify-between py-4 px-1",
-                                    isDesktop ? "gap-3" : "gap-1 xxs:gap-3",
+                                    "gap-1 xxs:gap-3",
                                   )}
                                 >
                                   <Link
@@ -465,7 +301,7 @@ export default function CartModal({
             </>
           )}
         </AnimatePresence>
-      )}
+      
     </>
   );
 }

+ 27 - 57
src/components/customer/credentials/CredentialModal.tsx

@@ -1,6 +1,5 @@
 "use client";
 
-import { Popover, PopoverTrigger, PopoverContent } from "@heroui/popover";
 import { useDisclosure } from "@heroui/react";
 import { AnimatePresence, motion } from "framer-motion";
 import clsx from "clsx";
@@ -10,7 +9,6 @@ import { Avatar } from "@heroui/avatar";
 import { useForm } from "react-hook-form";
 import { usePathname, useRouter } from "next/navigation";
 import { useCustomToast } from '@/utils/hooks/useToast';
-import { useMediaQuery } from "@utils/hooks/useMediaQueryHook";
 import { useBodyScrollLock } from "@utils/hooks/useBodyScrollLock";
 import OpenAuth from "../OpenAuth";
 import { isObject } from '@/utils/type-guards';
@@ -52,20 +50,11 @@ export default function CredentialModal({
   const router = useRouter();
   const dispatch = useAppDispatch();
   const { showToast } = useCustomToast();
-  const isDesktop = useMediaQuery("(min-width: 1024px)");
+
   const { resetGuestToken } = useGuestCartToken();
 
-  useBodyScrollLock(finalIsOpen && !isDesktop);
+  useBodyScrollLock(finalIsOpen );
 
-  const finalOnOpenChange = (open: boolean) => {
-    if (isControlled) {
-      if (open) onOpen?.();
-      else onClose?.();
-    } else {
-      if (open) internalOnOpen();
-      else internalOnClose();
-    }
-  };
 
   const {
     handleSubmit,
@@ -105,33 +94,33 @@ export default function CredentialModal({
 
   const innerContent = (_onClose?: () => void) => (
     <div className={clsx("flex w-full flex-col rounded-md py-4", {
-      "gap-y-6": !!session?.user || (!session?.user && isDesktop),
-      "gap-y-10": !session?.user && !isDesktop,
+      "gap-y-6": !!session?.user,
+      "gap-y-10": !session?.user,
     })}>
       {isObject(session?.user) ? (
         <>
           <header>
-            <div className={clsx("flex flex-col gap-3", !isDesktop && "items-center justify-center")}>
-              <div className={clsx("flex gap-3", !isDesktop ? "flex-col items-center" : "items-center")}>
+            <div className={clsx("flex flex-col gap-3", "items-center justify-center")}>
+              <div className={clsx("flex gap-3",  "flex-col items-center")}>
                 <Avatar
                   isBordered
                   showFallback
                   color="default"
-                  icon={<OpenAuth className={clsx(isDesktop ? "h-8" : "h-12 w-12")} />}
-                  size={isDesktop ? "md" : "lg"}
-                  className={clsx(!isDesktop && "h-24 w-24 text-large")}
+                  icon={<OpenAuth className={clsx("h-12 w-12")} />}
+                  size={"lg"}
+                  className={clsx( "h-24 w-24 text-large")}
                 />
-                <div className={clsx("flex flex-col justify-center", !isDesktop ? "items-center gap-1" : "items-start")}>
-                  <h4 className={clsx("leading-none dark:text-white", isDesktop ? "font-semibold text-default-500 text-small" : "text-xl font-bold text-black")}>
+                <div className={clsx("flex flex-col justify-center", "items-center gap-1")}>
+                  <h4 className={clsx("leading-none dark:text-white", "text-xl font-bold text-black")}>
                     {session?.user?.name}
                   </h4>
-                  <h5 className={clsx("tracking-tight dark:text-white", isDesktop ? "text-default-500 text-small" : "text-sm text-gray-500")}>
+                  <h5 className={clsx("tracking-tight dark:text-white",  "text-sm text-gray-500")}>
                     {session?.user?.email}
                   </h5>
                 </div>
               </div>
 
-              <p className={clsx("text-default-500 dark:text-white", isDesktop ? "text-small pl-px" : "text-center mt-2")}>
+              <p className={clsx("text-default-500 dark:text-white", "text-center mt-2")}>
                 Manage Cart, Orders
                 <span aria-label="confetti" className="px-2" role="img">
                   🎉
@@ -141,12 +130,12 @@ export default function CredentialModal({
           </header>
 
           <footer>
-            <form onSubmit={handleSubmit(onSubmit)} className={clsx(!isDesktop && "flex justify-center")}>
+            <form onSubmit={handleSubmit(onSubmit)} className={clsx("flex justify-center")}>
               <button
                 className={clsx(
                   "rounded-full bg-gray-800 px-5 py-2.5 text-sm font-medium text-white hover:bg-gray-900 focus:outline-none focus:ring-4 focus:ring-gray-300 dark:border-gray-700 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-700",
                   isSubmitting ? " cursor-not-allowed" : " cursor-pointer",
-                  isDesktop ? "w-full" : "w-40 min-w-[150px] mt-2"
+                  "w-40 min-w-[150px] mt-2"
                 )}
                 type="submit"
               >
@@ -166,14 +155,12 @@ export default function CredentialModal({
         </>
       ) : (
         <>
-          <header className={clsx({ "text-center": !isDesktop })}>
+          <header className="text-center">
             <div className="flex flex-col gap-y-2">
-              <h4 className={clsx("font-bold leading-none text-black dark:text-white",
-                isDesktop ? "text-xl" : "text-3xl")}>
+              <h4 className="font-bold leading-none text-black dark:text-white text-3xl">
                 Welcome Guest
               </h4>
-              <p className={clsx("text-default-500 dark:text-neutral-400",
-                isDesktop ? "text-sm" : "text-lg")}>
+              <p className="text-default-500 dark:text-neutral-400 text-lg">
                 Manage Cart, Orders
                 <span aria-label="confetti" className="px-2" role="img">
                   🎉
@@ -218,31 +205,6 @@ export default function CredentialModal({
     </div>
   );
 
-  if (isDesktop) {
-    return (
-      <Popover
-        backdrop="opaque"
-        isOpen={finalIsOpen}
-        onOpenChange={finalOnOpenChange}
-        defaultOpen={false}
-        color="default"
-        placement="bottom-end"
-      >
-        <PopoverTrigger>
-          <button
-            type="button"
-            aria-label="Open account"
-            className={clsx(className, "cursor-pointer bg-transparent")}
-          >
-            {children ? children : <OpenAuth />}
-          </button>
-        </PopoverTrigger>
-        <PopoverContent className="min-w-[300px] px-4">
-          {innerContent(finalOnClose)}
-        </PopoverContent>
-      </Popover>
-    );
-  }
 
   return (
     <>
@@ -263,7 +225,7 @@ export default function CredentialModal({
               animate={{ opacity: 1 }}
               exit={{ opacity: 0 }}
               onClick={finalOnClose}
-              className="fixed inset-0 z-40 bg-transparent lg:hidden"
+              className="fixed inset-0 z-40 bg-transparent"
               style={{ top: "68px", bottom: "64px" }}
             />
 
@@ -284,6 +246,14 @@ export default function CredentialModal({
               <div className="flex flex-col gap-1 border-b border-neutral-100 p-4 dark:border-neutral-800">
                 <div className="flex items-center justify-between">
                   <p className="text-xl font-semibold dark:text-white">Account</p>
+                  <button
+                    aria-label="Close account"
+                    className="rounded-full p-1 hover:bg-neutral-100 "
+                    onClick={finalOnClose}
+                    type="button"
+                  >
+                    <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>
               </div>
 

+ 0 - 105
src/components/layout/navbar/BottomNavbar.tsx

@@ -1,105 +0,0 @@
-"use client";
-
-import Cart from "@components/cart";
-import { CategoryIcon } from "@components/common/icons/CategoryIcon";
-import { HomeIcon } from "@components/common/icons/HomeIcon";
-import { IconSkeleton } from "@components/common/skeleton/IconSkeleton";
-import UserAccount from "@components/customer/credentials";
-import Link from "next/link";
-import { Suspense, memo } from "react";
-import clsx from "clsx";
-import OpenCart from "@components/cart/OpenCart";
-import { useAppSelector } from "@/store/hooks";
-import OpenAuth from "@components/customer/OpenAuth";
-
-type Tab = "home" | "category" | "cart" | "account" | null;
-
-const BottomNavbar = memo(function BottomNavbar({
-  onMenuOpen,
-  activeTab,
-  setActiveTab,
-}: {
-  onMenuOpen: () => void;
-  activeTab: Tab;
-  setActiveTab: (tab: Tab) => void;
-}) {
-  const cartDetail = useAppSelector((state) => state.cartDetail);
-  const itemBase =
-    "flex flex-col items-center gap-1 text-xs font-semibold py-2 rounded-lg transition-colors cursor-pointer";
-
-  const getIconWrapperClass = (tab: Tab) =>
-    clsx(
-      "flex items-center justify-center rounded-full transition-all duration-300 px-6 py-1",
-      activeTab === tab
-        ? "bg-selected-color dark:bg-selected-bg-bottom-dark dark:text-selected-bottom-dark"
-        : "bg-transparent text-neutral-900 dark:text-neutral-400"
-    );
-
-  return (
-    <div className="fixed inset-x-0 bottom-0 z-30 lg:hidden">
-      <nav className="px-3 h-16 border-t border-neutral-200 bg-white dark:border-neutral-800 dark:bg-black">
-        <div className="flex h-full items-center justify-between">
-
-          {/* Home */}
-          <Link
-            href="/"
-            aria-label="Go to Home Page"
-            onClick={() => setActiveTab("home")}
-            className={itemBase}
-          >
-            <div className={getIconWrapperClass("home")}>
-              <HomeIcon />
-            </div>
-            <span>Home</span>
-          </Link>
-
-          {/* Categories */}
-          <button
-            onClick={() => {
-              setActiveTab("category");
-              onMenuOpen();
-            }}
-            type="button"
-            className={itemBase}
-          >
-            <div className={getIconWrapperClass("category")}>
-              <CategoryIcon />
-            </div>
-            <span>Categories</span>
-          </button>
-
-          {/* Cart */}
-          <Cart
-            className={itemBase}
-            onOpen={() => setActiveTab("cart")}
-            onClose={() => setActiveTab(null)}
-            isOpen={activeTab === "cart"}
-          >
-            <div className={getIconWrapperClass("cart")}>
-              <OpenCart quantity={cartDetail?.cart?.itemsQty} />
-            </div>
-            <span>Cart</span>
-          </Cart>
-
-          {/* Account */}
-          <Suspense fallback={<IconSkeleton />}>
-            <UserAccount
-              className={itemBase}
-              onOpen={() => setActiveTab("account")}
-              onClose={() => setActiveTab(null)}
-              isOpen={activeTab === "account"}
-            >
-              <div className={getIconWrapperClass("account")}>
-                <OpenAuth />
-              </div>
-              <span>Account</span>
-            </UserAccount>
-          </Suspense>
-
-        </div>
-      </nav>
-    </div>
-  );
-});
-
-export default BottomNavbar;

+ 5 - 8
src/components/layout/navbar/CartAndUserActions.tsx

@@ -1,25 +1,22 @@
 import { Suspense } from "react";
 import Cart from "@/components/cart";
 import UserAccount from "@components/customer/credentials";
-import ThemeSwitcherWrapper from "@components/theme/theme-switch";
 import { IconSkeleton } from "@/components/common/skeleton/IconSkeleton";
 import { SessionManager } from "@/providers";
 
 export function CartAndUserActions() {
   return (
     <div className="flex max-w-fit gap-2 md:gap-4">
-      <div className="flex">
-        <ThemeSwitcherWrapper />
-      </div>
-      <div className="hidden lg:block">
+
+     
         <Cart />
-      </div>
+      
       <Suspense fallback={<IconSkeleton />}>
-        <div className="hidden lg:block">
+        
           <SessionManager>
             <UserAccount />
           </SessionManager>
-        </div>
+       
       </Suspense>
     </div>
   );

+ 1 - 1
src/components/layout/navbar/MobileMenu.tsx

@@ -2,7 +2,7 @@
 
 import { AnimatePresence, motion } from "framer-motion";
 import Link from "next/link";
-import BottomNavbar from "./BottomNavbar";
+
 import { MobileSearchBar } from "./MobileSearch";
 import { useState } from "react";
 import { useBodyScrollLock } from "@utils/hooks/useBodyScrollLock";

+ 1 - 2
src/components/layout/navbar/index.tsx

@@ -62,10 +62,9 @@ export default async function Navbar() {
                 <LogoIcon />
               </Link>
               
-              
             </div>
 
-            <div className="hidden flex-1 justify-center md:flex">
+            <div className="flex-1 justify-center flex">
               <Suspense fallback={<SearchSkeleton />}>
                 <Search search={false} />
               </Suspense>

+ 2 - 1
src/utils/constants.ts

@@ -69,6 +69,7 @@ export const SIGNUP_IMG = "/image/sign-in.webp";
 export const SIGNIN_IMG = "/image/login.webp";
 export const FORGET_PASSWORD_IMG = "/image/forget-password.webp";
 export const NOT_IMAGE = "/image/placeholder.webp";
+export const LOGO_IMG = "/image/logo.png";
 
 export const variants = {
   hidden: { opacity: 0, y: 50 },
@@ -179,7 +180,7 @@ export const imageProtocol = (process.env.NEXT_SERVER_MAGENTO_PROTOCOL ||
 
 
 
-export function getImageUrl(url?: string, baseUrl?: string, fallback?: string) {
+export function getImageUrl(url?: string, baseUrl?: string, fallback: string = NOT_IMAGE) {
   if (!url) return fallback;
 
   if (url.startsWith("http://") || url.startsWith("https://")) {