diff --git a/framework/commerce/types/product.ts b/framework/commerce/types/product.ts index eb69371db..e31f34df9 100644 --- a/framework/commerce/types/product.ts +++ b/framework/commerce/types/product.ts @@ -30,6 +30,7 @@ export type ProductOptionValues = { export type ProductVariant = { id: string | number + name:string, options: ProductOption[] availableForSale?: boolean } diff --git a/framework/vendure/api/operations/get-product.ts b/framework/vendure/api/operations/get-product.ts index b7ddc5c42..66f798ee1 100644 --- a/framework/vendure/api/operations/get-product.ts +++ b/framework/vendure/api/operations/get-product.ts @@ -36,6 +36,7 @@ export default function getProductOperation({ })), variants: product.variants.map((v) => ({ id: v.id, + name:v.name, options: v.options.map((o) => ({ // This __typename property is required in order for the correct // variant selection to work, see `components/product/helpers.ts` diff --git a/framework/vendure/schema.d.ts b/framework/vendure/schema.d.ts index ad7662bec..465f33cfb 100644 --- a/framework/vendure/schema.d.ts +++ b/framework/vendure/schema.d.ts @@ -3303,7 +3303,7 @@ export type GetProductQuery = { __typename?: 'Query' } & { variants: Array< { __typename?: 'ProductVariant' } & Pick< ProductVariant, - 'id' | 'priceWithTax' | 'currencyCode' | 'price' + 'id' | 'priceWithTax' | 'currencyCode' | 'price' | "name" > & { options: Array< { __typename?: 'ProductOption' } & Pick< diff --git a/framework/vendure/utils/normalize.ts b/framework/vendure/utils/normalize.ts index 66a0e525d..afe453f52 100644 --- a/framework/vendure/utils/normalize.ts +++ b/framework/vendure/utils/normalize.ts @@ -1,5 +1,5 @@ import { Cart } from '@commerce/types/cart' -import { ProductCard } from '@commerce/types/product' +import { ProductCard, Product } from '@commerce/types/product' import { CartFragment, SearchResultFragment } from '../schema' export function normalizeSearchResult(item: SearchResultFragment): ProductCard { @@ -57,3 +57,18 @@ export function normalizeCart(order: CartFragment): Cart { })), } } + +export function normalizeProductCard(product: Product): ProductCard { + return { + id: product.id, + name: product.name, + slug: product.slug, + imageSrc: product.images[0].url, + price: product.price, + currencyCode: product.currencyCode, + productVariantId: product.variants?.[0].id.toString(), + productVariantName:product.variants?.[0].name, + facetValueIds: product.facetValueIds, + collectionIds: product.collectionIds, + } +} \ No newline at end of file diff --git a/framework/vendure/utils/queries/get-product-query.ts b/framework/vendure/utils/queries/get-product-query.ts index 2eb7ee278..1ff057e61 100644 --- a/framework/vendure/utils/queries/get-product-query.ts +++ b/framework/vendure/utils/queries/get-product-query.ts @@ -12,6 +12,7 @@ export const getProductQuery = /* GraphQL */ ` } variants { id + name priceWithTax currencyCode options { diff --git a/pages/product/[slug].tsx b/pages/product/[slug].tsx index 2da14a995..7b45f7270 100644 --- a/pages/product/[slug].tsx +++ b/pages/product/[slug].tsx @@ -1,22 +1,48 @@ -import { Product } from '@framework/schema' +import { Collection } from '@commerce/types/collection' +import { Product, ProductCard } from '@commerce/types/product' import commerce from '@lib/api/commerce' import { GetStaticPathsContext, GetStaticPropsContext, InferGetStaticPropsType } from 'next' +import { useEffect } from 'react' import { Layout, RecipeDetail, RecommendedRecipes, RelevantBlogPosts } from 'src/components/common' +import { useLocalStorage } from 'src/components/hooks/useLocalStorage' import { ProductInfoDetail, ReleventProducts, ViewedProducts } from 'src/components/modules/product-detail' -import { MAX_PRODUCT_CAROUSEL, REVALIDATE_TIME } from 'src/utils/constanst.utils' +import { LOCAL_STORAGE_KEY, MAX_PRODUCT_CAROUSEL, REVALIDATE_TIME } from 'src/utils/constanst.utils' import { BLOGS_DATA_TEST, INGREDIENT_DATA_TEST, RECIPE_DATA_TEST } from 'src/utils/demo-data' import { getAllPromies } from 'src/utils/funtion.utils' import { PromiseWithKey } from 'src/utils/types.utils' - -export default function Slug({ product, relevantProducts, collections }: InferGetStaticPropsType) { - +interface Props { + relevantProducts: ProductCard[], + product: Product, + collections: Collection[] +} +export default function Slug({ product, relevantProducts, collections }: Props) { + const [viewedProduct,setViewedProduct] = useLocalStorage(LOCAL_STORAGE_KEY.VIEWEDPRODUCT, []); + useEffect(() => { + // const local = localStorage.getItem(LOCAL_STORAGE_KEY.VIEWEDPRODUCT) + // if(local){ + // const oldList:Product[] = JSON.parse(local) + // if(!oldList.find(p => p.id === product.id)){ + // localStorage.setItem(LOCAL_STORAGE_KEY.VIEWEDPRODUCT,JSON.stringify([...oldList,product])) + // } + // }else{ + // localStorage.setItem(LOCAL_STORAGE_KEY.VIEWEDPRODUCT,JSON.stringify([product])) + // } + if(viewedProduct){ + if(!viewedProduct.find(p => p.id === product.id)){ + setViewedProduct([...viewedProduct, product]) + } + }else{ + setViewedProduct([product]) + } + }, [product]) + return <> - + - + } @@ -68,7 +94,6 @@ export async function getStaticProps({ }) promisesWithKey.push({ key: 'collections', promise: collectionsPromise, keyResult: 'collections' }) - try { const promises = getAllPromies(promisesWithKey) const rs = await Promise.all(promises) diff --git a/src/components/hooks/useLocalStorage.tsx b/src/components/hooks/useLocalStorage.tsx new file mode 100644 index 000000000..b265ded9a --- /dev/null +++ b/src/components/hooks/useLocalStorage.tsx @@ -0,0 +1,36 @@ +import { useState } from "react"; + +// Hook +export function useLocalStorage(key: string, initialValue: T) { + // State to store our value + // Pass initial state function to useState so logic is only executed once + const [storedValue, setStoredValue] = useState(() => { + try { + // Get from local storage by key + const item = localStorage.getItem(key); + // Parse stored json or if none return initialValue + return item ? JSON.parse(item) : initialValue; + } catch (error) { + // If error also return initialValue + // console.log(error); + return initialValue; + } + }); + // Return a wrapped version of useState's setter function that ... + // ... persists the new value to localStorage. + const setValue = (value: T | ((val: T) => T)) => { + try { + // Allow value to be a function so we have same API as useState + const valueToStore = + value instanceof Function ? value(storedValue) : value; + // Save state + setStoredValue(valueToStore); + // Save to local storage + localStorage.setItem(key, JSON.stringify(valueToStore)); + } catch (error) { + // A more advanced implementation would handle the error case + // console.log(error); + } + }; + return [storedValue, setValue] as const; +} \ No newline at end of file diff --git a/src/components/modules/product-detail/ProductInfoDetail/ProductInfoDetail.tsx b/src/components/modules/product-detail/ProductInfoDetail/ProductInfoDetail.tsx index 64a00f7e6..5e51bd090 100644 --- a/src/components/modules/product-detail/ProductInfoDetail/ProductInfoDetail.tsx +++ b/src/components/modules/product-detail/ProductInfoDetail/ProductInfoDetail.tsx @@ -3,7 +3,6 @@ import ProductImgs from './components/ProductImgs/ProductImgs' import ProductInfo from './components/ProductInfo/ProductInfo' import s from './ProductInfoDetail.module.scss' import { Product } from '@commerce/types/product' -import { Collection } from '@framework/schema' interface Props { productDetail: Product, diff --git a/src/components/modules/product-detail/ReleventProducts/ReleventProducts.tsx b/src/components/modules/product-detail/ReleventProducts/ReleventProducts.tsx index d1afde538..9e2581833 100644 --- a/src/components/modules/product-detail/ReleventProducts/ReleventProducts.tsx +++ b/src/components/modules/product-detail/ReleventProducts/ReleventProducts.tsx @@ -1,5 +1,5 @@ +import { Collection } from '@commerce/types/collection'; import { ProductCard } from '@commerce/types/product'; -import { Collection } from '@framework/schema'; import React, { useMemo } from 'react'; import ListProductWithInfo from 'src/components/common/ListProductWithInfo/ListProductWithInfo'; import { getCategoryNameFromCollectionId } from 'src/utils/funtion.utils'; diff --git a/src/components/modules/product-detail/ViewedProducts/ViewedProducts.tsx b/src/components/modules/product-detail/ViewedProducts/ViewedProducts.tsx index 87e600416..7d10f761d 100644 --- a/src/components/modules/product-detail/ViewedProducts/ViewedProducts.tsx +++ b/src/components/modules/product-detail/ViewedProducts/ViewedProducts.tsx @@ -1,13 +1,28 @@ -import React from 'react'; +import { Product } from '@commerce/types/product'; +import React, { useEffect, useState } from 'react'; import ListProductWithInfo from 'src/components/common/ListProductWithInfo/ListProductWithInfo'; -import { PRODUCT_DATA_TEST } from 'src/utils/demo-data'; +import { ProductCardProps } from 'src/components/common/ProductCard/ProductCard'; +import { LOCAL_STORAGE_KEY } from 'src/utils/constanst.utils' +import { normalizeProductCard } from '@framework/utils/normalize'; +import { useLocalStorage } from 'src/components/hooks/useLocalStorage'; +interface Props { +} +const ViewedProducts = ({}:Props) => { + const [data, setData] = useState([]) + const [viewedProduct] = useLocalStorage(LOCAL_STORAGE_KEY.VIEWEDPRODUCT, []); -const ViewedProducts = () => { + useEffect(() => { + setData(viewedProduct.map((p)=>normalizeProductCard(p))) + }, [viewedProduct]) + + if (data.length>0){ + return
+ } return ( ); diff --git a/src/utils/constanst.utils.ts b/src/utils/constanst.utils.ts index 082154f5c..9d03994a3 100644 --- a/src/utils/constanst.utils.ts +++ b/src/utils/constanst.utils.ts @@ -44,7 +44,8 @@ export const ACCOUNT_TAB = { } export const LOCAL_STORAGE_KEY = { - TOKEN: 'token' + TOKEN: 'token', + VIEWEDPRODUCT: "viewed-product" } export const QUERY_SPLIT_SEPERATOR = ',' diff --git a/src/utils/funtion.utils.ts b/src/utils/funtion.utils.ts index 73cd6094a..94521e072 100644 --- a/src/utils/funtion.utils.ts +++ b/src/utils/funtion.utils.ts @@ -1,6 +1,7 @@ +import { Collection } from '@commerce/types/collection'; import { Facet } from "@commerce/types/facet"; import { Product, ProductCard, ProductOption, ProductOptionValues } from "@commerce/types/product"; -import { Collection, FacetValue, SearchResultSortParameter } from './../../framework/vendure/schema.d'; +import { FacetValue, SearchResultSortParameter } from './../../framework/vendure/schema.d'; import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED, CODE_FACET_FEATURED_VARIANT, FACET, PRODUCT_SORT_OPTION_VALUE } from "./constanst.utils"; import { PromiseWithKey, SelectedOptions, SortOrder } from "./types.utils";