Przeglądaj źródła

导航栏+个人中心样式

zhangzf 1 dzień temu
rodzic
commit
4c27682bd0
37 zmienionych plików z 1488 dodań i 427 usunięć
  1. BIN
      public/image/account/202608271600.png
  2. BIN
      public/image/account/202608271601.png
  3. BIN
      public/image/account/202609011640.png
  4. BIN
      public/image/account/202609021400.png
  5. BIN
      public/image/theme/image_flame.webp
  6. BIN
      public/image/theme/image_hot.webp
  7. 19 16
      src/app/(public)/category/[slug]/[id]/_components/ProductListing.tsx
  8. 31 27
      src/app/(public)/category/[slug]/[id]/page.tsx
  9. 127 41
      src/app/(public)/customer/account/_components/AccountHeader.tsx
  10. 12 12
      src/app/(public)/customer/account/_components/HelpModal.tsx
  11. 198 93
      src/app/(public)/customer/account/edit/page.tsx
  12. 32 20
      src/app/(public)/customer/account/mycoupon/_components/GiftCardList.tsx
  13. 10 9
      src/app/(public)/customer/account/page.tsx
  14. 25 13
      src/app/(public)/customer/account/pointsreceivelist/_components/RedeemGiftCard.tsx
  15. 66 13
      src/app/(public)/customer/address/_components/DefaultAddress.tsx
  16. 1 1
      src/app/(public)/customer/address/new/_components/CustomerAddressPhone.tsx
  17. 85 25
      src/app/(public)/customer/address/page.tsx
  18. 2 2
      src/app/(public)/page.tsx
  19. 2 2
      src/app/(public)/track_order/_components/TrackFaqs.tsx
  20. 14 13
      src/app/(public)/track_order/page.tsx
  21. 16 15
      src/components/customer/SettingModal.tsx
  22. 328 69
      src/components/customer/accountSwiper.tsx
  23. 226 17
      src/components/layout/navbar/MobileMenu.tsx
  24. 3 2
      src/components/layout/navbar/MobileMenuTrigger.tsx
  25. 40 27
      src/components/layout/navbar/index.tsx
  26. 5 5
      src/components/theme/filters/NewSortOrder.tsx
  27. 1 1
      src/components/theme/ui/PhoneNumberInput/PhoneNumberInput.tsx
  28. 3 3
      src/graphql/catalog/queries/GetCategoryProducts.ts
  29. 24 0
      src/graphql/customer/mutations/UpdataCustomerInformation.ts
  30. 1 0
      src/graphql/customer/mutations/index.ts
  31. 20 0
      src/graphql/customer/query/GetCustomerInformation.ts
  32. 2 1
      src/graphql/customer/query/index.ts
  33. 54 0
      src/graphql/theme/queries/GetTreeFrontMenus.ts
  34. 3 0
      src/graphql/theme/queries/index.ts
  35. 56 0
      src/types/customer/information/information.ts
  36. 38 0
      src/types/theme/menus.ts
  37. 44 0
      src/utils/constants.ts

BIN
public/image/account/202608271600.png


BIN
public/image/account/202608271601.png


BIN
public/image/account/202609011640.png


BIN
public/image/account/202609021400.png


BIN
public/image/theme/image_flame.webp


BIN
public/image/theme/image_hot.webp


+ 19 - 16
src/app/(public)/category/[slug]/[id]/_components/ProductListing.tsx

@@ -7,7 +7,7 @@ import ProductGridItems from "@components/catalog/product/ProductGridItems";
 import NewMobileFilter from "@components/theme/filters/NewMobileFilter";
 import NewSortOrder from "@components/theme/filters/NewSortOrder";
 
-import { SortByFields } from "@utils/constants";
+import { newSortByFields } from "@utils/constants";
 import { isArray } from "@/utils/type-guards";
 import { useRouter, usePathname } from "next/navigation";
 import { useMutation, useApolloClient } from "@apollo/client/react";
@@ -16,11 +16,12 @@ import {CATEGORY_PRODUCTS} from "@/graphql";
 interface QueryState {
   // search: string;
 
-  sort: string;
-
+  sortValue: string;
+  sortKey: string;
   filters: Record<string, string>;
 
   cursor?: string;
+  reverse?:boolean,
 }
 interface ProductListingProps {
   slug:string;
@@ -74,8 +75,8 @@ export default function ProductListing({
         {
           slug: slug ||"ready-to-go-wig",
         
-          // sort: currentQuery.sort,
-
+          sortKey: currentQuery.sortKey,
+          reverse: currentQuery.reverse,
           filter:JSON.stringify( currentQuery.filters),
 
           first: 15,
@@ -83,7 +84,7 @@ export default function ProductListing({
           after: null,
         },
       });
-      console.log("res----------------------------------:",res);
+      console.log("res----------------------------------:",res,currentQuery);
       
       // const res = await fetch("/api/products", {
       //   method: "POST",
@@ -128,7 +129,7 @@ export default function ProductListing({
     console.log("query-----------------------------22222:",query);
     fetchProducts(query);
     
-  }, [query.filters, query.sort]);
+  }, [query.filters, query.sortValue]);
   //query.search
   const router = useRouter();
   const pathname = usePathname();
@@ -142,8 +143,8 @@ export default function ProductListing({
       params.set("q", nextQuery.search);
     }
 
-    if (nextQuery.sort) {
-      params.set("sort", nextQuery.sort);
+    if (nextQuery.sortValue) {
+      params.set("sort", nextQuery.sortValue);
     }
 
     Object.entries(nextQuery.filters).forEach(([key, value]) => {
@@ -190,7 +191,8 @@ export default function ProductListing({
           slug: slug ||"ready-to-go-wig",
         
           // sort: query.sort,
-
+          sortKey: query.sortKey,
+          reverse: query.reverse,
           filter:JSON.stringify(query.filters),
 
           first: 15,
@@ -200,6 +202,7 @@ export default function ProductListing({
       });
     //search: query.search,
     console.log("触发view more调接口");
+    console.log("res----------------------------------:",res,query);
     // const result = await res.json();
 
     const moreProducts =
@@ -226,15 +229,15 @@ export default function ProductListing({
           }}
         />
         <NewSortOrder
-          sortOrders={SortByFields}
+          sortOrders={newSortByFields}
           title="Sort by"
-          value={query.sort}
-          onChange={(sort) => {
+          value={query.sortValue}
+          onChange={(sortValue,sortKey,reverse) => {
             updateQuery({
               ...query,
-
-              sort,
-
+              sortValue,
+              sortKey,
+              reverse:reverse,
               cursor: undefined,
             });
           }}

+ 31 - 27
src/app/(public)/category/[slug]/[id]/page.tsx

@@ -19,9 +19,9 @@ import {
 import {
   // cachedGraphQLRequest,
   // cachedCategoryRequest,
-  getFilterAttributes,
+  // getFilterAttributes,
 } from "@/utils/hooks/useCache";
-import { SortByFields } from "@utils/constants";
+import { newSortByFields } from "@utils/constants";
 // import { CategoryDetail } from "@components/theme/search/CategoryDetail";
 import CategoryDesc from "./_components/CategoryDesc";
 import FaqList, { FaqItem } from "./_components/FaqList";
@@ -162,7 +162,7 @@ export default async function CategoryPage({
   // const currentPage = page ? parseInt(page) - 1 : 0;
   const sortValue = getSearchParam(resolvedParams?.sort) ?? "name-asc";
   const selectedSort =
-    SortByFields.find((s) => s.key === sortValue) || SortByFields[0];
+    newSortByFields.find((s) => s.key === sortValue) || newSortByFields[0];
 
   const { filterObject: baseFilterObject } = newBuildProductFilters(
     resolvedParams || {},
@@ -200,31 +200,31 @@ export default async function CategoryPage({
       reverse: selectedSort.reverse,
     },
   });
-  console.log("categoryProductDatas-----------:",categoryProductDatas);
+  console.log("categoryProductDatas-----------:",categoryProductDatas,selectedSort);
   
   // 这个也改成 serverGraphqlFetch
-  const productVariables = {
-    query: searchValue || "",
-    filter: filterInput,
-    ...(before
-      ? {
-          last: itemsPerPage,
-          before,
-        }
-      : {
-          first: itemsPerPage,
-          after: cursor || null,
-        }),
-    sortKey: selectedSort.sortKey,
-    reverse: selectedSort.reverse,
-  };
-  const { data } = await serverGraphqlFetch<
-    ProductsResponse,
-    typeof productVariables
-  >({
-    query: GET_FILTER_PRODUCTS,
-    variables: productVariables,
-  });
+  // const productVariables = {
+  //   query: searchValue || "",
+  //   filter: filterInput,
+  //   ...(before
+  //     ? {
+  //         last: itemsPerPage,
+  //         before,
+  //       }
+  //     : {
+  //         first: itemsPerPage,
+  //         after: cursor || null,
+  //       }),
+  //   sortKey: selectedSort.sortKey,
+  //   reverse: selectedSort.reverse,
+  // };
+  // const { data } = await serverGraphqlFetch<
+  //   ProductsResponse,
+  //   typeof productVariables
+  // >({
+  //   query: GET_FILTER_PRODUCTS,
+  //   variables: productVariables,
+  // });
   // const [{ data }] = await Promise.all([
   //   cachedCategoryRequest<ProductsResponse>(categorySlug, GET_FILTER_PRODUCTS, {
   //     query: searchValue || "",
@@ -253,6 +253,8 @@ export default async function CategoryPage({
   console.log("initialQuery------------", {
     search: searchValue,
     sort: sortValue,
+    sortKey: selectedSort.sortKey,
+    reverse: selectedSort.reverse,
     filters: baseFilterObject,
   });
   // 模拟FAQ假数据
@@ -326,7 +328,9 @@ export default async function CategoryPage({
           initialTotal={totalCount}
           initialQuery={{
             // search: searchValue,
-            sort: sortValue,
+            sortValue: sortValue,
+            sortKey: selectedSort.sortKey,
+            reverse: selectedSort.reverse,
             filters: baseFilterObject,
             cursor,
           }}

Plik diff jest za duży
+ 127 - 41
src/app/(public)/customer/account/_components/AccountHeader.tsx


+ 12 - 12
src/app/(public)/customer/account/_components/HelpModal.tsx

@@ -14,12 +14,12 @@ export default function HelpModal({
       <div className="absolute bottom-0 rounded-none left-0 right-0 w-full h-full bg-white  overflow-auto">
         <div className="flex flex-col">
           {/* 顶部导航 */}
-          <header className="bg-black text-white py-4 px-4 flex items-center">
+          <header className=" text-black py-4 px-4 flex items-center border-b border-[#F9F9F9FF]">
             <button onClick={helponClose} className="mr-4">
               <svg
                 xmlns="http://www.w3.org/2000/svg"
                 className="h-6 w-6"
-                fill="none"
+                fill="#000"
                 viewBox="0 0 24 24"
                 stroke="currentColor"
               >
@@ -27,20 +27,23 @@ export default function HelpModal({
                   strokeLinecap="round"
                   strokeLinejoin="round"
                   strokeWidth={2}
+                  fill="none"
                   d="M15 19l-7-7 7-7"
                 />
               </svg>
             </button>
-            <h1 className="text-lg font-medium">HELP & SUPPORT</h1>
+            <h1 className="text-lg w-full text-center  font-normal">
+              Help & Support
+            </h1>
           </header>
           {/* 菜单列表 */}
           <div className="px-4 py-2">
             {/* Information */}
+
             <Link
               href={"/help-faqs"}
-              className="py-4 border-b border-gray-100 flex justify-between items-center"
+              className="py-4 border-b border-gray-100 flex justify-between items-center bg-[#F9F9F9FF] mb-4  px-3"
             >
-              {/* <div > */}
               <span className="text-black font-medium">Help & FAQ</span>
               <svg
                 xmlns="http://www.w3.org/2000/svg"
@@ -56,14 +59,13 @@ export default function HelpModal({
                   d="M9 5l7 7-7 7"
                 />
               </svg>
-              {/* </div> */}
             </Link>
             {/* Address */}
+
             <Link
               href={"/track_order"}
-              className="py-4 border-b border-gray-100 flex justify-between items-center"
+              className="py-4 border-b border-gray-100 flex justify-between items-center bg-[#F9F9F9FF] mb-4  px-3"
             >
-              {/* <div > */}
               <span className="text-black font-medium">Track Order</span>
               <svg
                 xmlns="http://www.w3.org/2000/svg"
@@ -79,14 +81,13 @@ export default function HelpModal({
                   d="M9 5l7 7-7 7"
                 />
               </svg>
-              {/* </div> */}
             </Link>
             {/* help & Support */}
+
             <Link
               href={"/hair-care-tips"}
-              className="py-4 border-b border-gray-100 flex justify-between items-center"
+              className="py-4 border-b border-gray-100 flex justify-between items-center bg-[#F9F9F9FF] mb-4  px-3"
             >
-              {/* <div > */}
               <span className="text-black font-medium">Hair Care Tips</span>
               <svg
                 xmlns="http://www.w3.org/2000/svg"
@@ -102,7 +103,6 @@ export default function HelpModal({
                   d="M9 5l7 7-7 7"
                 />
               </svg>
-              {/* </div> */}
             </Link>
           </div>
         </div>

+ 198 - 93
src/app/(public)/customer/account/edit/page.tsx

@@ -2,53 +2,106 @@
 import React from "react";
 import { useState } from "react";
 import { useEffect } from "react";
-import {
-  parseDate,
-  CalendarDate,
-  DateValue
-} from "@internationalized/date";
+import { parseDate, CalendarDate, DateValue } from "@internationalized/date";
 import Link from "next/link";
 import { DatePicker } from "@heroui/date-picker";
+import CustomerAddressPhone from "../../address/new/_components/CustomerAddressPhone";
+import { useForm, FormProvider,Controller } from "react-hook-form";
+import { useMutation, useApolloClient } from "@apollo/client/react";
+import { CustomerProfileResult } from "@components/customer/information/information";
+import {GET_CUSTOMER_PROFILE,UPDATE_CUSTOMER_PROFILE} from "@/graphql";
+import { UpdateCustomerProfileVars } from "@components/customer/information/information";
+import { useCustomToast } from "@/utils/hooks/useToast";
 const AccountEditPage = () => {
   interface UserForm {
-  firstname: string;
-  lastname: string;
-  date: CalendarDate  | string;
-  email: string;
-  current_password: string;
-  password: string;
-}
-  // 统一表单对象
-  const [form, setForm] = useState<UserForm>({
-    firstname: "",
-    lastname: "",
-    date: parseDate("2026-05-12"),
-    email: "",
-    current_password: "",
-    password: "",
+    firstname: string;
+    lastname: string;
+    date: CalendarDate | string |null;
+    email: string;
+    current_password: string;
+    password: string;
+    phone: string;
+  }
+
+  const { showToast } = useCustomToast();
+  const addressForm = useForm<UserForm>({
+    mode: "onBlur",
+    reValidateMode: "onChange",
+    defaultValues: {
+      firstname: "",
+      lastname: "",
+      date: parseDate("2026-05-12"),
+      email: "",
+      current_password: "",
+      password: "",
+      phone: "",
+    },
   });
+  const [hasBirthDate, setHasBirthDate] = useState(false);
+  const { reset,setValue } = addressForm;
+  const apolloClient = useApolloClient();
+  const fetchProducts = async () => {
+      try {
+        const res = await apolloClient.query<CustomerProfileResult>({
+          query: GET_CUSTOMER_PROFILE,
+        });
+       // 空值判断
+    if (!res.data) {
+      console.warn("没有返回用户数据");
+      return;
+    }
 
-  // 统一处理所有input变化
-  const handleChange = (e:any) => {
-    const { name, value } = e.target;
-    setForm((prev) => ({
-      ...prev,
-      [name]: value,
-    }));
-  };
+    const profile = res.data.readCustomerProfile;
+
+      //接口数据映射到表单,reset回填
+      reset({
+        firstname: profile.firstName,
+        lastname: profile.lastName,
+        email: profile.email,
+        phone: profile.phone,
+        date: profile.dateOfBirth||null,
+        current_password: "",
+        password: "",
+      });
+       if(profile.dateOfBirth){
+         setHasBirthDate(true);
+       }else{
+         setHasBirthDate(false);
+       }
+      } catch (error) {
+        console.error("fetch products error", error);
+      }
+    };
+  useEffect(() => {
+   
+    fetchProducts();
+    
+  }, []);
+  const [updateProfileMutate] = useMutation(UPDATE_CUSTOMER_PROFILE, {
+  onCompleted: (res) => {
+    console.log("更新结果", res);
+    const result = res.createCustomerProfileUpdate.customerProfileUpdate;
+    if (result.success) {
+      // 更新成功后的逻辑,提示、刷新表单等
+      showToast(result.message, "success");
+    }
+  },
+  onError: (err) => {
+    console.error("更新用户资料失败:", err);
+     showToast(err.message, "danger");
+  },
+});
+ 
   // 1. 定义状态:控制修改密码显示隐藏
   const [showContent, setShowContent] = useState(false);
   //   日期选择方法
-  const handleDateChange = (dateValue: DateValue  | null) => {
+  const handleDateChange = (dateValue: DateValue | null) => {
     if (!dateValue) return;
 
     // 把 CalendarDate 转成 标准字符串:YYYY-MM-DD
     const formattedDate = `${dateValue.year}-${String(dateValue.month).padStart(2, "0")}-${String(dateValue.day).padStart(2, "0")}`;
-
-    setForm((prev) => ({
-      ...prev,
-      date: formattedDate, // 现在是正常字符串了
-    }));
+    return formattedDate
+ 
   };
   // 勾选框改变时:隐藏时 → 清空密码
   const handleCheckChange = (e: React.ChangeEvent<HTMLInputElement>) => {
@@ -57,88 +110,135 @@ const AccountEditPage = () => {
 
     // ✅ 关键:不勾选时清空密码字段
     if (!isChecked) {
-      setForm((prev) => ({
-        ...prev,
-        current_password: "",
-        password: "",
-      }));
+     setValue("current_password", "");
+    setValue("password", "");
     }
   };
-  // 统一提交
-  const handleSubmit = (e:any) => {
-    e.preventDefault();
-    // 直接拿form所有数据
-    console.log("表单数据:", form);
-  };
-  useEffect(() => {
-    // console.log("form 已更新 →", form);
-  }, [form]);
+ 
+  
+    // 表单提交
+    const addressFormOnSubmit = async (formData: UserForm) => {
+      console.log("最终提交给后端的数据:", formData);
+      const inputPayload: UpdateCustomerProfileVars["input"] = {
+        firstName: formData.firstname,
+        lastName: formData.lastname,
+        email: formData.email,
+        phone: formData.phone || null,
+        dateOfBirth: formData.date || null,
+      };
+      // 勾选修改密码时附加密码字段
+      if (showContent) {
+        inputPayload.current_password = formData.current_password;
+        inputPayload.password = formData.password;
+      }
+      await updateProfileMutate({
+        variables: {
+        input: inputPayload,
+        },
+      });
+   
+    };
+ 
+   const {
+    register,
+    handleSubmit,
+    control,
+    formState: { errors },
+  } = addressForm;
   return (
     <div className="w-full h-full">
-      <form className="overflow-hidden" onSubmit={handleSubmit}>
-        <div className="bg-[#fff] pr-2.5 pl-2.5 w-full text-center text-base h-11 leading-11 font-semibold relative text-[#0b0b0b]">
-           <Link
-              className="absolute left-2.5 text-2xl inline-block top-1/2  -translate-y-1/2"
-              href="/customer/account"
-              aria-label="Go to customer account page"
-            >
+      <FormProvider {...addressForm}>
+      <form className="overflow-hidden"  onSubmit={handleSubmit(addressFormOnSubmit)} >
+        <div className=" text-black py-4 px-4 flex items-center border-b border-[#F9F9F9FF]">
+          <Link
+            className="mr-4"
+            href="/customer/account"
+            aria-label="Go to customer account page"
+          >
             <svg
               xmlns="http://www.w3.org/2000/svg"
-              width="24"
-              height="24"
+              className="h-6 w-6"
+              fill="#000"
               viewBox="0 0 24 24"
-              fill="none"
+              stroke="currentColor"
             >
               <path
-                stroke="rgba(0, 0, 0, 1)"
-                stroke-width="1.2"
-                stroke-linejoin="round"
-                stroke-linecap="round"
-                d="M15 18L9 12L15 6"
-              ></path>
+                strokeLinecap="round"
+                strokeLinejoin="round"
+                strokeWidth={2}
+                fill="none"
+                d="M15 19l-7-7 7-7"
+              />
             </svg>
           </Link>
-          <span className="vipReturnTitles">Account Information</span>
+          <h1 className="text-lg w-full text-center  font-normal">
+            Information
+          </h1>
         </div>
         <input name="form_key" type="hidden" value="kW7wucXHATeeem75"></input>
-        <div className="bg-[#f8f8f8] p-[20px_10px]">
-          <div className="text-start">*Name</div>
+        <div className=" p-[20px_10px]">
+          <div className="text-start mb-3">*Name</div>
           <div className="flex justify-between items-center">
             <input
-              name="firstname" // 必须和state字段对应
-              value={form.firstname}
-              onChange={handleChange}
+              // name="firstname" // 必须和state字段对应
+             
+              {...register("firstname", {
+                  required: "First name is required",
+                })}
               placeholder="firstname"
-              className="w-43 h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal"
+              className="w-43 h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal"
             />
             <input
-              name="lastname" // 必须和state字段对应
-              value={form.lastname}
-              onChange={handleChange}
+              // name="lastname" // 必须和state字段对应
+              
+                {...register("lastname", {
+                  required: "Last name is required",
+                })}
               placeholder="lastname"
-              className="w-43 h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+              className="w-43 h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
             />
           </div>
           <div className="mt-7.5">
+            <Controller
+        name="date"
+        control={control}
+        rules={{
+          required: "date name is required",
+        }}
+      
+        render={({ field: { onChange, value } }) => (
             <DatePicker
               size="md"
-              onChange={handleDateChange}
+              label="Date Of Birth"
+              isRequired={hasBirthDate}
+              onChange={(dateValue) => {
+              // HeroUI DatePicker 返回 CalendarDate 对象,转成字符串 yyyy‑MM‑dd
+              onChange(handleDateChange(dateValue) );
+                
+           
+            }}
               radius="sm"
-              className="bg-[#fff]"
+              className="bg-white"
+              description={"This cannot be modified. Please fill in carefully."}
               labelPlacement={"outside"}
-              showMonthAndYearPickers
+              showMonthAndYearPickers={false}
               aria-label="date picker"
             />
+              )}
+      />
             {/* label={"Date of Birth"} */}
           </div>
+          <CustomerAddressPhone />
           <div className="mt-7.5">
-            <label htmlFor="email">Email Address*</label>
+            <label >Email Address*</label>
             <input
-              name="email" // 必须和state字段对应
-              value={form.email}
-              onChange={handleChange}
+              // name="email" // 必须和state字段对应
+            
+               {...register("email", {
+                  required: "email name is required",
+                })}
               placeholder="email"
-              className="w-full h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+              className="w-full mt-3 h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
             />
           </div>
           {/* 选择框(开关)*/}
@@ -155,30 +255,35 @@ const AccountEditPage = () => {
             <div>
               <input
                 type="password"
-                name="current_password" // name对应key
-                value={form.current_password}
-                onChange={handleChange}
+                // name="current_password" // name对应key
+              
+                 {...register("current_password", {
+                  required: "Password is required",
+                })}
                 placeholder="Old password"
-                className="mt-7.5 w-full h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+                className="mt-7.5 w-full h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
               />
               <input
                 type="password"
-                name="password" // name对应key
-                value={form.password}
-                onChange={handleChange}
+                // name="password" // name对应key
+             
+                  {...register("password", {
+                  required: "Password is required",
+                })}
                 placeholder="* New Password"
-                className=" mt-7.5 w-full h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+                className=" mt-7.5 w-full h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
               />
             </div>
           )}
           <button
-            className="w-full h-11 leading-11 bg-[#1bbc9b] text-sm font-normal text-center mt-7.5"
+            className="w-full h-12 leading-12  rounded-[26px] bg-[rgba(3,97,65,1)] text-base text-white font-bold text-center mt-7.5"
             type="submit"
           >
-            Save Changes
+            Save
           </button>
         </div>
       </form>
+      </FormProvider>
     </div>
   );
 };

+ 32 - 20
src/app/(public)/customer/account/mycoupon/_components/GiftCardList.tsx

@@ -75,31 +75,43 @@ const GiftCardList: React.FC = () => {
   return (
     <div
       ref={containerRef}
-      className="h-[calc(100vh-80px)] overflow-auto px-2 py-4"
+      className="h-[calc(100vh-80px)] overflow-auto px-4 py-4"
     >
       {/* 礼品卡列表 */}
       <div className="space-y-4">
         {giftCards.map((item,index) => (
-          <div
-            key={index}
-            className="flex items-center bg-blue-100 rounded-lg overflow-hidden"
-          >
-            {/* 礼品卡信息区 */}
-            <div className="flex-1 p-4 text-blue-600">
-              <h3 className="text-2xl font-bold">${Number(item.giftcard_amount)} OFF</h3>
-              <p className="text-sm mt-1">
-                For All Products
-              </p>
-              <p className="text-xs mt-2 text-blue-500">{item.expirationdate}</p>
-            </div>
-            {/* 分割线 */}
-            <div className="w-0.5 bg-blue-300 h-16 relative">
-              <div className="absolute top-0 left-[-4px] w-2 h-2 bg-white border border-blue-300 rounded-full"></div>
-              <div className="absolute bottom-0 left-[-4px] w-2 h-2 bg-white border border-blue-300 rounded-full"></div>
+          <>
+            <div
+              key={item.id}
+              className="rounded-md p-4 pt-2 h-33.5 flex items-center justify-between text-white bg-[url('/image/account/202609021400.png')] bg-no-repeat bg-size-[100%_100%]"
+            >
+              {/* 左侧折扣+积分信息 */}
+              <div className="flex flex-col h-full py-2 justify-between">
+                    <span className="text-sm font-normal">Expire In {item.expirationdate} </span>
+                <div className="flex flex-col gap-0.5">
+                <span className="text-2xl leading-7.5 font-bold ">Gift Card</span>
+                <span className="text-sm font-normal  opacity-90">
+                  For All Products
+                </span>
+                </div>
+              </div>
+
+              {/* 右侧按钮区域 */}
+              <div className="w-[132px] h-full  text-center flex flex-col justify-between pt-1">
+                <div className="font-bold text-5xl">
+                  ${Number(item.giftcard_amount)}
+                </div>
+                {/* 按钮:已兑换显示 USED,未兑换显示 REDEEM */}
+                <button
+                  className={`w-full h-8  rounded-2xl border mx-auto bg-white text-[#01754AFF] border-white hover:bg-orange-50 transition-colors`}
+               
+                >
+                  Use Now
+                </button>
+                {/* <p className="text-xs mt-1 opacity-90">ALL Products</p> */}
+              </div>
             </div>
-            {/* 使用按钮区 */}
-            <div className="px-6 py-4 font-bold text-black">Use Now</div>
-          </div>
+            </>
         ))}
       </div>
 

+ 10 - 9
src/app/(public)/customer/account/page.tsx

@@ -34,12 +34,12 @@ const orderNavs = [
 const MyAccountPage = () => {
   
   return (
-    <div className="min-h-screen bg-white">
+    <div className="min-h-screen bg-white pt-2 px-2">
       <AccountHeader />
       {/* 我的订单 */}
       <div className="">
         <h3 className="mt-8 ml-4 text-base">My Order</h3>
-        <div className="flex flex-wrap justify-between mt-5 pr-4 pl-4">
+        <div className="flex flex-wrap justify-between mt-5 ">
           {orderNavs.map((item, index) => (
             <a
               key={index}
@@ -59,7 +59,7 @@ const MyAccountPage = () => {
         </div>
       </div>
 
-      <div className="h-2.5 bg-[#F0F0F0]"></div>
+      <div className="h-px bg-[#F0F0F0] mt-6"></div>
 
       {/* 服务菜单 */}
       {/* <div className="">
@@ -125,14 +125,15 @@ const MyAccountPage = () => {
         </div>
       </div> */}
 
-      <div className="h-2.5 bg-[#F0F0F0]"></div>
+      {/* <div className="h-2.5 bg-[#F0F0F0]"></div> */}
       {/* 商品推荐 */}
       <div className="mb-12.5">
-        <div className="flex  justify-between items-center pt-7.5 pb-5 pl-4 pr-4">
-          <h1 className="font-bold text-base">You May Like</h1>
-          <a className="text-xs text-[#666666]" href="/flash-sale.html">
-            View More &gt;
-          </a>
+        <div className="flex  justify-between items-center pt-6 pb-5 pl-4 pr-4">
+          <h1 className="font-medium text-ly-18 leading-ly-26 text-base">Recommended For You</h1>
+          {/* <a className="text-xs text-[#666666]" href="/flash-sale.html">
+            View More  
+             &gt;
+          </a> */}
         </div>
         {/* 引入轮播组件 */}
         <AccountSwiper />

+ 25 - 13
src/app/(public)/customer/account/pointsreceivelist/_components/RedeemGiftCard.tsx

@@ -3,6 +3,7 @@
 import React, { useState, useEffect } from "react";
 import { clientFetch } from "@/lib/restApiClient";
 import type { GiftItem  } from '@/types/api/gift/lists';
+import { useCustomToast } from "@/utils/hooks/useToast";
 
 const RedeemGiftCard: React.FC = () => {
   // // 模拟积分兑换数据(可根据实际接口替换)
@@ -15,7 +16,7 @@ const RedeemGiftCard: React.FC = () => {
   //   { id: 6, discount: "$85 OFF", points: 6800, isRedeemed: false },
   // ];
   const [list, setList] = useState<GiftItem[]>([]);
-  const [myPoints, setmyPoints] = useState<string>();
+  const [myPoints, setmyPoints] = useState<number>();
   useEffect(() => {
     const load = async () => {
       const resp = await clientFetch("/api/gift/lists");
@@ -28,8 +29,8 @@ const RedeemGiftCard: React.FC = () => {
 
     load();
   }, []);
-
-  const handleAdd = async (id:number) => {
+   const { showToast } = useCustomToast();
+  const handleAdd = async (id:number,points:number) => {
     const res = await clientFetch("/api/gift/add", {
       method: "POST",
       headers: {
@@ -43,6 +44,14 @@ const RedeemGiftCard: React.FC = () => {
     });
 
     const json = await res;
+    if(json.success){
+        showToast(json.data, "success");
+     
+        setmyPoints((prev) => {
+         if (prev === undefined) return prev;
+          return prev - points;
+        })
+    }
     console.log("返回结果:", json);
   };
 
@@ -112,31 +121,34 @@ const RedeemGiftCard: React.FC = () => {
           {Object.values(list).map((item) => (
             <div
               key={item.id}
-              className="bg-gradient-to-r from-orange-500 to-orange-400 rounded-md p-4 flex items-center justify-between text-white"
+              className="rounded-md p-4 pt-2 h-33.5 flex items-center justify-between text-white bg-[url('/image/account/202609021400.png')] bg-no-repeat bg-size-[100%_100%]"
             >
               {/* 左侧折扣+积分信息 */}
-              <div className="flex flex-col">
-                <span className="text-2xl font-bold">{item.amount}</span>
-                <span className="text-sm mt-1 opacity-90">
-                  SPEND POINTS {item.points}
+              <div className="flex flex-col h-full py-2 justify-between">
+                <span className="text-2xl leading-7.5 font-bold ">Gift Card</span>
+                <span className="text-sm font-normal  opacity-90">
+                  {item.points}Pts
                 </span>
               </div>
 
               {/* 右侧按钮区域 */}
-              <div className="w-[132px] h-[66px] pl-[16px] text-center flex flex-col justify-between border-l border-dashed  border-white">
+              <div className="w-[132px] h-full  text-center flex flex-col justify-between pt-1">
+                <div className="font-bold text-5xl">
+                  ${item.amount}
+                </div>
                 {/* 按钮:已兑换显示 USED,未兑换显示 REDEEM */}
                 <button
-                  onClick={() => handleAdd(item.id)}
-                  className={`w-19 h-7  rounded border mx-auto ${
+                  onClick={() => handleAdd(item.id,item.points)}
+                  className={`w-full h-8  rounded-2xl border mx-auto ${
                     !item.status
                       ? "bg-gray-300 text-gray-600 border-gray-400 cursor-not-allowed"
-                      : "bg-white text-orange-600 border-white hover:bg-orange-50 transition-colors"
+                      : "bg-white text-[#01754AFF] border-white hover:bg-orange-50 transition-colors"
                   }`}
                   disabled={!item.status}
                 >
                   {!item.num ? "USED" : "REDEEM"}
                 </button>
-                <p className="text-xs mt-1 opacity-90">ALL Products</p>
+                {/* <p className="text-xs mt-1 opacity-90">ALL Products</p> */}
               </div>
             </div>
           ))}

+ 66 - 13
src/app/(public)/customer/address/_components/DefaultAddress.tsx

@@ -12,7 +12,7 @@ export default function DefaultAddress() {
         `/api/customer/token/address`, ///api/shop/customer-addresses?page=${page}
       );
       console.log("完整返回:", resp.data);
-      resp.data.data= Array.isArray(resp.data.data) ? resp.data.data : [];
+      resp.data.data = Array.isArray(resp.data.data) ? resp.data.data : [];
       const sortedList = [...resp.data.data].sort((a, b) => {
         // true(1) 排在 false(0) 前面
         if (a.default_address !== b.default_address) {
@@ -22,37 +22,90 @@ export default function DefaultAddress() {
         return 0;
       });
       setAddressDatas(sortedList);
-      console.log("aaaa",sortedList,addressDatas);
-      
+      console.log("aaaa", sortedList, addressDatas);
     };
     load();
   }, []);
   return (
     <>
-      {(addressDatas || []).map((item) => (
+      {(addressDatas || []).map((item,index) => {
+        const isLastItem = index === (addressDatas || []).length - 1;
+        return(
         <div key={item.id} className="mb-6">
           {item.default_address ? (
-            <h2 className="text-xl font-bold uppercase mb-3">
+            <h2 className="text-ly-14 leading-ly-22 font-normal mb-2">
               Default Billing Address
             </h2>
           ) : null}
-          <div className="border border-gray-200 p-4">
+          {/* <div className="border border-gray-200 p-4">
             <p className="mb-1">{item.first_name + item.last_name}</p>
             <p className="mb-1">{item.address}</p>
             <p className="mb-1">{item.city + "," + item.postcode}</p>
             <p className="mb-1">{item.country}</p>
             <p className="mb-4">T: {item.phone}</p>
-            <div className="text-right">
-              <Link
-                href={`/customer/address/eidt/${item.id}`}
-                className="text-blue-600 underline hover:text-blue-800"
+           
+          </div> */}
+          <div className="border border-gray-200 rounded-xl bg-gray-50 px-4 py-3 bg-[url('/image/account/202609011640.png')] bg-no-repeat bg-size-[100%_100%]">
+            {/* 姓名 + 手机号行 */}
+            <div className="flex items-center text-ly-13 leading-ly-21 font-normal ">
+              <span>
+                {item.first_name} {item.last_name}
+              </span>
+              <span className="mx-2">|</span>
+              <span>+1 {item.phone}</span>
+            </div>
+
+            {/* 分割线 */}
+            <hr className="border-black my-3" />
+
+            {/* SVG定位图标 + 地址行 */}
+            <div className="flex items-center gap-3 ">
+              {/* 位置图标 SVG */}
+
+              <svg
+                xmlns="http://www.w3.org/2000/svg"
+                width="24"
+                height="24"
+                viewBox="0 0 24 24"
+                fill="none"
               >
-                Change Billing Address
-              </Link>
+                <path
+                  stroke="rgba(0, 0, 0, 1)"
+                  stroke-width="1.5"
+                  stroke-linejoin="round"
+                  stroke-linecap="round"
+                  d="M6.0175 15.917C4.4862 16.3764 3.53906 17.0111 3.53906 17.712C3.53906 19.1141 7.32759 20.2507 12.001 20.2507C16.6744 20.2507 20.4629 19.1141 20.4629 17.712C20.4629 17.0111 19.5157 16.3764 17.9844 15.917"
+                ></path>
+                <path
+                  d="M12.0002 16.8657C12.0002 16.8657 17.5005 13.271 17.5005 9.11522C17.5005 6.15182 15.0379 3.74951 12.0002 3.74951C8.96254 3.74951 6.5 6.15182 6.5 9.11522C6.5 13.271 12.0002 16.8657 12.0002 16.8657Z"
+                  stroke="rgba(0, 0, 0, 1)"
+                  stroke-width="1.5"
+                  stroke-linejoin="round"
+                ></path>
+                <path
+                  d="M11.9983 11.3653C13.1666 11.3653 14.1138 10.4181 14.1138 9.24979C14.1138 8.08144 13.1666 7.13428 11.9983 7.13428C10.83 7.13428 9.88281 8.08144 9.88281 9.24979C9.88281 10.4181 10.83 11.3653 11.9983 11.3653Z"
+                  stroke="rgba(0, 0, 0, 1)"
+                  stroke-width="1.5"
+                  stroke-linejoin="round"
+                ></path>
+              </svg>
+              <span className="text-ly-13 leading-ly-21 font-normal">
+                {item.address}, {item.city}, {item.postcode}, {item.country}
+              </span>
             </div>
           </div>
+          <div className="text-left mt-3 ">
+            <Link
+              href={`/customer/address/eidt/${item.id}`}
+              className="text-black-600 underline hover:text-blue-800"
+            >
+              Change Billing Address
+            </Link>
+          </div>
+          {!isLastItem && <hr className="border-black my-6" />}
         </div>
-      ))}
+       
+      )})}
     </>
   );
 }

+ 1 - 1
src/app/(public)/customer/address/new/_components/CustomerAddressPhone.tsx

@@ -61,7 +61,7 @@ export default function CustomerAddressPhone() {
 
   return (
     <div className="w-full mt-4">
-      <label className="text-ly-12 block mb-4 font-semibold">
+      <label className="text-ly-12 block mb-3 font-semibold">
         Phone number *
       </label>
       {/* ✅ Controller 本身写法没问题,保留即可,字段 name="phone" 和父表单一致 */}

+ 85 - 25
src/app/(public)/customer/address/page.tsx

@@ -1,31 +1,42 @@
 import React from "react";
 import Link from "next/link";
-import  DefaultAddress from "./_components/DefaultAddress";
+import DefaultAddress from "./_components/DefaultAddress";
 const CustomerAddressPage = async () => {
   // 模拟地址数据(可根据实际需求替换)
   const addressData = {
     billing: {
       name: "zhang xxxx",
+      first_name: "zhang",
+      last_name: "aaaaa",
       line1: "ddddd",
       line2: "aaaaaaaa",
       cityStateZip: "cscsc, Alabama, 90605",
+      address: "address",
+      city: "city",
+      postcode: "90603",
       country: "United States",
       phone: "2123165641",
     },
     shipping: {
       name: "zhang xxxx",
+      first_name: "zhang",
+      last_name: "aaaaa",
       line1: "ddddd",
       line2: "aaaaaaaa",
       cityStateZip: "cscsc, Alabama, 90605",
+      address: "address",
+      city: "city",
+      postcode: "90603",
       country: "United States",
       phone: "2123165641",
     },
   };
- 
+
   return (
     <div className="w-full h-full">
       <div className="bg-[#fff] pr-2.5 pl-2.5 w-full text-center text-base h-11 leading-11 font-semibold relative text-[#0b0b0b]">
-        <Link href={"/customer/account"}
+        <Link
+          href={"/customer/account"}
           // onClick={() => window.history.back()}
           className="absolute left-2.5 text-2xl inline-block top-1/2  -translate-y-1/2"
         >
@@ -48,36 +59,81 @@ const CustomerAddressPage = async () => {
         <span className="vipReturnTitles">SHIPPING ADDRESS</span>
       </div>
       <div className="max-w-md mx-auto p-4 bg-[#f8f8f8]">
-        <DefaultAddress  />
+        <DefaultAddress />
         {/* 账单地址模块 */}
         <div className="mb-6">
-          <h2 className="text-xl font-bold uppercase mb-3">
+          <h2 className="text-ly-14 leading-ly-22 font-normal mb-2">
             Default Billing Address
           </h2>
-          <div className="border border-gray-200 p-4">
-            <p className="mb-1">{addressData.billing.name}</p>
-            <p className="mb-1">{addressData.billing.line1}</p>
-            <p className="mb-1">{addressData.billing.line2}</p>
-            <p className="mb-1">{addressData.billing.cityStateZip}</p>
-            <p className="mb-1">{addressData.billing.country}</p>
-            <p className="mb-4">T: {addressData.billing.phone}</p>
-            <div className="text-right">
-              <a
-                href="#"
-                className="text-blue-600 underline hover:text-blue-800"
+
+          {/* 外层卡片容器,浅背景圆角 */}
+          <div className="border border-gray-200 rounded-xl bg-gray-50 px-4 py-3 bg-[url('/image/account/202609011640.png')] bg-no-repeat bg-size-[100%_100%]">
+            {/* 姓名 + 手机号行 */}
+            <div className="flex items-center text-ly-13 leading-ly-21 font-normal ">
+              <span>
+                {addressData.billing.first_name} {addressData.billing.last_name}
+              </span>
+              <span className="mx-2">|</span>
+              <span>+1 {addressData.billing.phone}</span>
+            </div>
+
+            {/* 分割线 */}
+            <hr className="border-black my-3" />
+
+            {/* SVG定位图标 + 地址行 */}
+            <div className="flex items-center gap-3 ">
+              {/* 位置图标 SVG */}
+
+              <svg
+                xmlns="http://www.w3.org/2000/svg"
+                width="24"
+                height="24"
+                viewBox="0 0 24 24"
+                fill="none"
               >
-                Change Billing Address
-              </a>
+                <path
+                  stroke="rgba(0, 0, 0, 1)"
+                  stroke-width="1.5"
+                  stroke-linejoin="round"
+                  stroke-linecap="round"
+                  d="M6.0175 15.917C4.4862 16.3764 3.53906 17.0111 3.53906 17.712C3.53906 19.1141 7.32759 20.2507 12.001 20.2507C16.6744 20.2507 20.4629 19.1141 20.4629 17.712C20.4629 17.0111 19.5157 16.3764 17.9844 15.917"
+                ></path>
+                <path
+                  d="M12.0002 16.8657C12.0002 16.8657 17.5005 13.271 17.5005 9.11522C17.5005 6.15182 15.0379 3.74951 12.0002 3.74951C8.96254 3.74951 6.5 6.15182 6.5 9.11522C6.5 13.271 12.0002 16.8657 12.0002 16.8657Z"
+                  stroke="rgba(0, 0, 0, 1)"
+                  stroke-width="1.5"
+                  stroke-linejoin="round"
+                ></path>
+                <path
+                  d="M11.9983 11.3653C13.1666 11.3653 14.1138 10.4181 14.1138 9.24979C14.1138 8.08144 13.1666 7.13428 11.9983 7.13428C10.83 7.13428 9.88281 8.08144 9.88281 9.24979C9.88281 10.4181 10.83 11.3653 11.9983 11.3653Z"
+                  stroke="rgba(0, 0, 0, 1)"
+                  stroke-width="1.5"
+                  stroke-linejoin="round"
+                ></path>
+              </svg>
+              <span className="text-ly-13 leading-ly-21 font-normal">
+                {addressData.billing.address}, {addressData.billing.city},{" "}
+                {addressData.billing.postcode}, {addressData.billing.country}
+              </span>
             </div>
           </div>
+
+          {/* 复选框 + Change Address */}
+          <div className="flex items-center gap-3 mt-5">
+            <input
+              type="checkbox"
+              className="w-10 h-10 border-2 border-black rounded bg-white"
+            />
+            <span className="text-2xl text-gray-500">Change Address</span>
+          </div>
         </div>
-        
+
         {/* 收货地址模块 */}
-        <div>
+        {/* <div>
           <h2 className="text-xl font-bold uppercase mb-3">
             Default Shipping Address
           </h2>
-          <div className="border border-gray-200 p-4">
+          <div className="border border-gray-200  p-4">
             <p className="mb-1">{addressData.shipping.name}</p>
             <p className="mb-1">{addressData.shipping.line1}</p>
             <p className="mb-1">{addressData.shipping.line2}</p>
@@ -93,12 +149,16 @@ const CustomerAddressPage = async () => {
               </a>
             </div>
           </div>
+        </div> */}
+        <div className="w-89.5">
+          <Link
+            href={"/customer/address/new"}
+            className="w-full h-12 leading-12 block  rounded-[26px] bg-[rgba(3,97,65,1)] text-base text-white font-bold text-center mt-7.5"
+          >
+            Add New Adress
+          </Link>
         </div>
-        <Link href={"/customer/address/new"} className="text-center bg-[#000] block text-white mt-5  width-full height-11 leading-11 text-lg" >
-          + New Address
-        </Link>
       </div>
-    
     </div>
   );
 };

+ 2 - 2
src/app/(public)/page.tsx

@@ -3,7 +3,7 @@
 // import HomeImageBanner from "@components/home/HomeImageBanner";
 // import { ThemeCustomizationResponse } from "@/types/theme/theme-customization";
 // import { cachedGraphQLRequest } from "@/utils/hooks/useCache";
-import { DatePicker } from "@heroui/date-picker";
+// import { DatePicker } from "@heroui/date-picker";
 import HomeContent from "./_components/HomeContent";
 import { restApiFetch } from "@utils/bagisto/index";
 import {HomeApiData} from "@/types/home/type"
@@ -32,7 +32,7 @@ const homeData: HomeApiData = res.body.data;
   return (
     <>
       {/* <HomeImageBanner /> */}
-      <DatePicker label={"Birth date"} labelPlacement={"outside"} />
+      {/* <DatePicker label={"Birth date"} labelPlacement={"outside"} /> */}
       {/* <RenderThemeCustomization themeCustomizations={data?.themeCustomizations ?? {edges: []}} /> */}
       <HomeContent homeData={homeData} />
     </>

+ 2 - 2
src/app/(public)/track_order/_components/TrackFaqs.tsx

@@ -39,7 +39,7 @@ export default function TrackFaqs() {
           <button
             type="button"
             onClick={() => toggleFaq(idx)}
-            className="w-full flex justify-between items-center px-6 py-5 text-left text-lg"
+            className="w-full flex justify-between items-center px-0 py-5 text-left text-lg"
           >
             <span>Q:{item.question}</span>
             {/* 上下箭头图标 */}
@@ -61,7 +61,7 @@ export default function TrackFaqs() {
           </button>
           {/* 回答内容,展开/收起 */}
           <div
-            className={`px-6 overflow-hidden transition-all duration-300 ${
+            className={`px-0 overflow-hidden transition-all duration-300 ${
               activeIndex === idx ? "max-h-[800px] pb-6" : "max-h-0"
             }`}
           >

+ 14 - 13
src/app/(public)/track_order/page.tsx

@@ -1,16 +1,16 @@
 import HeadBack from "@/components/head/HeadBack";
 import TrackOrder from "./_components/TrackOrder";
 import TrackFaqs from "./_components/TrackFaqs";
-
+import AccountSwiper from "@components/customer/accountSwiper";
 export default function TrackOrderPage() {
   return (
     <>
       <HeadBack />
-      <div className="min-h-screen bg-[#f0ede4] text-black">
-        <div className="max-w-3xl mx-auto px-4 py-8">
+      <div className="  text-black">
+        <div className="max-w-3xl mx-auto  pb-3 pt-0">
           {/* 顶部 Track Order 区域 */}
-          <section className="mb-10">
-            <h1 className="text-[clamp(1.8rem,4vw,2.5rem)] font-normal mb-6">
+          <section className="mb-3 bg-[#f0ede4] px-4 pt-8 pb-6">
+            <h1 className="leading-ly-28 font-normal text-ly-20 mb-6">
               Track Order
             </h1>
             <div className="space-y-3 mb-8 text-base leading-relaxed">
@@ -25,8 +25,8 @@ export default function TrackOrderPage() {
           </section>
 
           {/* FAQ 区域 */}
-          <section className="bg-white rounded-[8px] overflow-hidden">
-            <h2 className="text-[clamp(1.5rem,3vw,2rem)] font-normal p-6 pb-4">
+          <section className="bg-white rounded-lg overflow-hidden px-4">
+            <h2 className="leading-ly-28 font-normal text-ly-20 p-6 px-0 pb-4">
               People Also Ask
             </h2>
             {/* 问答 */}
@@ -34,14 +34,15 @@ export default function TrackOrderPage() {
           </section>
 
           {/* You May Like 标题区域 */}
-          <div className="mt-10">
-            <h2 className="text-[clamp(1.4rem,3vw,1.8rem)] font-normal">
-              You May Like
-            </h2>
-            {/* 下方可自行添加商品列表 */}
-          </div>
+       
         </div>
       </div>
+         <div className="">
+            <h2 className="px-4 mb-6 leading-ly-26 font-medium text-ly-18">
+              You May Also Like 
+            </h2>
+             <AccountSwiper />
+          </div>
     </>
   );
 }

+ 16 - 15
src/components/customer/SettingModal.tsx

@@ -19,14 +19,14 @@ export default function SettingModal({
       <div className="fixed inset-0 z-50 bg-black/50">
         {/* 弹框主体 - 从底部滑上来 */}
         <div className="absolute bottom-0 rounded-none left-0 right-0 w-full h-full bg-white  overflow-auto">
-          <div className="flex flex-col">
+          <div className="flex flex-col w-full h-full relative">
             {/* 顶部导航 */}
-            <header className="bg-black text-white py-4 px-4 flex items-center">
+            <header className=" text-black py-4 px-4 flex items-center border-b border-[#F9F9F9FF]">
               <button onClick={onClose} className="mr-4">
                 <svg
                   xmlns="http://www.w3.org/2000/svg"
                   className="h-6 w-6"
-                  fill="none"
+                  fill="#000"
                   viewBox="0 0 24 24"
                   stroke="currentColor"
                 >
@@ -34,26 +34,27 @@ export default function SettingModal({
                     strokeLinecap="round"
                     strokeLinejoin="round"
                     strokeWidth={2}
+                     fill="none"
                     d="M15 19l-7-7 7-7"
                   />
                 </svg>
               </button>
-              <h1 className="text-lg font-medium">Setting</h1>
+              <h1 className="text-lg w-full text-center  font-normal">Setting</h1>
             </header>
 
             {/* 头像区域 */}
-            <div className="bg-[#FFF5F0] py-8 flex flex-col items-center">
+            {/* <div className="bg-[#FFF5F0] py-8 flex flex-col items-center">
               <div className="w-20 h-20 bg-white rounded-full flex items-center justify-center border-2 border-black">
                 <span className="text-black font-bold text-lg">LOGO</span>
               </div>
-            </div>
+            </div> */}
 
             {/* 菜单列表 */}
-            <div className="px-4 py-2">
+            <div className="px-4 py-2  h-full relative">
               {/* Information */}
               <Link
                 href={"/customer/account/edit"}
-                className="py-4 border-b border-gray-100 flex justify-between items-center"
+                className="py-4 border-b border-gray-100 flex justify-between items-center bg-[#F9F9F9FF] mb-4  px-3"
               >
                 {/* <div > */}
                 <span className="text-black font-medium">Information</span>
@@ -76,7 +77,7 @@ export default function SettingModal({
               {/* Address */}
               <Link
                 href={"/customer/address"}
-                className="py-4 border-b border-gray-100 flex justify-between items-center"
+                className="py-4 border-b border-gray-100 flex justify-between items-center bg-[#F9F9F9FF] mb-4  px-3"
               >
                 {/* <div > */}
                 <span className="text-black font-medium">Address</span>
@@ -101,7 +102,7 @@ export default function SettingModal({
                 onClick={() => {
                   setShowSetting(true);
                 }}
-                className="py-4 border-b border-gray-100 flex justify-between items-center"
+                className="py-4 border-b border-gray-100 flex justify-between items-center bg-[#F9F9F9FF] mb-4  px-3"
               >
                 {/* <div > */}
                 <span className="text-black font-medium">help & Support</span>
@@ -122,23 +123,23 @@ export default function SettingModal({
                 {/* </div> */}
               </div>
               {/* Subscribe 开关 */}
-              <div className="py-4 border-b border-gray-100 flex justify-between items-center">
+              <div className="py-4 border-b border-gray-100 flex justify-between items-center bg-[#F9F9F9FF] px-3">
                 <span className="text-black font-medium">Subscribe</span>
-                <div className="relative w-12 h-6 rounded-full bg-black">
+                <div className="relative w-12 h-6 rounded-full bg-[#036141FF]">
                   <div className="absolute right-0.5 top-0.5 w-5 h-5 bg-white rounded-full"></div>
                 </div>
               </div>
 
               {/* Log Out */}
-              <div className="py-4 border-b border-gray-100">
-                <button className="text-black font-medium w-full text-left">
+              <div className="py-4  absolute left-1/2 -translate-x-1/2 bottom-7 ">
+                <button className="text-black font-medium  w-full underline text-center">
                   Log Out
                 </button>
               </div>
             </div>
 
             {/* 版本号 */}
-            <div className="py-6 text-center text-gray-400 text-sm">v1.1.0</div>
+            {/* <div className="py-6 text-center text-gray-400 text-sm">v1.1.0</div> */}
           </div>
         </div>
       </div>

+ 328 - 69
src/components/customer/accountSwiper.tsx

@@ -1,85 +1,344 @@
 "use client";
-import { Swiper, SwiperSlide } from 'swiper/react';
+// import { Swiper, SwiperSlide } from 'swiper/react';
+import { OpenAddToCartModalButton } from "@/components/common/AddToCartModal/OpenAddToCartModalButton";
 import Image from 'next/image';
-import 'swiper/css';
+// import 'swiper/css';
 // 模拟商品数据(和图片内容匹配)
-const productList = [
-  {
-    id: 1,
-    imgUrl: "https://cdn.alipearlhair.com/media/catalog/product/cache/6/thumbnail/750x1000/9df78eab33525d08d6e5fb8d27136e95/b/o/body_wave_lace_front_wig_2.jpg", // 替换成你的真实图片路径(public下)
-    title: "Widow's Peak M Hairline 13x6 HD Lace Front Wig N...",
-    price: "$132.91",
-    originalPrice: "$204.16",
-    tag: "Extra $50 OFF | Code: MW50 | This Week",
-  },
-  {
-    id: 2,
-    imgUrl: "https://cdn.alipearlhair.com/media/catalog/product/cache/6/thumbnail/750x1000/9df78eab33525d08d6e5fb8d27136e95/b/o/body_wave_lace_front_wig_2.jpg", // 替换成你的真实图片路径
-    title: "Alipearl Deep Wave Wig 100% Human Hair Swiss...",
-    price: "$149.66",
-    originalPrice: "$199.54",
-    tag: "", // 第二个商品无标签
-  },
-  // 可添加更多商品
-];
+const productList:any = [
+                    {
+                        "id": 3792,
+                        "sku": "BW--Closure",
+                        "name": "100% Brazilian Virgin Hair Body Wave HD Transparent Lace Closure ",
+                        "slug": null,
+                        "type": "flexible_variant",
+                        "price": 94.71,
+                        "min_price": 94.71,
+                        "special_price": null,
+                        "image_url": "https:www.asteriahair.com/media/catalog/product/b/o/body_13__1_1.jpg",
+                        "is_new": true,
+                        "is_featured": false,
+                        "short_description": "Asteria Hair 100% Brazilian Virgin Hair Body Wave Lace Closure, Free Part 4x4 Closure Can be Parted anywhere you like, transparent lace color and HD invisible lace to choose from, both color body wave hair lace closure fits all skin tone perfectly.",
+                        "rating": 5,
+                        "review_count": 10
+                    },
+                    {
+                        "id": 3843,
+                        "sku": "#613--STW--3+C--10A613",
+                        "name": "100% Human Hair 613 Blonde Brazilian Straight Hair 3 Bundles With Closure In Stock",
+                        "slug": null,
+                        "type": "flexible_variant",
+                        "price": 168.18,
+                        "min_price": 168.18,
+                        "special_price": null,
+                        "image_url": "https:www.asteriahair.com/media/catalog/product/6/1/613_3_.jpg",
+                        "is_new": true,
+                        "is_featured": false,
+                        "short_description": "Asteria Hair 613 Blonde Color Straight Human Hair Weave 3 Bundles With 4x4 Lace Closure Transparent Lace Color",
+                        "rating": 0,
+                        "review_count": 0
+                    },
+                    {
+                        "id": 3850,
+                        "sku": "#613--BW--4--10A613",
+                        "name": "100% Human Hair Body Wave 4 Bundles 613 Blonde Hair",
+                        "slug": null,
+                        "type": "flexible_variant",
+                        "price": 156.73,
+                        "min_price": 156.73,
+                        "special_price": null,
+                        "image_url": "https:www.asteriahair.com/media/catalog/product/b/l/blonde_hair.jpg",
+                        "is_new": true,
+                        "is_featured": false,
+                        "short_description": "100% Brazilian Virgin Human Hair Weaves Body Wave 4 Bundles 613 Blonde Hair For Sale",
+                        "rating": 0,
+                        "review_count": 0
+                    },
+                    {
+                        "id": 3790,
+                        "sku": "BW--3",
+                        "name": "100% Human Hair Natural Black Brazilian Body Wave Hair 8-30 Inch",
+                        "slug": null,
+                        "type": "flexible_variant",
+                        "price": 183.29,
+                        "min_price": 183.29,
+                        "special_price": null,
+                        "image_url": "https:www.asteriahair.com/media/catalog/product/b/o/body_wave_human_hair_bundles.png",
+                        "is_new": true,
+                        "is_featured": false,
+                        "short_description": "Unprocessed Brazilian Body Wave 3 Bundles Virgin Hair Body Wave Natural Black Hot Sale Asteria Hair Product",
+                        "rating": 0,
+                        "review_count": 0
+                    },
+                    {
+                        "id": 3868,
+                        "sku": "P--LD--3",
+                        "name": "100% Unprocessed Virgin Peruvian Human Hair Weave Loose Deep 3 PCS In Stock",
+                        "slug": null,
+                        "type": "flexible_variant",
+                        "price": 193.24,
+                        "min_price": 193.24,
+                        "special_price": null,
+                        "image_url": "https:www.asteriahair.com/media/catalog/product/3/_/3__1_9.jpg",
+                        "is_new": true,
+                        "is_featured": false,
+                        "short_description": "Asteria Hair 100% Unprocessed Peruvian Virgin Human Hair Weave 3 Bundles Deal Loose Deep Wave Weaves 3 PCs Hair Bundles",
+                        "rating": 5,
+                        "review_count": 17
+                    },
+                    {
+                        "id": 3835,
+                        "sku": "P--BW--4",
+                        "name": "100% Virgin Hair Natural Black Peruvian Body Wave Hair 8-30 Inch For Selling",
+                        "slug": null,
+                        "type": "flexible_variant",
+                        "price": 222.27,
+                        "min_price": 222.27,
+                        "special_price": null,
+                        "image_url": "https:www.asteriahair.com/media/catalog/product/x/y/xys_3917_2.jpg",
+                        "is_new": true,
+                        "is_featured": false,
+                        "short_description": "Unprocessed Peruvian Body Wave 4 Bundles Virgin Hair Body Wave Natural Black Top Selling Asteria Hair Bundles",
+                        "rating": 5,
+                        "review_count": 10
+                    },
+                    {
+                        "id": 3786,
+                        "sku": "test11111",
+                        "name": "12312312",
+                        "slug": null,
+                        "type": "flexible_variant",
+                        "price": 111,
+                        "min_price": 111,
+                        "special_price": null,
+                        "image_url": null,
+                        "is_new": true,
+                        "is_featured": true,
+                        "short_description": "<p>3324124124</p>",
+                        "rating": 0,
+                        "review_count": 0
+                    },
+                    {
+                        "id": 3807,
+                        "sku": "DW--Frontal",
+                        "name": "13*4 Ear to Ear Transparent HD Lace Frontal Human Brazilian Hair Deep Wave Hair",
+                        "slug": null,
+                        "type": "flexible_variant",
+                        "price": 121.75,
+                        "min_price": 121.75,
+                        "special_price": null,
+                        "image_url": "https:www.asteriahair.com/media/catalog/product/0/2/02_4_3.jpg",
+                        "is_new": true,
+                        "is_featured": false,
+                        "short_description": "Asteria 13*4 Lace Frontal Human Hair Brazilian Hair Sale Deep Wave Hair Cheap Brazilian Hair Ear To Ear Lace Frontal Human Hair",
+                        "rating": 5,
+                        "review_count": 3
+                    },
+                    {
+                        "id": 3860,
+                        "sku": "#T1 B613--BW--3+F--10A",
+                        "name": "1B 613 Ombre Human Virgin Hair Body Wave Hair 3 Bundles with 13*4 Lace Frontal",
+                        "slug": null,
+                        "type": "flexible_variant",
+                        "price": 199.24,
+                        "min_price": 199.24,
+                        "special_price": null,
+                        "image_url": "https:www.asteriahair.com/media/catalog/product/1/b/1b-613-blonde-bundles-with-frontal.jpg",
+                        "is_new": true,
+                        "is_featured": false,
+                        "short_description": "Asteria Hair #1B\/613 Ombre Blonde Hair Color 100% Human Body Wave Hair 3 Bundles with Ear to Ear Lace Frontal Deal",
+                        "rating": 0,
+                        "review_count": 0
+                    },
+                    {
+                        "id": 3800,
+                        "sku": "STW--3+F",
+                        "name": "1pc Ear To Ear Lace Frontal with 3 Bundles Straight Virgin Brazilian Hair Weft",
+                        "slug": null,
+                        "type": "flexible_variant",
+                        "price": 281.86,
+                        "min_price": 281.86,
+                        "special_price": null,
+                        "image_url": "https:www.asteriahair.com/media/catalo/product/3/_/3_11_1.jpg",
+                        "is_new": true,
+                        "is_featured": false,
+                        "short_description": "High Quality Asteria Products 1pc Ear To Ear Lace Frontal Closure Totally Hand Made Pre-Plucked Hairline With Baby Hair 3 Bundles Human Straight Virgin Brazilian Hair Weft Natural Black Tangle Free No Splits",
+                        "rating": 0,
+                        "review_count": 0
+                    },
+                ]
+// 五星星星渲染
+  const renderStars = (count: number) => {
+    return Array.from({ length: count }).map((_, idx) => (
+      <svg
+        key={idx}
+        className="w-4 h-4 text-green-600 inline-block"
+        viewBox="0 0 24 24"
+        fill="currentColor"
+      >
+        <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
+      </svg>
+    ));
+  };
 const AccountSwiper = () => {
   return (
     <div className="w-full max-w-[700px] mx-auto px-4">
-      {/* Swiper 核心容器 */}
-      <Swiper
-     
-        slidesPerView={2} // 一行显示2个商品(和图片一致)
-        spaceBetween={8} // 商品之间的间距
-        breakpoints={{
-          // 响应式适配(小屏显示1个)
-          390: { slidesPerView: 2 },
-          640: { slidesPerView: 2 },
-        }}
-        className="product-swiper"
-      >
-        {/* 循环渲染商品卡片 */}
-        {productList.map((product) => (
-          <SwiperSlide key={product.id} className="rounded-lg overflow-hidden">
-            <div className="flex flex-col h-full">
-              {/* 商品图片(带标签) */}
-              <div className="relative w-full aspect-[3/4]">
-                <Image
-                  src={product.imgUrl}
-                  alt={product.title}
-                  fill
-                  className="w-full h-auto object-cover rounded-t-lg"
-                />
-                {/* 促销标签(有内容才显示) */}
-                {product.tag && (
-                  <div className="absolute bottom-2 left-2 bg-black/80 text-white text-xs px-2 py-1 rounded">
-                    {product.tag}
+       {/* 商品2列网格 */}
+            <div className="grid grid-cols-2 gap-4">
+              {productList.map((item) => (
+                <div key={item.id} className="flex flex-col gap-3">
+                  {/* Next.Image 商品图容器 */}
+                  <div className="relative w-full aspect-[3/4] overflow-hidden rounded-md">
+                    <Image
+                      src={item.image_url}
+                      alt={item.name}
+                      fill
+                      sizes="(max-width:768px) 50vw, 33vw"
+                      className="object-cover"
+                    />
                   </div>
-                )}
-              </div>
-
-              {/* 商品标题 */}
-              <p className="mt-3 text-sm text-gray-800 font-medium line-clamp-2">
-                {product.title}
-              </p>
-
-              {/* 价格区域 */}
-              <div className="flex items-center gap-2 mt-2">
-                <span className="text-red-600 font-bold">{product.price}</span>
-                <span className="text-xs text-gray-500 line-through">
-                  {product.originalPrice}
-                </span>
-                {/* 购物车图标 */}
-                <span className="ml-auto text-gray-600">🛒</span>
-              </div>
+      
+                  {/* 标题 */}
+                  <p className="text-sm text-gray-800 truncate">{item.name}</p>
+      
+                  {/* 星级 + 评论数 */}
+                  <div className="flex items-center gap-1">
+                    {renderStars(item.rating)}
+                    <span className="text-sm text-gray-600 ml-1 underline">
+                      {item.review_count}
+                    </span>
+                  </div>
+                  {/* 价格 + 购物车图标 */}
+                  <div className="flex items-center justify-between">
+                    <div className="flex items-center gap-1">
+                      <span className="font-semibold">${item.min_price}</span>
+                      <span className="text-xs text-gray-500 line-through">
+                        ${item.price}
+                      </span>
+                    </div>
+                    {/* 购物车图标 */}
+                    <OpenAddToCartModalButton
+                      productId={
+                        item.id
+                      }
+                      icon={
+                        <svg
+                          xmlns="http://www.w3.org/2000/svg"
+                          width="32"
+                          height="32"
+                          viewBox="0 0 32 32"
+                          fill="none"
+                        >
+                          <rect
+                            x="0"
+                            y="0"
+                            width="32"
+                            height="32"
+                            rx="8"
+                            fill="#036141"
+                          ></rect>
+                          <path
+                            stroke="rgba(255, 255, 255, 1)"
+                            stroke-width="1.3333333333333333"
+                            stroke-linejoin="round"
+                            stroke-linecap="round"
+                            d="M6.40039 7.79993L8.03372 7.79993L8.73372 10.5999M8.73372 10.5999L11.0671 19.9333L23.2004 19.9333L25.5337 10.5999L8.73372 10.5999Z"
+                          ></path>
+                          <ellipse
+                            cx="11.066040515899658"
+                            cy="23.199928283691406"
+                            rx="1.4000248908996582"
+                            ry="1.4000015258789062"
+                            stroke="rgba(255, 255, 255, 1)"
+                            stroke-width="1.3333333333333333"
+                            stroke-linejoin="round"
+                            stroke-linecap="round"
+                          ></ellipse>
+                          <ellipse
+                            cx="23.1998291015625"
+                            cy="23.199928283691406"
+                            rx="1.4000244140625"
+                            ry="1.4000015258789062"
+                            stroke="rgba(255, 255, 255, 1)"
+                            stroke-width="1.3333333333333333"
+                            stroke-linejoin="round"
+                            stroke-linecap="round"
+                          ></ellipse>
+                          <path
+                            stroke="rgba(255, 255, 255, 1)"
+                            stroke-width="1.3333333333333333"
+                            stroke-linejoin="round"
+                            stroke-linecap="round"
+                            d="M15.2656 15.2665L18.999 15.2665"
+                          ></path>
+                          <path
+                            stroke="rgba(255, 255, 255, 1)"
+                            stroke-width="1.3333333333333333"
+                            stroke-linejoin="round"
+                            stroke-linecap="round"
+                            d="M17.1348 17.1332L17.1348 13.3999"
+                          ></path>
+                        </svg>
+                      }
+                    />
+                  </div>
+                </div>
+              ))}
             </div>
-          </SwiperSlide>
-        ))}
-      </Swiper>
+      {/* Swiper 核心容器 */}
+   
 
       {/* 分页器容器(底部小圆点) */}
       {/* <div className="swiper-pagination flex justify-center mt-4"></div> */}
     </div>
   );
 };
+  //  <Swiper
+     
+  //       slidesPerView={2} // 一行显示2个商品(和图片一致)
+  //       spaceBetween={8} // 商品之间的间距
+  //       breakpoints={{
+  //         // 响应式适配(小屏显示1个)
+  //         390: { slidesPerView: 2 },
+  //         640: { slidesPerView: 2 },
+  //       }}
+  //       className="product-swiper"
+  //     >
+  //       {/* 循环渲染商品卡片 */}
+  //       {productList.map((product) => (
+  //         <SwiperSlide key={product.id} className="rounded-lg overflow-hidden">
+  //           <div className="flex flex-col h-full">
+  //             {/* 商品图片(带标签) */}
+  //             <div className="relative w-full aspect-[3/4]">
+  //               <Image
+  //                 src={product.imgUrl}
+  //                 alt={product.title}
+  //                 fill
+  //                 className="w-full h-auto object-cover rounded-t-lg"
+  //               />
+  //               {/* 促销标签(有内容才显示) */}
+  //               {product.tag && (
+  //                 <div className="absolute bottom-2 left-2 bg-black/80 text-white text-xs px-2 py-1 rounded">
+  //                   {product.tag}
+  //                 </div>
+  //               )}
+  //             </div>
+
+  //             {/* 商品标题 */}
+  //             <p className="mt-3 text-sm text-gray-800 font-medium line-clamp-2">
+  //               {product.title}
+  //             </p>
 
+  //             {/* 价格区域 */}
+  //             <div className="flex items-center gap-2 mt-2">
+  //               <span className="text-red-600 font-bold">{product.price}</span>
+  //               <span className="text-xs text-gray-500 line-through">
+  //                 {product.originalPrice}
+  //               </span>
+  //               {/* 购物车图标 */}
+  //               <span className="ml-auto text-gray-600">🛒</span>
+  //             </div>
+  //           </div>
+  //         </SwiperSlide>
+  //       ))}
+  //     </Swiper>
 export default AccountSwiper;

+ 226 - 17
src/components/layout/navbar/MobileMenu.tsx

@@ -3,49 +3,116 @@
 import { AnimatePresence, motion } from "framer-motion";
 import Link from "next/link";
 
-import { MobileSearchBar } from "./MobileSearch";
+// import { MobileSearchBar } from "./MobileSearch";
 import { useBodyScrollLock } from "@utils/hooks/useBodyScrollLock";
-
+import{useState} from "react";
+import {useLoginModal} from "@/providers/LoginModalProvider";
+import { useCustomToast } from "@/utils/hooks/useToast";
+import { confirmDialog } from "@/components/theme/ui/kernel/confirm/api";
+import { useRouter } from 'next/navigation';
+import Image from "next/image";
+import type { TreefrontMenu } from "@/types/theme/menus";
+import {CurrencySwitch} from "@/components/common/CurrencySwitch/CurrencySwitch";
 interface MobileMenuProps {
-  menu: any[];
+  menu: TreefrontMenu[];
   isOpen: boolean;
   onClose: () => void; // 接收关闭函数
+  isGuest:boolean;
 }
-export default function MobileMenu({ menu, isOpen, onClose }: MobileMenuProps) {
+export default function MobileMenu({ menu, isOpen, onClose,isGuest }: MobileMenuProps) {
+   const {openLoginModal} = useLoginModal();
+   const { showToast } = useCustomToast();
+   const router = useRouter();
+  // 当前选中一级菜单ID
+  const [activeMainId, setActiveMainId] = useState(menu[0]._id);
+  // 存储右侧二级菜单展开状态集合 key:二级id  value:是否展开boolean
+  const [expandedSubIds, setExpandedSubIds] = useState<Record<string, boolean>>({});
 
+  // 获取选中的一级菜单
+  const activeMainMenu = menu.find((item) => item._id === activeMainId);
+  // 当前一级下面全部二级菜单列表
+  const subMenuList = activeMainMenu?.children?.edges?.map((e) => e.node) ?? [];
 
+  // 切换右侧二级菜单展开/收起
+  const toggleSubExpand = (subId: number) => {
+    setExpandedSubIds((prev) => ({
+      ...prev,
+      [subId]: !prev[subId],
+    }));
+  };
   useBodyScrollLock(isOpen);
-
+  const guestUserClick = () => {
+          openLoginModal({
+              onFinalResult: async (res) => {
+                  if(res.mergeCartSuccess) { // 购物车合并成功
+                      // if(!res.cartAfterMerge?.isVip && res.cartAfterMerge?.vipPlusAmount === 0) {
+                      //     await switchVipPlus(true);
+                      // }
+                      return
+                  } 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');
+                      }
+                  }
+              }
+          });
+      };
 
   return (
     <>
       <AnimatePresence>
-        {isOpen && (
+        {/* {isOpen && ( */}
           <>
             <motion.div
-              initial={{ opacity: 0 }}
-              animate={{ opacity: 1 }}
-              exit={{ opacity: 0 }}
+              // initial={{ opacity: 0 }}
+              // animate={{ opacity: 1 }}
+              initial={false}
+              animate={{
+                opacity: isOpen ? 1 : 0,
+                pointerEvents: isOpen ? "auto" : "none"
+              }}
+              // exit={{ opacity: 0 }}
               onClick={onClose}
               className="fixed inset-0 z-40 bg-transparent lg:hidden"
               style={{ top: "68px", bottom: "64px" }}
             />
 
             <motion.div
-              initial={{ x: "-100%" }}
-              animate={{ x: 0 }}
-              exit={{ x: "-100%" }}
+              // initial={{ x: "-100%" }}
+              // animate={{ x: 0 }}
+              // exit={{ x: "-100%" }}
+              initial={false}
+              animate={{
+                x: isOpen ? 0 : "-100%"
+              }}
               transition={{ type: "spring", damping: 25, stiffness: 200 }}
               className="fixed left-0 z-50 w-full max-w-[448px] border-r border-neutral-200 bg-white dark:border-neutral-800 dark:bg-black lg:hidden drawer-scrollbar-hidden"
               style={{
-                top: "68px",
-                bottom: "64px",
+                top: "0px",
+                bottom: "0px",
                 width: "100%",
                 maxWidth: "448px",
                 height: "calc(var(--visual-viewport-height) - 132px)",
               }}
             >
-              <div className="h-full overflow-y-auto px-4 py-4 drawer-scrollbar-hidden">
+              {/* <div className="h-full overflow-y-auto px-4 py-4 drawer-scrollbar-hidden">
                 <MobileSearchBar onClose={onClose} />
 
                 <h1 className="mt-4 px-2 text-2xl font-semibold text-black dark:text-white">
@@ -68,10 +135,152 @@ export default function MobileMenu({ menu, isOpen, onClose }: MobileMenuProps) {
                     </li>
                   ))}
                 </ul>
-              </div>
+              </div> */}
+             <div className="h-screen flex flex-col overflow-hidden bg-white">
+             
+               <div className="flex justify-between items-center px-4 h-13.5 border-b border-[#E1E1E1FF]">
+                 <h2 className="text-base font-medium">VIP Rewards</h2>
+                 <div className="flex gap-3 items-center">
+                  {
+                    isGuest ? (
+                         <button onClick={guestUserClick} className="bg-emerald-700 text-white w-25.75 h-9 flex items-center justify-center  rounded-full text-xs font-normal">
+                            Register/Login
+                          </button>
+                    ) : null
+                  }
+                   <svg
+                     width="24"
+                     height="24"
+                     viewBox="0 0 24 24"
+                     fill="none"
+                     stroke="currentColor"
+                     strokeWidth="2"
+                    onClick={onClose}
+                   >
+                     <path d="M18 6L6 18M6 6l12 12" />
+                   </svg>
+                 </div>
+               </div>
+                       
+               <div className="flex flex-1 overflow-hidden">
+               
+                 <aside className="w-23 border-r border-gray-200 overflow-y-auto">
+                   {menu.map((mainItem) => (
+                     <div
+                       key={mainItem._id}
+                       onClick={() => setActiveMainId(mainItem._id)}
+                       className={`py-3 pl-4 pr-3   text-xs cursor-pointer border-r-2 border-b border-[#E1E1E1FF]
+                         ${
+                           activeMainId === mainItem._id
+                             ? "border-r-emerald-700 text-emerald-700 font-bold"
+                             : " text-gray-700 font-normal"
+                         }
+                       `}
+                     >
+                       {mainItem.name} {mainItem.showIcon&&(<Image
+                                     className=" block"
+                                     src="/image/theme/image_flame.webp"
+                                     alt={"userphoto"}
+                                     width={16}
+                                     height={16}
+                                   />)}
+                     </div>
+                   ))}
+                 </aside>
+                 
+          
+                 <main className="flex-1 overflow-y-auto p-3 pr-4">
+                   {/*顶部横幅占位 */}
+                   <div className="w-full  bg-gray-100 mb-4 flex items-end">
+                     {/* <span className="text-3xl text-white pl-6 pb-4">Shop By Feature</span> */}
+                      <Image
+                         src={activeMainMenu?.wapImage||''}
+                         alt={activeMainMenu?.name||''}
+                         className="object-cover"
+                         width={269}
+                         height={37}
+                       />
+                   </div>
+                 
+                   {subMenuList.length === 0 ? (
+                     <div className="text-gray-500 text-xl">No sub categories</div>
+                   ) : (
+                     subMenuList.map((subItem) => {
+                       const isOpen = !!expandedSubIds[subItem._id];
+                      
+                       const cardEdges = subItem?.children?.edges ?? [];
+                     
+                       return (
+                         <div key={subItem._id} className="mb-6">
+                          
+                           <div
+                             onClick={() => toggleSubExpand(subItem._id)}
+                             className="flex justify-between items-center cursor-pointer py-2  px-3 h-10 bg-[#F9F9F9FF]"
+                           >
+                           <h3 className="text-2xl font-medium">{subItem.name}</h3>
+                            <svg xmlns="http://www.w3.org/2000/svg"  width="24" height="24" viewBox="0 0 24 24" fill="none" 
+                            className={`transition-transform duration-300 ${isOpen ? "rotate-0" : "rotate-180"}`}
+                            >
+                             <path    stroke="rgba(0, 0, 0, 1)" stroke-width="1.5" stroke-linejoin="round" stroke-linecap="square"  d="M18 15L12 9L6 15"></path>
+                            </svg>
+                           </div>
+                       
+                           {/*二级展开之后渲染三级卡片网格 */}
+                           {isOpen && cardEdges.length > 0 && (
+                             <div className="grid grid-cols-3 gap-2 pt-3">
+                               {cardEdges.map((edge) => {
+                                 const cardItem = edge.node;
+                                 return (
+                                   <div
+                                     key={cardItem._id}
+                                     className="flex flex-col items-center cursor-pointer"
+                                   >
+                                     <div className="w-full relative  bg-gray-200 mb-1 overflow-hidden">
+                                     
+                                         <Image
+                                           src={cardItem.image||''}
+                                           alt={cardItem.name||''}
+                                           width={84}
+                                           height={112}
+                                           className="object-cover"
+                                         />
+                                         {
+                                          cardItem.showIcon&&(
+                                             <Image
+                                                className=" block absolute top-0 left-0"
+                                                src="/image/theme/image_hot.webp"
+                                                alt={"userphoto"}
+                                                width={30}
+                                                height={14}
+                                              />
+                                          )
+                                         }
+                                     </div>
+                                     <span className="text-xs font-normal text-center">{cardItem.name}</span>
+                                   </div>
+                                 );
+                               })}
+                             </div>
+                           )}
+                           {isOpen && cardEdges.length === 0 && (
+                             <div className="py-3 text-gray-500">No feature items</div>
+                           )}
+                         </div>
+                       );
+                     })
+                   )}
+                 </main>
+               </div>
+                 
+               {/* =========底部固定货币选择栏 ========= */}
+               <div className="h-12 border-t border-gray-200 flex items-center px-4 gap-4">
+                  <CurrencySwitch/>
+               </div>
+             </div>
+
             </motion.div>
           </>
-        )}
+        {/* )} */}
       </AnimatePresence>
     </>
   );

+ 3 - 2
src/components/layout/navbar/MobileMenuTrigger.tsx

@@ -2,8 +2,9 @@
 
 import { useState } from "react";
 import MobileMenu from "./MobileMenu";
+import type { TreefrontMenu } from "@/types/theme/menus";
 
-export default function MobileMenuTrigger({ menuData }: { menuData: any[] }) {
+export default function MobileMenuTrigger({ menuData,isGuest }: { menuData: TreefrontMenu[],isGuest:boolean }) {
   const [isOpen, setIsOpen] = useState(false);
   const toggleMenu = () => setIsOpen((prev) => !prev);
   const closeMenu = () => setIsOpen(false);
@@ -21,7 +22,7 @@ export default function MobileMenuTrigger({ menuData }: { menuData: any[] }) {
       </button>
 
       {/* 抽屉菜单 */}
-      <MobileMenu menu={menuData} isOpen={isOpen} onClose={closeMenu} />
+      <MobileMenu menu={menuData} isGuest={isGuest} isOpen={isOpen} onClose={closeMenu} />
     </>
   );
 }

+ 40 - 27
src/components/layout/navbar/index.tsx

@@ -9,36 +9,49 @@ import { NavigationSkeleton } from "./NavigationSkeleton";
 import { ActionsSkeleton } from "./ActionsSkeleton";
 import { NavbarErrorBoundary } from "@/components/error/ErrorBoundary";
 
-import { GET_TREE_CATEGORIES } from "@/graphql";
-import { cachedGraphQLRequest } from "@utils/hooks/useCache";
-import { TreeCategoriesResponse } from "@/types/theme/category-tree";
+import { GET_TREE_CATEGORIES,GET_TREEFRONT_MENUS } from "@/graphql";
+// import { cachedGraphQLRequest } from "@utils/hooks/useCache";
+import { serverGraphqlFetch } from "@utils/bagisto/index";
+// import { TreeCategoriesResponse } from "@/types/theme/category-tree";
 import NavbarSearchIcon from "./NavbarSearchIcon"
+import type { GetTreefrontMenusResult } from "@/types/theme/menus";
+import { authOptions } from "@utils/auth";
+import { getServerSession } from "next-auth";
 export default async function Navbar() {
-  const { data } = await cachedGraphQLRequest<TreeCategoriesResponse>(
-    "category",
-    GET_TREE_CATEGORIES,
-    { parentId: 1 },
-  );
-  console.log('TreeCategoriesResponse --- ',data)
-  const categories = data?.treeCategories || [];
 
-  const filteredCategories = categories
-    .filter((cat: any) => cat.id !== "1")
-    .map((cat: any) => {
-      const translation = cat.translation;
-      return {
-        id: cat.id,
-        _id: cat._id,
-        name: translation?.name || "",
-        slug: translation?.slug || "",
-      };
-    })
-    .filter((item: any) => item.name && item.slug);
+    const session = await getServerSession(authOptions);// 游客是null
+    const isGuest = session === null;
+  // const { data } = await cachedGraphQLRequest<TreeCategoriesResponse>(
+  //   "category",
+  //   GET_TREE_CATEGORIES,
+  //   { parentId: 1 },
+  // );
+  // console.log('TreeCategoriesResponse --- ',data)
+  // const categories = data?.treeCategories || [];
+   const { data: menudata } = await serverGraphqlFetch<
+  GetTreefrontMenusResult
+>({
+  query: GET_TREEFRONT_MENUS,
+});
+console.log('GetTreefrontMenusResult --- ',menudata)
+  const menudatas = menudata.treefrontMenus;
+  // const filteredCategories = categories
+  //   .filter((cat: any) => cat.id !== "1")
+  //   .map((cat: any) => {
+  //     const translation = cat.translation;
+  //     return {
+  //       id: cat.id,
+  //       _id: cat._id,
+  //       name: translation?.name || "",
+  //       slug: translation?.slug || "",
+  //     };
+  //   })
+  //   .filter((item: any) => item.name && item.slug);
 
-  const menuData = [
-    { id: "all", name: "All", slug: "" },
-    ...filteredCategories.slice(0, 3),
-  ];
+  // const menuData = [
+  //   { id: "all", name: "All", slug: "" },
+  //   ...filteredCategories.slice(0, 3),
+  // ];
 
   return (
     <NavbarErrorBoundary>
@@ -49,7 +62,7 @@ export default async function Navbar() {
             <div className="flex items-center max-w-fit gap-5 xl:gap-6">
               {/* 2. STATIC HOLE: Categories (Suspended) */}
               <Suspense fallback={<NavigationSkeleton />}>
-                <MobileMenuTrigger menuData={menuData} />
+                <MobileMenuTrigger isGuest={isGuest} menuData={menudatas} />
               </Suspense>
               <div className="flex-1 justify-center flex">
                 {/* <Suspense fallback={<SearchSkeleton />}>

+ 5 - 5
src/components/theme/filters/NewSortOrder.tsx

@@ -17,7 +17,7 @@ interface NewSortOrderProps {
   sortOrders: SortOrderTypes[];
   title: string;
   value: string;
-  onChange: (sort: string) => void;
+  onChange: (sortValue: string,sortKey: string,reverse:boolean) => void;
 }
 
 const NewSortOrder: FC<NewSortOrderProps> = ({
@@ -28,8 +28,8 @@ const NewSortOrder: FC<NewSortOrderProps> = ({
 }) => {
   const { isOpen, onOpen, onOpenChange } = useDisclosure();
 
-  const handleSortChange = (sort: string) => {
-    onChange(sort);
+  const handleSortChange = (sortValue: string,sortKey: string,reverse:boolean) => {
+    onChange(sortValue,sortKey,reverse);
     onOpenChange();
   };
 
@@ -71,11 +71,11 @@ const NewSortOrder: FC<NewSortOrderProps> = ({
             {/* 列表容器,完全复刻截图样式 */}
             <div className="flex flex-col">
               {sortOrders.map((order) => {
-                const isActive = value === order.value;
+                const isActive = value === order.key;
                 return (
                   <button
                     key={order.value}
-                    onClick={() => handleSortChange(order.value)}
+                    onClick={() => handleSortChange(order.key,order.sortKey,order.reverse)}
                     className={`w-full text-center py-4 text-xl transition-colors ${
                       isActive
                         ? "bg-neutral-100 dark:bg-neutral-800 font-semibold"

+ 1 - 1
src/components/theme/ui/PhoneNumberInput/PhoneNumberInput.tsx

@@ -168,7 +168,7 @@ function PhoneNumberInput({
                 value={value}
                 ref={ref}
             />
-            <div className="flex gap-4 box-border border-1 border-ly-inputborder">
+            <div className="flex gap-4 box-border border-1 border-ly-inputborder rounded-lg">
                 <div className="px-3.75" onClick={handleShowCodeList}>
                     <div className="h-11.5 text-ly-12 flex items-center">
                         

+ 3 - 3
src/graphql/catalog/queries/GetCategoryProducts.ts

@@ -1,8 +1,8 @@
 import { gql } from "@apollo/client";
-// graphql 查询文本
+
 export const CATEGORY_PRODUCTS = gql`
-query CategoryProducts($slug: String!, $filter: String, $first: Int, $after: String) {
-  categoryProducts(slug: $slug, filter: $filter, first: $first, after: $after) {
+query CategoryProducts($slug: String!, $filter: String, $sortKey: String, $reverse: Boolean, $first: Int, $after: String) {
+  categoryProducts(slug: $slug, filter: $filter, sortKey: $sortKey, reverse: $reverse, first: $first, after: $after) {
     totalCount
     pageInfo {
      endCursor     

+ 24 - 0
src/graphql/customer/mutations/UpdataCustomerInformation.ts

@@ -0,0 +1,24 @@
+import { gql, TypedDocumentNode } from "@apollo/client";
+import { UpdateCustomerProfileData, UpdateCustomerProfileVars } from "@/types/customer/information/information";
+export const UPDATE_CUSTOMER_PROFILE: TypedDocumentNode<UpdateCustomerProfileData, UpdateCustomerProfileVars> = gql`
+mutation updateCustomerProfile($input: createCustomerProfileUpdateInput!) {
+  createCustomerProfileUpdate(input: $input) {
+    customerProfileUpdate {
+      id
+      firstName
+      lastName
+      email
+      phone
+      gender
+      dateOfBirth
+      status
+      subscribedToNewsLetter
+      isVerified
+      isSuspended
+      image
+      success
+      message
+    }
+  }
+}
+`;

+ 1 - 0
src/graphql/customer/mutations/index.ts

@@ -3,3 +3,4 @@ export { CUSTOMER_LOGIN} from "./CustomerLogin";
 export { CUSTOMER_LOGOUT} from "./CustomerLogout";
 export { VERIFY_CUSTOMER} from "./VerifyCustomer";
 export {FORGET_PASSWORD} from "./ForgetPassword"
+export {UPDATE_CUSTOMER_PROFILE} from "./UpdataCustomerInformation"

+ 20 - 0
src/graphql/customer/query/GetCustomerInformation.ts

@@ -0,0 +1,20 @@
+import { gql } from "@apollo/client";
+
+// graphql 查询文本
+export const GET_CUSTOMER_PROFILE = gql`
+query getCustomerProfile {
+  readCustomerProfile {
+    id
+    firstName
+    lastName
+    email
+    dateOfBirth
+    gender
+    phone
+    status
+    subscribedToNewsLetter
+    isVerified
+    image
+  }
+}
+`;

+ 2 - 1
src/graphql/customer/query/index.ts

@@ -1,4 +1,5 @@
 export {
     GET_ORDER_DETAILS
 } from "./GetOrderDetails";
-export {GET_CUSTOMER_ADDRESS} from "./GetCustomerAddress";
+export {GET_CUSTOMER_ADDRESS} from "./GetCustomerAddress";
+export { GET_CUSTOMER_PROFILE } from "./GetCustomerInformation";

+ 54 - 0
src/graphql/theme/queries/GetTreeFrontMenus.ts

@@ -0,0 +1,54 @@
+// @/graphql/queries/getTreefrontMenus.ts
+import { gql } from "@apollo/client";
+
+export const GET_TREEFRONT_MENUS = gql`
+  query GetTreefrontMenus {
+    treefrontMenus {
+      _id
+      name
+      slug
+      url
+      showIcon
+      image
+      wapImage
+      banner
+      wapBanner
+      sortOrder
+      status
+      children {
+        edges {
+          node {
+            _id
+            name
+            slug
+            url
+            image
+            wapImage
+            banner
+            wapBanner
+            sortOrder
+            status
+            children {
+              edges {
+                node {
+                  _id
+                  name
+                  slug
+                  url
+                  showIcon
+                  image
+                  wapImage
+                  banner
+                  wapBanner
+                  sortOrder
+                  status
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+`;
+

+ 3 - 0
src/graphql/theme/queries/index.ts

@@ -9,3 +9,6 @@ GET_THEME_CUSTOMIZATION
 export {
     PAGE_BY_URL_KEY
 } from "./PageByUrlKey"
+export {
+    GET_TREEFRONT_MENUS
+} from "./GetTreeFrontMenus"

+ 56 - 0
src/types/customer/information/information.ts

@@ -0,0 +1,56 @@
+import { CalendarDate } from "@internationalized/date";
+/** 用户资料 */
+interface ReadCustomerProfile {
+  id: string;
+  firstName: string;
+  lastName: string;
+  email: string;
+  dateOfBirth: string |null | CalendarDate;
+  gender: string;
+  phone: string;
+  status: string;
+  subscribedToNewsLetter: boolean;
+  isVerified: string;
+  image: null | string;
+}
+
+/** GraphQL 查询返回根类型 */
+export interface CustomerProfileResult {
+  readCustomerProfile: ReadCustomerProfile;
+}
+// Mutation 返回数据类型
+export type UpdateCustomerProfileData = {
+  createCustomerProfileUpdate: {
+    customerProfileUpdate: {
+      id: string;
+      firstName: string;
+      lastName: string;
+      email: string;
+      phone: string | null;
+      gender: string | null;
+      dateOfBirth: string | null | CalendarDate;
+      status: string;
+      subscribedToNewsLetter: boolean;
+      isVerified: string;
+      isSuspended: string;
+      image: string | null;
+      success: boolean;
+      message: string;
+    };
+  };
+};
+
+// 请求变量类型‑传给后端 input
+export type UpdateCustomerProfileVars = {
+  input: {
+    firstName?: string;
+    lastName?: string;
+    email?: string;
+    phone?: string | null;
+    gender?: string | null;
+    dateOfBirth?: string | null | CalendarDate ;
+    subscribedToNewsLetter?: boolean;
+    current_password?: string;
+    password?: string;
+  };
+};

+ 38 - 0
src/types/theme/menus.ts

@@ -0,0 +1,38 @@
+
+export type MenuChildNode = {
+  _id: number;
+  name: string;
+  slug: string;
+  url: string;
+  showIcon:boolean,
+  image: string | null;
+  wapImage: string | null;
+  banner: string | null;
+  wapBanner: string | null;
+  sortOrder: number;
+  status: string;
+  children: {
+    edges: { node: MenuChildNode }[];
+  };
+};
+
+export type TreefrontMenu = {
+  _id: number;
+  name: string;
+  slug: string;
+  url: string;
+  image: string | null;
+  wapImage: string | null;
+  banner: string | null;
+  wapBanner: string | null;
+  sortOrder: number;
+  status: string;
+  showIcon:boolean,
+  children: {
+    edges: { node: MenuChildNode }[];
+  };
+};
+
+export type GetTreefrontMenusResult = {
+  treefrontMenus: TreefrontMenu[];
+};

+ 44 - 0
src/utils/constants.ts

@@ -105,6 +105,50 @@ export const SortByFields: SortOrderTypes[] = [
     reverse: true,
   },
 ];
+export const newSortByFields: SortOrderTypes[] = [
+  {
+    key: "featured",
+    title: "Featured",
+    value: "featured",
+    sortKey: "FEATURED",
+    reverse: false,
+  },
+  {
+    key: "best-selling",
+    title: "Best Selling",
+    value: "best-selling",
+    sortKey: "BEST_SELLING",
+    reverse: false,
+  },
+  {
+    key: "newest",
+    title: "Newest First",
+    value: "newest",
+    sortKey: "NEWEST",
+    reverse: false,
+  },
+  {
+    key: "oldest",
+    title: "Oldest First",
+    value: "oldest",
+    sortKey: "OLDEST",
+    reverse: false,
+  },
+  {
+    key: "price-asc",
+    title: "Cheapest First",
+    value: "price-asc",
+    sortKey: "PRICE",
+    reverse: false,
+  },
+  {
+    key: "price-desc",
+    title: "Expensive First",
+    value: "price-desc",
+    sortKey: "PRICE",
+    reverse: true,
+  },
+];
 
 export const DEFAULT_EXPIRES_DAYS = 7; // cookie里的 GUEST_CART_TOKEN GUEST_CART_ID IS_GUEST 的过期时间
 const sameSite: boolean | "lax" | "none" | "strict" | undefined = "lax";