mirror of
https://github.com/vercel/commerce.git
synced 2025-07-23 04:36:49 +00:00
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:
@@ -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 = {
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
framework/vendure/schema.d.ts
vendored
2
framework/vendure/schema.d.ts
vendored
@@ -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<
|
||||||
|
@@ -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
3
next-env.d.ts
vendored
@@ -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.
|
|
||||||
|
@@ -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",
|
||||||
|
@@ -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}/>
|
||||||
|
@@ -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 /> */}
|
||||||
|
1
src/components/hooks/product/index.tsx
Normal file
1
src/components/hooks/product/index.tsx
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default as useProductDetail } from './useProductDetail'
|
16
src/components/hooks/product/useProductDetail.tsx
Normal file
16
src/components/hooks/product/useProductDetail.tsx
Normal 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
|
@@ -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
|
||||||
|
@@ -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 >
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -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}
|
||||||
|
@@ -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}>
|
||||||
|
@@ -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"
|
||||||
|
Reference in New Issue
Block a user