Sfoglia il codice sorgente

zzf-migrate-pc: 提交ts代码,排除图片资源,测试push

zhangzf 2 giorni fa
parent
commit
870d8d241e

+ 1 - 0
src/app/(public)/_components/HomeContent.tsx

@@ -99,6 +99,7 @@ export default function HomeContent({ homeData }: HomeContentProps) {
        sections,
         // categories 
       } = homeData;
+      console.log("sections--------------------------ccc",sections);
       // 过滤出 banner 板块
   const bannerSection = sections.find(sec => sec.showstyle === '2');
   const bannerImages = bannerSection?.images ?? [];

+ 113 - 0
src/app/(public)/customer/account/birthday/page.tsx

@@ -0,0 +1,113 @@
+import Image from "next/image";
+import Link from "next/link";
+import { restApiFetch } from "@utils/bagisto/index";
+
+// 格式化日期为 "Apr. 22" 这种英文短格式
+function formatBirthdayDisplay(isoDate: string) {
+  if (!isoDate) return "";
+  const date = new Date(isoDate);
+  // 月份缩写 + 日期
+  return date.toLocaleDateString("en-US", {
+    month: "short",
+    day: "numeric",
+  });
+}
+
+// 判断今天是否是生日(只对比月、日,忽略年份)
+function isTodayBirthday(isoDate: string): boolean {
+  if (!isoDate) return false;
+  const now = new Date();
+  const birthDate = new Date(isoDate);
+  const currentMonth = now.getMonth();
+  const currentDate = now.getDate();
+  const birthMonth = birthDate.getMonth();
+  const birthDay = birthDate.getDate();
+  return currentMonth === birthMonth && currentDate === birthDay;
+}
+
+export default async function GetPoints() {
+  const res = await restApiFetch({
+    api: `/customer/birthday`,
+    method: "GET",
+    isRoute: false,
+  });
+  console.log("res----------------aaa", res);
+
+  // 从接口拿生日字符串,示例:"1998-10-08"
+  const birthDateStr = res?.body?.data?.date_of_birth ?? "";
+  const isBirthdayToday = isTodayBirthday(birthDateStr);
+  const birthdayDisplayText = formatBirthdayDisplay(birthDateStr);
+
+  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] border-b border-[#E1E1E1]">
+          <Link
+            className="absolute left-2.5 text-2xl inline-block top-1/2  -translate-y-1/2"
+            href="/customer/account/mypoints"
+            aria-label="Go to customer account page"
+          >
+            <svg
+              xmlns="http://www.w3.org/2000/svg"
+              width="24"
+              height="24"
+              viewBox="0 0 24 24"
+              fill="none"
+            >
+              <path
+                stroke="rgba(0, 0, 0, 1)"
+                stroke-width="1.2"
+                stroke-linejoin="round"
+                stroke-linecap="round"
+                d="M15 18L9 12L15 6"
+              ></path>
+            </svg>
+          </Link>
+          <span className="vipReturnTitles">Birthday Rewards</span>
+        </div>
+        <div className="relative w-full max-w-[390px] mx-auto aspect-[390/705] overflow-hidden ">
+          {/* 底层背景大图,整张底图 */}
+          <Image
+            src={
+              isBirthdayToday
+                ? "/image/account/birthday_happy.png"
+                : "/image/account/birthday_waiting.png"
+            }
+            alt={
+              isBirthdayToday
+                ? "Birthday active gift"
+                : "Birthday upcoming notice"
+            }
+            fill
+            className="object-contain"
+            priority
+          />
+          {isBirthdayToday ? (
+            <>
+              <div className="absolute top-[43%] left-1/2 -translate-x-1/2 w-[62%] text-center text-white text-md md:text-lg font-medium">
+                A Little Birthday Gift, <br /> Just for You
+              </div>
+              <div className="absolute top-[52%] left-1/2 -translate-x-1/2 text-white text-ly-24 font-bold">
+                Enjoy $15 OFF
+              </div>
+              <div className="absolute top-[60%] left-1/2 -translate-x-1/2 w-[64%] text-center text-white text-md md:text-lg font-medium">
+                with your Birthday Gift Card.<br />Treat Yourself & Shine.
+              </div>
+            </>
+          ) : (
+            <>
+              <div className="absolute top-[45%] left-1/2 -translate-x-1/2 w-[60%] text-center text-white text-md md:text-lg leading-relaxed">
+                <p>Your special day is coming!</p>
+                <p>We'll be celebrating you all month long.</p>
+                <p>Your $15 Birthday Gift Card will be ready soon.</p>
+              </div>
+              {/* 这里动态渲染生日,不再写死Apr.22 */}
+              <div className="absolute top-[77%] left-1/2 -translate-x-1/2 text-white text-xl font-bold">
+                [{birthdayDisplayText}]
+              </div>
+            </>
+          )}
+        </div>
+      </div>
+    
+  );
+}

+ 116 - 0
src/app/(public)/customer/order/view/order_id/[id]/_components/OrderAddressCard.tsx

@@ -0,0 +1,116 @@
+// import type { CustomerOrder } from "@/types/customer/accountOrderDetail/order";
+import type { OrderDetails } from "@/types/customer/type";
+
+// 提取地址类型
+type OrderAddressItem = OrderDetails["addresses"]["edges"][number]["node"];
+
+interface Props {
+  orderData: OrderDetails;
+}
+
+const getFullAddress = (address: OrderAddressItem) => {
+  return [
+    address.address,
+    address.city,
+    address.state,
+    address.postcode,
+    address.country,
+  ]
+    .filter(Boolean)
+    .join(", ");
+};
+
+function AddressBlock({ address }: { address: OrderAddressItem }) {
+  return (
+    <div
+      className="
+        mt-4
+        overflow-hidden
+        bg-[url('/image/account/202609011640.png')] bg-no-repeat bg-size-[100%_100%]
+      "
+    >
+      {/* Name + Phone */}
+      <div
+        className="
+          px-4
+          py-4
+          text-[14px]
+        "
+      >
+        {address.firstName} {address.lastName}
+        <span className="mx-2 text-[#999]">|</span>
+        {address.phone}
+      </div>
+      {/* address */}
+      <div
+        className="
+          flex
+          items-start
+          gap-3
+          border-t
+          border-[#F7EBE6]
+          px-4
+          py-4
+          text-[14px]
+        "
+      >
+        <svg
+          width="20"
+          height="20"
+          viewBox="0 0 24 24"
+          fill="none"
+          className="mt-[1px] shrink-0"
+        >
+          <path
+            d="M12 21C12 21 19 15.5 19 9.5C19 5.36 15.87 2 12 2C8.13 2 5 5.36 5 9.5C5 15.5 12 21 12 21Z"
+            stroke="currentColor"
+            strokeWidth="1.5"
+          />
+          <circle
+            cx="12"
+            cy="9.5"
+            r="2.5"
+            stroke="currentColor"
+            strokeWidth="1.5"
+          />
+        </svg>
+        <span>{getFullAddress(address)}</span>
+      </div>
+    </div>
+  );
+}
+
+export default function OrderAddressCard({ orderData }: Props) {
+  // 从edges里筛选收货地址、账单地址
+  const addressNodes = orderData.addresses.edges.map((edge) => edge.node);
+  const shippingAddress = addressNodes.find(
+    (item) => item.addressType === "order_shipping",
+  );
+  const billingAddress = addressNodes.find(
+    (item) => item.addressType === "order_billing",
+  );
+
+  return (
+    <section
+      className="
+        rounded-[6px]
+        bg-white
+        px-4
+        pb-4
+      "
+    >
+      {shippingAddress && (
+        <div>
+          <h2 className="text-[17px]">Ship to:</h2>
+          <AddressBlock address={shippingAddress} />
+        </div>
+      )}
+      {billingAddress && (
+        <div className="mt-6">
+          <h2 className="text-[17px]">Billing to:</h2>
+          <AddressBlock address={billingAddress} />
+        </div>
+      )}
+    </section>
+  );
+}

+ 134 - 36
src/app/(public)/customer/order/view/order_id/[id]/_components/OrderInfoBlock.tsx

@@ -1,51 +1,149 @@
-// OrderInfoBlock.tsx
-import type { OrderData } from "@/types/api/customer/order/vieworder";
-
+// import type { CustomerOrder } from "@/types/customer/accountOrderDetail/order";
+import type { OrderDetails } from "@/types/customer/type";
+import Image from "next/image";
 interface Props {
-  orderData: OrderData; // 直接传入完整订单对象
+  orderData: OrderDetails;
 }
+export default function PromotionDetails({ orderData }: Props) {
+  const money = (value: number) =>
+    new Intl.NumberFormat("en-US", {
+      style: "currency",
+      currency: "USD",
+    }).format(value);
 
-export default function OrderInfoBlock({ orderData }: Props) {
-  console.log("orderData:::",orderData);
-  
-  // 拼接收货信息
-  const shipToText = `${orderData.shipping_address.first_name} ${orderData.shipping_address.last_name}, 
-Phone: ${orderData.shipping_address.phone}, 
-${orderData.shipping_address.city}, ${orderData.shipping_address.state}, ${orderData.shipping_address.country} ${orderData.shipping_address.postcode}`;
-
+  // 各个优惠金额,取绝对值用于展示
+  const couponDiscount = Math.abs(orderData.discountAmount ?? 0);
+  const giftCardAmt = Math.abs(Number(orderData.giftcardAmount ?? 0));
+  const rewardPointsAmt = Math.abs(orderData.rewardPointsAmount ?? 0);
+  const vipPlusAmt = orderData.vipPlusAmount
+    ? Number(orderData.vipPlusAmount)
+    : 0;
+  const vipDiscountAmt = Math.abs(Number(orderData.vipDiscountAmount ?? 0));
+  const earnedPoints = orderData.rewardPointsEarned ?? 0;
   return (
-    <>
-      <h2 className="text-xl font-bold mb-6 border-b pb-3">Order Information</h2>
+    <section
+      className="
+        rounded-[6px]
+        bg-white
+        px-4
+        pb-6
+      "
+    >
+      <h2 className="text-[18px]">Promotion Details</h2>
+      <div className="mt-6 border-t border-[#EEEEEE] pt-4">
+        {/* Subtotal */}
+        <div className="flex justify-between py-3 text-[14px]">
+          <span>Subtotal</span>
+          <span>{money(orderData.subTotal)}</span>
+        </div>
+
+        {/* Gift Card */}
+        {giftCardAmt > 0 && (
+          <div className="flex justify-between py-3 text-[14px]">
+            <span>Gift Card</span>
+            <span className="bg-[#FFD600] px-2 py-1">
+              -{money(giftCardAmt)}
+            </span>
+          </div>
+        )}
 
-      {/* 两列网格布局 */}
-      <div className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-5">
-        <div className="font-medium text-gray-700">Ship To:</div>
-        <div>{shipToText}</div>
+        {/* Coupon Discount */}
+        {couponDiscount > 0 && (
+          <div className="flex justify-between py-3 text-[14px]">
+            <span>
+              Discount
+              {orderData.couponCode ? `(Code:${orderData.couponCode})` : ""}
+            </span>
+            <span className="bg-[#FFD600] px-2 py-1">
+              -{money(couponDiscount)}
+            </span>
+          </div>
+        )}
 
-        <div className="font-medium text-gray-700">Order Number:</div>
-        <div>{orderData.increment_id}</div>
+        {/* Reward Points */}
+        {rewardPointsAmt > 0 && (
+          <div className="flex justify-between py-3 text-[14px]">
+            <span>Points</span>
+            <span className="bg-[#FFD600] px-2 py-1">
+              -{money(rewardPointsAmt)}
+            </span>
+          </div>
+        )}
 
-        <div className="font-medium text-gray-700">Status:</div>
-        <div>{orderData.status}</div>
+        {/* VIP Plus Discount - 使用图片代替css标签 */}
+        {vipDiscountAmt > 0 && (
+          <div className="flex justify-between py-3 text-[14px]">
+            <span className="relative">
+              <Image
+                src="/image/account/plus-tag.png"
+                alt="PLUS"
+                width={60}
+                height={20}
+                className="inline-block align-middle"
+              />
+            </span>
+            <span className="bg-[#FFD600] px-2 py-1">
+              -{money(vipDiscountAmt)}
+            </span>
+          </div>
+        )}
 
-        <div className="font-medium text-gray-700">Order Date:</div>
-        <div>{orderData.created_at}</div>
+        {/* WIGGINS PLUS */}
+        {vipPlusAmt > 0 && (
+          <div className="flex justify-between py-3 text-[14px]">
+            <span>ASTERIA PLUS</span>
+            <span>{money(vipPlusAmt)}</span>
+          </div>
+        )}
 
-        <div className="font-medium text-gray-700">Customer Email:</div>
-        <div>{orderData.customer_email}</div>
+        {/* Shipping */}
+        <div className="flex justify-between py-3 text-[14px]">
+          <span>Shipping</span>
+          <span>
+            {orderData.shippingAmount === 0
+              ? "Free"
+              : money(orderData.shippingAmount)}
+          </span>
+        </div>
+
+        {/* Insurance for Lost */}
+        {Number(orderData.shippingInsuranceAmount) > 0 && (
+          <div className="flex justify-between py-3 text-[14px]">
+            <span>Insurance for Lost(3%)</span>
+            <span>{money(Number(orderData.shippingInsuranceAmount))}</span>
+          </div>
+        )}
 
-        <div className="font-medium text-gray-700">Shipping Method:</div>
-        <div>{orderData.shipping_title}</div>
+        {/* Tax */}
+        <div className="flex justify-between py-3 text-[14px]">
+          <span>Tax</span>
+          <span>{money(orderData.taxAmount)}</span>
+        </div>
 
-        <div className="font-medium text-gray-700">Payment Method:</div>
-        <div className="bg-pink-300 px-3 py-1 rounded w-fit">
-          {orderData.payment.method_title}
+        {/* Total */}
+        <div
+          className="
+            mt-2
+            flex
+            justify-between
+            border-t
+            border-[#EEEEEE]
+            pt-5
+            text-[15px]
+          "
+        >
+          <span>Total:</span>
+          <span className="font-medium">{money(orderData.grandTotal)}</span>
         </div>
 
-        {/* 可选:显示总价信息 */}
-        <div className="font-medium text-gray-700">Total Amount:</div>
-        <div>{orderData.order_currency_code} {orderData.grand_total}</div>
+        {/* You've earned points 底部文字 */}
+        {earnedPoints > 0 && (
+          <div className="mt-4 text-[14px] text-[#8A8A8A]">
+            You've earned {earnedPoints} points
+          </div>
+        )}
       </div>
-    </>
+    <div className="h-px bg-[rgba(172,189,184,1)] w-full mt-6"></div>
+    </section>
   );
-}
+}

+ 134 - 22
src/app/(public)/customer/order/view/order_id/[id]/_components/ProductList.tsx

@@ -1,29 +1,141 @@
 // ProductList.tsx
-import type { OrderData } from "@/types/api/customer/order/vieworder";
-
+// import type { CustomerOrder } from "@/types/customer/accountOrderDetail/order";
+import type { OrderDetails } from "@/types/customer/type";
 interface Props {
-  orderItems: OrderData["items"];
-  currencyCode: string;
+  orderData: OrderDetails;
 }
+const formatDate = (date: string) => {
+  if (!date) return "";
+  const datePart = date.split("T")[0]; // 原来split(" ")会报错!ISO时间是 2026-09-30T01:14:14-07:00,没有空格
+  const [year, month, day] = datePart.split("-");
+  return `${Number(month)}/${Number(day)}/${year}`;
+};
+const formatMoney = (value: number, currency: string) => {
+  return new Intl.NumberFormat("en-US", {
+    style: "currency",
+    currency,
+  }).format(value);
+};
+const formatStatus = (status: string) => {
+  return status
+    .replace(/_/g, " ")
+    .replace(/\b\w/g, (char) => char.toUpperCase());
+};
+export default function ProductList({ orderData }: Props) {
+  // 从edges取出商品数组
+  console.log("orderData------------------", orderData);
+  const itemList = orderData.items.edges.map((edge) => edge.node);
 
-export default function ProductList({ orderItems, currencyCode }: Props) {
   return (
-    <div className="mb-6 space-y-4">
-      {orderItems.map((item, index) => (
-        <div className="flex items-center gap-3" key={item.id ?? index}>
-          {/* 暂无商品原图:用占位框 */}
-          <div className="w-24 h-24 bg-gray-100 rounded shrink-0"></div>
-
-          <div className="flex-1">
-            <p className="font-medium">{item.name}</p>
-            <p className="text-sm text-gray-600 my-1">SKU: {item.sku ?? "-"}</p>
-            <div className="flex justify-between">
-              <span>{currencyCode} {item.price}</span>
-              <span>× {item.qty_ordered}</span>
+    <section
+      className="
+        rounded-[6px]
+        bg-white
+        px-4
+        py-6
+      "
+    >
+      {/* status */}
+      <div className="flex items-center gap-2 text-[16px]">
+        <span className="h-2 w-2 rounded-full bg-black" />
+        <span>{formatStatus(orderData.status)}</span>
+      </div>
+      {/* Order / Date */}
+      <div
+        className="
+          mt-4
+          flex
+          items-center
+          justify-between
+          border-b
+          border-[#F1E5DF]
+          pb-4
+          text-[14px]
+          text-[#8A8A8A]
+        "
+      >
+        <span>Order #{orderData.incrementId}</span>
+        <span>Date: {formatDate(orderData.createdAt)}</span>
+      </div>
+      {/* Products */}
+      <div>
+        {itemList.map((item, index) => {
+          // 解析baseImage
+          let imgUrl = "";
+          try {
+            const imgJson = JSON.parse(item.baseImage);
+            imgUrl = imgJson.small_image_url;
+          } catch (err) {
+            console.log(err);
+            imgUrl = "";
+          }
+          return (
+            <div
+              key={item.id}
+              className={`
+                flex
+                gap-4
+                py-4
+                ${
+                  index !== itemList.length - 1
+                    ? "border-b border-[#EEEEEE]"
+                    : ""
+                }
+              `}
+            >
+              {/* 商品图 */}
+              <div
+                className="
+                  h-[174px]
+                  w-[130px]
+                  shrink-0
+                  overflow-hidden
+                  bg-[#F5F5F5]
+                "
+              >
+                {imgUrl ? (
+                  <img
+                    src={imgUrl}
+                    alt={item.name}
+                    className="h-full w-full object-cover"
+                  />
+                ) : null}
+              </div>
+              <div className="flex min-w-0 flex-1 flex-col">
+                <h3
+                  className="
+                    text-[14px]
+                    leading-[20px]
+                    text-black
+                  "
+                >
+                  {item.name}
+                </h3>
+                {/* 当前 API 没有 options,有了以后直接显示 */}
+                {item.sku && (
+                  <div
+                    className="
+                    mt-3
+                    bg-[#EFF9F6]
+                    px-3
+                    py-2
+                    text-[13px]
+                    text-[#333]
+                  "
+                  >
+                    {item.sku}
+                  </div>
+                )}
+                <div className="mt-auto text-[15px]">
+                  <span>{formatMoney(item.price, "USD")}</span>
+                  <span className="ml-1">x {item.qtyOrdered}</span>
+                </div>
+              </div>
             </div>
-          </div>
-        </div>
-      ))}
-    </div>
+          );
+        })}
+      </div>
+      <div className="h-px bg-[rgba(172,189,184,1)] w-full mt-2"></div>
+    </section>
   );
-}
+}

+ 37 - 18
src/app/(public)/customer/order/view/order_id/[id]/page.tsx

@@ -1,9 +1,13 @@
 import OrderHeader from "./_components/OrderHeader";
 import OrderInfoBlock from "./_components/OrderInfoBlock";
 import ProductList from "./_components/ProductList";
-import PriceSummary from "./_components/PriceSummary";
-import {restApiFetch} from "@utils/bagisto/index";
-import type {  OrderData,OrderApiResponse } from "@/types/api/customer/order/vieworder";
+import OrderAddressCard from "./_components/OrderAddressCard";
+// import PriceSummary from "./_components/PriceSummary";
+// import {restApiFetch} from "@utils/bagisto/index";
+import {serverGraphqlFetch} from "@utils/bagisto/index";
+import type { OrderDetailsData } from "@/types/customer/type";
+import { GET_ORDER_DETAILS } from "@/graphql";
+// import type {  OrderData,OrderApiResponse } from "@/types/api/customer/order/vieworder";
 type Params = {
   id: string;
 };
@@ -11,28 +15,43 @@ export default async function OrderDetail({ params }: { params: Params }) {
   // 解包拿到真实订单ID
   const realParams = await params;
   let orderId = realParams.id;
-  orderId = '9';
+  orderId = '881900125';
   console.log("orderId:", orderId);
 
   // 2. 服务端 fetch 请求我们自己的 Next.js API
-  const res = await restApiFetch<OrderApiResponse>({
-    api:`/customer/orders/${orderId}`,
-    method:"GET",
-    isRoute: false
+  // const res = await restApiFetch<OrderApiResponse>({
+  //   api:`/customer/orders/${orderId}`,
+  //   method:"GET",
+  //   isRoute: false
+  // }
+  // ); 
+  const {data: orderDetailData} = await serverGraphqlFetch<OrderDetailsData,{id:string}>({
+        query: GET_ORDER_DETAILS,
+        variables: {
+            id:orderId
+        }
+    }); 
+  console.log("父组件 orderDetailData:", orderDetailData?.customerOrder); 
+  // const orderData: OrderData = res.body.data;
+  // console.log("父组件 orderData:", orderData, !!orderData); 
+  const customerOrder = orderDetailData?.customerOrder;
+  if (!customerOrder) {
+    return <div>Loading / Order does not exist</div>;
   }
-  );
-
-  const orderData: OrderData = res.body.data;
-  console.log("父组件 orderData:", orderData, !!orderData); 
   return (
     <div className="bg-gray-50 min-h-screen">
       {/* 顶部固定导航栏(客户端组件:返回按钮) */}
       <OrderHeader title="Order Information" />
 
-      <div className="px-5 py-4">
+      <div className="">
         {/* 收货信息、支付信息区块 */}
-        <OrderInfoBlock orderData={orderData} />
-
+        {/* <OrderInfoBlock orderData={orderData} /> */}
+          {/* 商品列表:循环渲染,自动兼容1件或多件商品 */}
+        <ProductList
+          orderData={customerOrder}
+        />
+        <OrderInfoBlock orderData={customerOrder} />
+         <OrderAddressCard orderData={customerOrder}/>
         {/* 分享按钮区域 */}
         <div className="my-6">
           <h3 className="text-lg font-bold mb-4">Share order:</h3>
@@ -45,13 +64,13 @@ export default async function OrderDetail({ params }: { params: Params }) {
         </div>
 
         {/* 商品列表:循环渲染,自动兼容1件或多件商品 */}
-            <ProductList
+            {/* <ProductList
           orderItems={orderData.items}
           currencyCode={orderData.order_currency_code}
-        />
+        /> */}
 
         {/* 金额汇总 */}
-        <PriceSummary orderData={orderData} />
+        {/* <PriceSummary orderData={orderData} /> */}
       </div>
     </div>
   );

+ 5 - 5
src/app/(public)/search/page.tsx

@@ -21,10 +21,10 @@ import HotSearch from "./_components/HotSearch";
 const Pagination = dynamicImport(
   () => import("@/components/catalog/Pagination"),
 );
-const ProductGridItems = dynamicImport(
-  () => import("@/components/catalog/product/ProductGridItems"),
-);
-
+// const ProductGridItems = dynamicImport(
+//   () => import("@/components/catalog/product/ProductGridItems"),
+// );
+import NewProductGridItems from "@/components/catalog/product/NewProductGridItems";
 export const dynamicParams = true;
 
 // SEO+分享
@@ -169,7 +169,7 @@ export default async function SearchPage({
       )}
       {isArray(products) ? (
         <Grid className="grid grid-flow-row grid-cols-2 gap-5 lg:gap-11.5 w-full max-w-screen-2xl mx-auto md:grid-cols-3 lg:grid-cols-4 px-4 xss:px-7.5">
-          <ProductGridItems products={products} />
+          <NewProductGridItems products={products} />
         </Grid>
       ) : null}
 

+ 36 - 0
src/app/api/customer/birthday/route.ts

@@ -0,0 +1,36 @@
+import { NextResponse } from "next/server";
+import { restApiFetch } from "@/utils/bagisto";
+
+
+export async function GET() {
+  try {
+    //  const { id } = await params; // 解包拿到订单ID
+  
+
+   const apiUrl = `/customer/birthday`;
+
+    console.log("最终请求API地址:", apiUrl);
+
+    const response = await restApiFetch<any>({
+      api: apiUrl,
+      method: "GET",
+      cache: "no-store",
+
+    });
+
+    return NextResponse.json(response.body,{
+        status: response.status,
+    });
+
+  } catch (error) {
+
+
+    return NextResponse.json(
+      {
+        message: "Network error",
+        error: error instanceof Error ? error.message : error,
+      },
+      { status: 500 }
+    );
+  }
+}

+ 129 - 0
src/components/catalog/product/NewProductGridItems.tsx

@@ -0,0 +1,129 @@
+import { getImageUrl } from "@/utils/constants";
+import Grid from "@/components/theme/ui/grid/Grid";
+import { OpenAddToCartModalButton } from "@/components/common/AddToCartModal/OpenAddToCartModalButton";
+import Image from "next/image";
+import Link from "next/link";
+export default function NewProductGridItems({
+  products,
+}: {
+  products: any;
+}) {
+  return products.map((product: any) => {
+
+    const imageUrl = getImageUrl(product?.baseImageUrl);
+    const price =
+      product?.type === "configurable"
+        ? product?.minimumPrice ?? "0"
+        : product?.price ?? "0";
+    // const currency = product?.priceHtml?.currencyCode;
+    return (
+            <Grid.Item key={product.id} className="flex flex-col ">
+                        {/* 产品图片 */}
+                        <Image
+                          src={imageUrl}
+                          alt={product.name}
+                          width={339}
+                          height={452}
+                          sizes="(max-width: 768px) 50vw, (max-width: 1200px) 33vw, 25vw"
+                          className=" bg-neutral-100 object-cover transition duration-300 ease-in-out group-hover:scale-105"
+                        />
+                        <Link href={`/${product.urlKey}.html`} className="text-sm text-gray-800 truncate mt-4 mb-2" aria-label={`View ${product.name}`}>
+                          {product.name}
+                        </Link>
+                        {/* 评分 + 评论数 */}
+                        {product.review_count ? (
+                          <div className="flex items-center gap-1">
+                            <div className="flex">
+                              {/* 星级图标(简化版,可替换为真实星星图标) */}
+                              {[1, 2, 3, 4, 5].map((star) => (
+                                <svg
+                                  key={star}
+                                  className="w-4 h-4 text-yellow-400"
+                                  fill="currentColor"
+                                  viewBox="0 0 20 20"
+                                >
+                                  <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
+                                </svg>
+                              ))}
+                            </div>
+                            <span className="text-xs text-gray-600">
+                              {product.review_count}
+                            </span>
+                          </div>
+                        ) : null}
+                        {/* 价格 + 购物车图标 */}
+                        <div className="flex items-center mt-4 justify-between">
+                          <div className="flex items-center gap-1">
+                            <span className="font-semibold">${price}</span>
+                            <span className="text-xs text-gray-500 line-through">
+                              ${product.price}
+                            </span>
+                          </div>
+                          {/* 购物车图标 */}
+                          <OpenAddToCartModalButton
+                              productId={product.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>
+            </Grid.Item>
+    );
+  });
+}

+ 6 - 5
src/components/layout/navbar/SearchPop.tsx

@@ -2,13 +2,14 @@
 import { useState, useEffect, useRef } from "react";
 import { useSearchProduct } from "@/utils/hooks/useSearchProduct";
 import Grid from "@/components/theme/ui/grid/Grid";
-import dynamicImport from "next/dynamic";
+// import dynamicImport from "next/dynamic";
 import HotSearch from "@/app/(public)/search/_components/HotSearch"
 import { useMediaQuery } from "@utils/hooks/useMediaQueryHook";
 import Link from "next/link";
-const ProductGridItems = dynamicImport(
-  () => import("@/components/catalog/product/ProductGridItems"),
-);
+// const ProductGridItems = dynamicImport(
+//   () => import("@/components/catalog/product/ProductGridItems"),
+// );
+import NewProductGridItems from "@/components/catalog/product/NewProductGridItems";
 
 // 定义弹窗入参类型
 interface SearchPopProps {
@@ -158,7 +159,7 @@ export default function SearchPop({ open, onClose }: SearchPopProps) {
         {keyword && data?.edges?.length !== 0 && (
           <div>
             <Grid className="grid grid-flow-row grid-cols-2 gap-5 lg:gap-11.5 w-full max-w-screen-2xl mx-auto md:grid-cols-3 lg:grid-cols-4 px-4 xss:px-7.5">
-              <ProductGridItems products={products} />
+              <NewProductGridItems products={products} />
             </Grid>
 
             <div className="flex justify-center mt-10 mb-16">

+ 8 - 0
src/graphql/customer/query/GetOrderDetails.ts

@@ -9,6 +9,7 @@ export const GET_ORDER_DETAILS: TypedDocumentNode<OrderDetailsData> = gql`
     id
     _id
     status
+    customerFullName
     channelName
     customerEmail
     customerFirstName
@@ -103,6 +104,13 @@ export const GET_ORDER_DETAILS: TypedDocumentNode<OrderDetailsData> = gql`
         }
       }
     }
+    shipments {
+      edges {
+        node {
+          id
+        }
+      }
+    }
     createdAt
     updatedAt
   }

+ 11 - 1
src/types/customer/order.ts

@@ -82,6 +82,7 @@ export interface OrderDetails {
     id: string;
     _id: number;
     status: string;
+    customerFullName: string;
     channelName: string;
     customerEmail: string;
     customerFirstName: string;
@@ -131,9 +132,18 @@ export interface OrderDetails {
     addresses: AddressListInOrderDetails;
     createdAt: string;
     updatedAt: string;
-  
+    shipments: OrderShipmentConnection;
 }
 
+// 发货信息
+export type OrderShipmentConnection = {
+  edges: Array<{
+    node: {
+      id: string;
+    };
+  }>;
+};
+
 export interface OrderDetailsData {
   customerOrder: OrderDetails | null;
 }