Merge branch 'feature/m1-get-product-detail' of github.com:KieIO/grocery-vercel-commerce into feature/m3-add-product-to-cart-product-detail

This commit is contained in:
DatNguyen
2021-10-11 16:05:56 +07:00
15 changed files with 79 additions and 46 deletions

View File

@@ -47,6 +47,8 @@ export type Product = {
currencyCode: CurrencyCode currencyCode: CurrencyCode
options: ProductOption[] options: ProductOption[]
facetValueIds?: string[] facetValueIds?: string[]
collectionIds?: string[]
collection?: string,
} }
export type ProductCard = { export type ProductCard = {

View File

@@ -2,7 +2,7 @@ import { Product } from '@commerce/types/product'
import { OperationContext } from '@commerce/api/operations' import { OperationContext } from '@commerce/api/operations'
import { Provider, VendureConfig } from '../' import { Provider, VendureConfig } from '../'
import { GetProductQuery } from '../../schema' import { GetProductQuery } from '../../schema'
import { getProductQuery } from '../../utils/queries/get-product-query' import { getProductQuery, getProductDetailQuery } from '../../utils/queries/get-product-query'
export default function getProductOperation({ export default function getProductOperation({
commerce, commerce,
@@ -53,7 +53,8 @@ export default function getProductOperation({
displayName: og.name, displayName: og.name,
values: og.options.map((o) => ({ label: o.name })), values: og.options.map((o) => ({ label: o.name })),
})), })),
facetValueIds: product.facetValues.map(item=> item.id) facetValueIds: product.facetValues.map(item=> item.id),
collectionIds: product.collections.map(item => item.id)
} as Product } as Product
} }

View File

@@ -3303,7 +3303,7 @@ export type GetProductQuery = { __typename?: 'Query' } & {
variants: Array< variants: Array<
{ __typename?: 'ProductVariant' } & Pick< { __typename?: 'ProductVariant' } & Pick<
ProductVariant, ProductVariant,
'id' | 'priceWithTax' | 'currencyCode' 'id' | 'priceWithTax' | 'currencyCode' | 'price'
> & { > & {
options: Array< options: Array<
{ __typename?: 'ProductOption' } & Pick< { __typename?: 'ProductOption' } & Pick<

View File

@@ -39,6 +39,25 @@ export const getProductQuery = /* GraphQL */ `
facetValues { facetValues {
id id
} }
collections {
id
}
} }
} }
` `
export const getProductDetailQuery = /* GraphQL */ `
query GetProductDetail($slug: String! = "hand-trowel") {
product(slug: $slug) {
name
description
variants {
price
priceWithTax
}
assets {
preview
name
}
}
}
`

3
next-env.d.ts vendored
View File

@@ -1,6 +1,3 @@
/// <reference types="next" /> /// <reference types="next" />
/// <reference types="next/types/global" /> /// <reference types="next/types/global" />
/// <reference types="next/image-types/global" /> /// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@@ -26,6 +26,7 @@
"body-scroll-lock": "^3.1.5", "body-scroll-lock": "^3.1.5",
"classnames": "^2.3.1", "classnames": "^2.3.1",
"cookie": "^0.4.1", "cookie": "^0.4.1",
"dns": "^0.2.2",
"email-validator": "^2.0.4", "email-validator": "^2.0.4",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-config-next": "^11.1.2", "eslint-config-next": "^11.1.2",
@@ -35,6 +36,7 @@
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",
"lodash.random": "^3.2.0", "lodash.random": "^3.2.0",
"lodash.throttle": "^4.1.1", "lodash.throttle": "^4.1.1",
"net": "^1.0.2",
"next": "^11.0.0", "next": "^11.0.0",
"next-seo": "^4.26.0", "next-seo": "^4.26.0",
"next-themes": "^0.0.14", "next-themes": "^0.0.14",

View File

@@ -12,7 +12,7 @@ import { PromiseWithKey } from 'src/utils/types.utils'
export default function Slug({ product, relevantProducts, collections }: InferGetStaticPropsType<typeof getStaticProps>) { export default function Slug({ product, relevantProducts, collections }: InferGetStaticPropsType<typeof getStaticProps>) {
return <> return <>
<ProductInfoDetail /> <ProductInfoDetail productDetail={product} collections={collections}/>
<RecipeDetail ingredients={INGREDIENT_DATA_TEST} /> <RecipeDetail ingredients={INGREDIENT_DATA_TEST} />
<RecommendedRecipes data={RECIPE_DATA_TEST} /> <RecommendedRecipes data={RECIPE_DATA_TEST} />
<ReleventProducts data={relevantProducts} collections={collections}/> <ReleventProducts data={relevantProducts} collections={collections}/>

View File

@@ -1,11 +1,12 @@
import commerce from '@lib/api/commerce'; import commerce from '@lib/api/commerce';
import { GetStaticPropsContext } from 'next'; import { GetStaticPropsContext } from 'next';
import { ProductCard } from '@commerce/types/product';
import { Layout, ListProductCardSkeleton } from 'src/components/common'; import { Layout, ListProductCardSkeleton } from 'src/components/common';
interface Props { interface Props {
products: any productDetail: ProductCard[],
} }
export default function Home({ products }: Props) { export default function Home({ productDetail }: Props) {
return ( return (
<> <>
{/* <ListProductCardSkeleton /> */} {/* <ListProductCardSkeleton /> */}

View File

@@ -0,0 +1 @@
export { default as useProductDetail } from './useProductDetail'

View File

@@ -0,0 +1,16 @@
import { GetProductQuery } from '@framework/schema'
import { getProductDetailQuery } from '@framework/utils/queries/get-product-query';
import gglFetcher from 'src/utils/gglFetcher'
import useSWR from 'swr'
interface ProductDetail {
slug: string
}
const useProductDetail = () => {
const { data, ...rest } = useSWR<GetProductQuery>([getProductDetailQuery],gglFetcher)
return { productDetail: data?.product, ...rest }
}
export default useProductDetail

View File

@@ -1,20 +1,28 @@
import React from 'react' import React, { useMemo } from 'react';
import ProductImgs from './components/ProductImgs/ProductImgs' import ProductImgs from './components/ProductImgs/ProductImgs'
import ProductInfo from './components/ProductInfo/ProductInfo' import ProductInfo from './components/ProductInfo/ProductInfo'
import s from './ProductInfoDetail.module.scss' import s from './ProductInfoDetail.module.scss'
import { Product } from '@commerce/types/product'
import { Collection } from '@framework/schema'
import { getCategoryNameFromCollectionId } from 'src/utils/funtion.utils';
interface Props { interface Props {
className?: string productDetail: Product,
children?: any collections: Collection[]
} }
const ProductInfoDetail = ({ }: Props) => { const ProductInfoDetail = ({ productDetail, collections }: Props) => {
const dataWithCategoryName = useMemo(() => {
return {
...productDetail,
collection: getCategoryNameFromCollectionId(collections, productDetail.collectionIds ? productDetail.collectionIds[0] : undefined)
}
}, [productDetail, collections])
return ( return (
<section className={s.productInfoDetail}> <section className={s.productInfoDetail}>
<ProductImgs/> <ProductImgs productImage={productDetail.images}/>
<ProductInfo/> <ProductInfo productInfoDetail={dataWithCategoryName}/>
</section > </section >
) )
} }
export default ProductInfoDetail export default ProductInfoDetail

View File

@@ -3,15 +3,15 @@ import { ImgWithLink } from 'src/components/common'
import s from './ProductImgItem.module.scss' import s from './ProductImgItem.module.scss'
export interface ProductImgItemProps { export interface ProductImgItemProps {
src: string url: string
alt?: string alt?: string
} }
const ProductImgItem = ({ src, alt }: ProductImgItemProps) => { const ProductImgItem = ({ url, alt }: ProductImgItemProps) => {
return ( return (
<section className={s.productImgItem}> <section className={s.productImgItem}>
<ImgWithLink src={src} alt={alt} /> <ImgWithLink src={url} alt={alt} />
</section > </section >
) )
} }

View File

@@ -3,26 +3,12 @@ import { ResponsiveType } from 'react-multi-carousel'
import { CarouselCommon } from 'src/components/common' import { CarouselCommon } from 'src/components/common'
import ProductImgItem, { ProductImgItemProps } from '../ProductImgItem/ProductImgItem' import ProductImgItem, { ProductImgItemProps } from '../ProductImgItem/ProductImgItem'
import s from './ProductImgs.module.scss' import s from './ProductImgs.module.scss'
import { ProductImage } from '@commerce/types/product';
interface Props { interface Props {
className?: string productImage: ProductImage[]
children?: any,
} }
const DATA = [
{
src: 'https://user-images.githubusercontent.com/76729908/133026929-199799fc-bd75-4445-a24d-15c0e41796eb.png',
alt: 'Meat',
},
{
src: 'https://user-images.githubusercontent.com/76729908/130574371-3b75fa72-9552-4605-aba9-a4b31cd9dce7.png',
alt: 'Broccoli',
},
{
src: 'https://user-images.githubusercontent.com/76729908/130574371-3b75fa72-9552-4605-aba9-a4b31cd9dce7.png',
alt: 'Broccoli',
}
]
const RESPONSIVE: ResponsiveType = { const RESPONSIVE: ResponsiveType = {
desktop: { desktop: {
@@ -31,11 +17,11 @@ const RESPONSIVE: ResponsiveType = {
slidesToSlide: 1, // optional, default to 1. slidesToSlide: 1, // optional, default to 1.
}, },
} }
const ProductImgs = ({ }: Props) => { const ProductImgs = ({ productImage }: Props) => {
return ( return (
<section className={s.productImgs}> <section className={s.productImgs}>
<CarouselCommon<ProductImgItemProps> <CarouselCommon<ProductImgItemProps>
data={DATA} data={productImage}
itemKey="product-detail-img" itemKey="product-detail-img"
Component={ProductImgItem} Component={ProductImgItem}
responsive={RESPONSIVE} responsive={RESPONSIVE}

View File

@@ -1,3 +1,4 @@
import { Product } from '@commerce/types/product'
import React from 'react' import React from 'react'
import { ButtonCommon, LabelCommon, QuanittyInput } from 'src/components/common' import { ButtonCommon, LabelCommon, QuanittyInput } from 'src/components/common'
import { IconBuy } from 'src/components/icons' import { IconBuy } from 'src/components/icons'
@@ -5,25 +6,25 @@ import { LANGUAGE } from 'src/utils/language.utils'
import s from './ProductInfo.module.scss' import s from './ProductInfo.module.scss'
interface Props { interface Props {
className?: string productInfoDetail: Product
children?: any,
} }
const ProductInfo = ({ }: Props) => { const ProductInfo = ({ productInfoDetail }: Props) => {
console.log(productInfoDetail)
return ( return (
<section className={s.productInfo}> <section className={s.productInfo}>
<div className={s.info}> <div className={s.info}>
<LabelCommon shape='half'>SEAFOOD</LabelCommon> <LabelCommon shape='half'>{productInfoDetail.collection}</LabelCommon>
<h2 className={s.heading}>SeaPAk</h2> <h2 className={s.heading}>{productInfoDetail.name}</h2>
<div className={s.price}> <div className={s.price}>
<div className={s.old}> <div className={s.old}>
<span className={s.number}>Rp 32.000</span> <span className={s.number}>Rp {productInfoDetail.price}</span>
<LabelCommon type='discount'>-15%</LabelCommon> <LabelCommon type='discount'>-15%</LabelCommon>
</div> </div>
<div className={s.current}>Rp 27.500</div> <div className={s.current}>Rp {productInfoDetail.price}</div>
</div> </div>
<div className={s.description}> <div className={s.description}>
In a large non-reactive dish, mix together the orange juice, soy sauce, olive oil, lemon juice, parsley {productInfoDetail.description}
</div> </div>
</div> </div>
<div className={s.actions}> <div className={s.actions}>

View File

@@ -23,7 +23,6 @@ const ReleventProducts = ({ data, collections }: Props) => {
if (data.length === 0) { if (data.length === 0) {
return null return null
} }
return ( return (
<ListProductWithInfo <ListProductWithInfo
title="Relevant Products" title="Relevant Products"