fogwind 1 週間 前
親
コミット
fa3f949c7c

+ 3 - 2
src/app/(checkout)/checkout/_components/CheckoutProducts/CheckoutProducts.tsx

@@ -7,7 +7,7 @@ import type {CartDetail, CartItem} from "@/types/cart/type";
 import { useAddProduct } from "@utils/hooks/useAddToCart";
 import { useCustomToast } from "@/utils/hooks/useToast";
 import {useConfig} from "@/utils/hooks/useConfig";
-import { formatCartDetail } from "@/utils/cartDetailTools";
+import { formatCartDetail, formatCartProductOptions } from "@/utils/cartDetailTools";
 import { overlayLoading } from "@/components/theme/ui/kernel/loading/api";
 import { confirmDialog } from "@/components/theme/ui/kernel/confirm/api";
 import CommonModal from "@/components/theme/ui/CommonModal";
@@ -155,6 +155,7 @@ export default function CheckoutProducts({
                 <div className="relative box-border w-full h-auto px-4 pt-2 pb-8">
                     {productList.map((product) => {
                         const baseImage = product.baseImage ? JSON.parse(product.baseImage) : {};
+                        const productOptions = product.options.edges.map((edge) => edge.node);
                         return (
                             <div key={product.id} className="w-full flex gap-2 mt-4 first:mt-0 pb-4 border-b-1 border-b-[#f0f0f0] last:border-none">
                                 <div className="relative w-33 h-44 flex-none">
@@ -171,7 +172,7 @@ export default function CheckoutProducts({
                                             </button>
                                         </div>
                                         <div className="w-full mt-2 text-ly-12 border-1 rounded-sm border-[#d9d9d9] px-2.5 py-1.5 leading-5">
-                                            {product.sku}
+                                            {formatCartProductOptions(productOptions)}
                                         </div>
                                     </div>
                                     <div className="flex items-center justify-between">

+ 2 - 1
src/app/(checkout)/checkout/cart/_components/UnpaidOrderSection.tsx

@@ -7,6 +7,7 @@ import { usePlaceOrder } from "@/utils/hooks/usePlaceOrder";
 import { useRouter } from 'next/navigation';
 import { useCustomToast } from "@/utils/hooks/useToast";
 import {useConfig} from "@/utils/hooks/useConfig";
+import {getProductAdditionalInfo} from "@/utils/orderDetailTools";
 import { confirmDialog } from "@/components/theme/ui/kernel/confirm/api";
 import { overlayLoading } from "@/components/theme/ui/kernel/loading/api";
 import type { OrderDetails, ProductsInOrderDetails } from "@/types/customer/type";
@@ -176,7 +177,7 @@ export function UnpaidOrderSection({
                                             </div>
                                         </div>
                                         <div className="w-full mt-2 text-ly-12 border-1 rounded-sm border-[#d9d9d9] px-2.5 py-1.5 leading-5">
-                                            {product.sku}
+                                             {getProductAdditionalInfo(JSON.parse(product.additional))}
                                         </div>
                                     </div>
                                     <div className="flex items-center justify-between">

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

@@ -17,11 +17,7 @@ import {
   GET_CATEGORY_ATTR_FILTERS,
   CATEGORY_PRODUCTS,
 } from "@/graphql";
-import {
-  // cachedGraphQLRequest,
-  // cachedCategoryRequest,
-  // getFilterAttributes,
-} from "@/utils/hooks/useCache";
+
 import { newSortByFields } from "@utils/constants";
 // import { CategoryDetail } from "@components/theme/search/CategoryDetail";
 import CategoryDesc from "./_components/CategoryDesc";

+ 2 - 1
src/env.ts

@@ -59,8 +59,9 @@ export const env = createEnv({
 
   /**
    * docker build 或 CI 阶段如果没有真实环境变量,可以临时跳过校验
+   * SKIP_ENV_VALIDATION 变量需要在docker的配置文件中注入
    */
-  //   skipValidation: !!process.env.SKIP_ENV_VALIDATION,
+  skipValidation: !!process.env.SKIP_ENV_VALIDATION,
 
   /**
    * .env 里如果某个变量写成空字符串(比如 XXX=),当作没配置处理,而不是校验通过一个空串

+ 1 - 0
src/graphql/cart/mutations/AddProductToCart.ts

@@ -38,6 +38,7 @@ export const CREATE_ADD_PRODUCT_IN_CART: TypedDocumentNode<AddToCartData,AddToCa
               type
               productUrlKey
               canChangeQty
+              options
             }
           }
         }

+ 1 - 0
src/graphql/cart/mutations/CreateApplyCoupon.ts

@@ -28,6 +28,7 @@ export const CREATE_APPLY_COUPON: TypedDocumentNode<CreateApplyCouponData,{coupo
                         type
                         productUrlKey
                         canChangeQty
+                        options
                         }
                     }
                     }

+ 1 - 0
src/graphql/cart/mutations/CreateMergeCart.ts

@@ -30,6 +30,7 @@ export const CREATE_MERGE_CART: TypedDocumentNode<CreateMergeCartData> = gql`
               type
               productUrlKey
               canChangeQty
+              options
             }
           }
         }

+ 1 - 0
src/graphql/cart/mutations/CreateRemoveCoupon.ts

@@ -24,6 +24,7 @@ export const CREATE_REMOVE_COUPON: TypedDocumentNode<CreateRemoveCouponData> = g
                         type
                         productUrlKey
                         canChangeQty
+                        options
                         }
                     }
                     }

+ 1 - 0
src/graphql/cart/mutations/GetCartItem.ts

@@ -24,6 +24,7 @@ export const GET_CART_ITEM: TypedDocumentNode<GetCartItemData> = gql`
               type
               productUrlKey
               canChangeQty
+              options
             }
           }
         }

+ 1 - 0
src/graphql/cart/mutations/RemoveCartItem.ts

@@ -24,6 +24,7 @@ export const REMOVE_CART_ITEM: TypedDocumentNode<RemoveCartItemData,{cartItemId:
               type
               productUrlKey
               canChangeQty
+              options
             }
           }
         }

+ 1 - 0
src/graphql/cart/mutations/UpdateCartItems.ts

@@ -33,6 +33,7 @@ export const UPDATE_CART_ITEM: TypedDocumentNode<UpdateCartItemData,{cartItemId:
               type
               productUrlKey
               canChangeQty
+              options
             }
           }
         }

+ 1 - 0
src/graphql/checkout/mutations/CreateSaveCheckoutCart.ts

@@ -42,6 +42,7 @@ export const CREATE_SAVE_CHECKOUT_CART: TypedDocumentNode<SaveCheckoutCartData,S
                             type
                             productUrlKey
                             canChangeQty
+                            options
                         }
                     }
                 }

+ 0 - 135
src/lib/graphql-fetch.ts

@@ -1,135 +0,0 @@
-
-import { type DocumentNode } from "graphql";
-import {
-  type OperationVariables,
-  ApolloClient
-} from "@apollo/client";
-import type {GraphqlRequestResult} from "@/types/graphqlFetch/type";
-import {getClient} from "@/lib/ApolloClientServer";
-
-
-
-/* 定义自己的context类型
-import "@apollo/client";
-import { HttpLink } from "@apollo/client";
-declare module "@apollo/client" {
-  interface DefaultContext extends HttpLink.ContextOptions {}
-}
-*/
-// Comprehensive error handling example. https://www.apollographql.com/docs/react/data/error-handling
-
-export type CacheLifePreset =
-  | "seconds"
-  | "minutes"
-  | "hours"
-  | "days"
-  | "weeks"
-  | "max";
-
-export type CacheLifeOption = number | CacheLifePreset;
-
-export function getRevalidateTime(
-  life?: CacheLifeOption
-): number | false {
-  if (!life) return false;
-  if (typeof life === "number") return life;
-
-  switch (life) {
-    case "seconds":
-      return 10;
-    case "minutes":
-      return 60;
-    case "hours":
-      return 3600;
-    case "days":
-      return 86400;
-    case "weeks":
-      return 604800;
-    case "max":
-      return false;
-    default:
-      return false;
-  }
-}
-
-export interface GraphQLRequestOptions {
-  tags?: string[];
-  life?: CacheLifeOption;
-  noCache?: boolean;
-  context?: Record<string, unknown>;
-  fetchPolicy?:
-  | "cache-first"
-  | "network-only"
-  | "no-cache"
-  | "cache-only";
-}
-
-export async function graphqlRequest<
-  TData = unknown,
-  TVariables extends OperationVariables = OperationVariables
->(
-  query: DocumentNode,
-  variables?: TVariables,
-  options?: GraphQLRequestOptions
-): Promise<GraphqlRequestResult<TData>> {
-  const client = getClient();
-  let resData;
-  const revalidate = getRevalidateTime(options?.life);
-  let queryOption: ApolloClient.QueryOptions<TData> = {
-    query,
-    variables,
-    fetchPolicy: "network-only",
-    context: {
-      fetchOptions: {
-        next: {
-          revalidate,
-          tags: options?.tags,
-        },
-      },
-    }
-  };
-
-  if (options?.noCache) {
-    /***
-     * client.query的参数是一个对象:
-     * {query, variables, context, fetchPolicy, errorPolicy}
-     * context.fetchOptions 可以设置nextjs fetch的缓存策略 https://www.apollographql.com/docs/react/integrations/nextjs
-     * context: {
-        fetchOptions: {
-          next: {
-            revalidate: 60,      // 对应 life: 'minutes'
-            tags: ['posts'],     // 对应 tags 选项
-          },
-        },
-      },
-     */
-    queryOption = {
-      query,
-      variables,
-      fetchPolicy: "no-cache", // 跳过 apollo client 的缓存,直接调fetch
-      context: {
-        ...options?.context,
-        fetchOptions: {
-          cache: 'no-store',   // 👈 告诉 Next.js 不要缓存这个 fetch
-        },
-      },
-    };
-  }
-
-  if (options?.context) {
-    throw new Error(
-      "graphqlRequest: Caching with `context` is unsafe. Use noCache instead."
-    );
-  }
-  try {
-      // Promise-based APIs (e.g. client.query, client.mutate) - Errors either reject the promise or are returned in the result as the error field.
-      // 如果错误被reject 则会进入catch
-      const result: ApolloClient.QueryResult<TData> = await client.query(queryOption);
-
-      resData = result.data || null;
-      return {data: resData, error: result.error? result.error.message : '' };
-  } catch (error) {
-    throw error;
-  }
-}
-

+ 12 - 0
src/types/cart/type.ts

@@ -1,4 +1,13 @@
 // Cart Item (node)
+export interface CartItemOptions {
+  value_id: number;
+  option_id: number;
+  value_code: string;
+  option_code: string;
+  value_label: string;
+  option_label: string;
+  attribute_name: string;                 
+}
 export interface CartItem {
   id: string;
   cartId: string;
@@ -11,6 +20,9 @@ export interface CartItem {
   type: string;
   productUrlKey: string;
   canChangeQty: boolean;
+  options: {
+    edges: Array<{node:CartItemOptions}>
+  }
 }
 
 // GraphQL Edge wrapper

+ 0 - 4
src/types/graphqlFetch/type.ts

@@ -1,7 +1,3 @@
-export interface GraphqlRequestResult<TData = unknown> {
-  data: TData | null;
-  error: string | undefined;
-}
 
 export interface FetchGraphqlError {
     message: string;

+ 15 - 1
src/utils/cartDetailTools.ts

@@ -1,4 +1,4 @@
-import type { CartDetail, CartAddress } from "@/types/cart/type";
+import type { CartDetail, CartAddress, CartItemOptions } from "@/types/cart/type";
 import type {ShipAddressFormData,BillAddressFormData} from "@/types/checkout/type";
 export function formatCartDetail(cart:CartDetail): CartDetail { 
     return {
@@ -98,4 +98,18 @@ export function billingAddressToCartAddress(address: BillAddressFormData):CartAd
         phone: address.billingPhoneNumber
     }
     return res;
+}
+
+export function formatCartProductOptions(options:CartItemOptions[]) {
+    let res = '';
+    if(options.length > 0) {
+        for(let i = 0; i < options.length; i++) {
+            if(res) {
+                res += '/' + options[i].option_label + ':' + options[i].value_label;
+            } else {
+                res = options[i].option_label + ':' + options[i].value_label;
+            }
+        }
+    }
+    return res;
 }

+ 0 - 95
src/utils/hooks/useCache.ts

@@ -1,95 +0,0 @@
-import { type DocumentNode, type OperationVariables } from "@apollo/client";
-import { graphqlRequest, type CacheLifeOption } from "@/lib/graphql-fetch";
-import type {GraphqlRequestResult} from "@/types/graphqlFetch/type";
-
-
-export interface PageCacheConfig {
-  tags: string[];
-  life: CacheLifeOption;
-}
-
-/**
- * Cache configuration for different pages and queries
- * Centralized management of cache tags and revalidation times
- */
-export const PAGE_CACHE_CONFIG: Record<string, PageCacheConfig> = {
-  // Home page
-  home: {
-    tags: ["home-page"],
-    life: "hours",
-  },
-
-  // Product pages
-  product: {
-    tags: ["all-products"],
-    life: "hours",
-  },
-
-  // Category/Collection pages
-  category: {
-    tags: ["categories"],
-    life: "hours",
-  },
-
-  // Static content
-  static: {
-    tags: ["static-content"],
-    life: "days",
-  },
-
-  // Search results
-  search: {
-    tags: ["search-results"],
-    life: "hours",
-  },
-};
-
-/**
- * Helper to get cache config for a specific page
- */
-export function getPageCacheConfig(
-  page: keyof typeof PAGE_CACHE_CONFIG,
-): PageCacheConfig {
-  return PAGE_CACHE_CONFIG[page];
-}
-
-/**
- * Helper to create dynamic product cache config with specific product identifier
- */
-// export function getProductCacheConfig(productId: string): PageCacheConfig {
-//   return {
-//     tags: ["products", `product-${productId}`],
-//     life: "hours",
-//   };
-// }
-export function getProductCacheConfig(): {noCache: boolean} {
-  return {noCache: true};
-}
-/**
- * Helper to create dynamic category cache config with specific category identifier
- */
-export function getCategoryCacheConfig(categoryId: string): PageCacheConfig {
-  return {
-    tags: ["categories", `category-${categoryId}`],
-    life: "hours",
-  };
-}
-
-
-
-
-/**
- * Wrapper for category-specific queries with dynamic cache tags
- */
-export async function cachedCategoryRequest<
-  TData = unknown,
-  TVariables extends OperationVariables = OperationVariables,
->(
-  categoryId: string,
-  query: DocumentNode,
-  variables?: TVariables,
-): Promise<GraphqlRequestResult<TData>> {
-  const config = getCategoryCacheConfig(categoryId);
-  return graphqlRequest<TData, TVariables>(query, variables, config);
-}
-