feat: get all collection

:%s
This commit is contained in:
lytrankieio123
2021-10-05 10:30:39 +07:00
parent a8955000a5
commit 1546d5df4b
17 changed files with 230 additions and 38 deletions

View File

@@ -2,8 +2,8 @@
"features": { "features": {
"cart": true, "cart": true,
"search": true, "search": true,
"wishlist": false, "wishlist": true,
"customerAuth": false, "customerAuth": true,
"customCheckout": false "customCheckout": true
} }
} }

View File

@@ -10,6 +10,7 @@ import type {
GetProductOperation, GetProductOperation,
} from '../types/product' } from '../types/product'
import type { APIProvider, CommerceAPI } from '.' import type { APIProvider, CommerceAPI } from '.'
import { GetAllCollectionsOperation } from '@commerce/types/collection';
const noop = () => { const noop = () => {
throw new Error('Not implemented') throw new Error('Not implemented')
@@ -25,6 +26,7 @@ export const OPERATIONS = [
'getAllProducts', 'getAllProducts',
'getProduct', 'getProduct',
'getAllFacets', 'getAllFacets',
'getAllCollections',
] as const ] as const
@@ -174,6 +176,22 @@ export type Operations<P extends APIProvider> = {
): Promise<T['data']> ): Promise<T['data']>
} }
getAllCollections: {
<T extends GetAllCollectionsOperation>(opts: {
variables?: T['variables']
config?: P['config']
preview?: boolean
}): Promise<T['data']>
<T extends GetAllCollectionsOperation>(
opts: {
variables?: T['variables']
config?: P['config']
preview?: boolean
} & OperationOptions
): Promise<T['data']>
}
} }

View File

@@ -16,6 +16,7 @@ Adding a commerce provider means adding a new folder in `framework` with a folde
- getProduct - getProduct
- getAllProducts - getAllProducts
- getAllFacets - getAllFacets
- getAllCollections
- `wishlist` - `wishlist`
- useWishlist - useWishlist
- useAddItem - useAddItem

View File

@@ -0,0 +1,54 @@
import { Asset } from '../../vendure/schema.d';
export type Collection = {
id: string
name: string
slug: string
description: string
featuredAsse: Asset
asset: Asset[]
}
export type SearchCollectionsBody = {
search?: string
sort?: string
locale?: string
}
export type CollectionTypes = {
collection: Collection
searchBody: SearchCollectionsBody
}
export type SearchCollectionsHook<T extends CollectionTypes = CollectionTypes> = {
data: {
collections: T['collection'][]
found: boolean
}
body: T['searchBody']
input: T['searchBody']
fetcherInput: T['searchBody']
}
export type CollectionsSchema<T extends CollectionTypes = CollectionTypes> = {
endpoint: {
options: {}
handlers: {
getCollections: SearchCollectionsHook<T>
}
}
}
export type GetAllCollectionsOperation<T extends CollectionTypes = CollectionTypes> = {
data: { collections: T['collection'][] }
variables: {
ids?: string[]
first?: number
}
}
export type GetCollectionOperation<T extends CollectionTypes = CollectionTypes> = {
data: { collection?: T['collection'] }
variables: { code: string; } | { code?: never; }
}

View File

@@ -1,4 +1,4 @@
import { CurrencyCode, FacetValue } from './../../vendure/schema.d'; import { CurrencyCode } from './../../vendure/schema.d';
import { FacetValueFilterInput, LogicalOperator, SearchResultSortParameter } from "@framework/schema" import { FacetValueFilterInput, LogicalOperator, SearchResultSortParameter } from "@framework/schema"
export type ProductImage = { export type ProductImage = {
@@ -59,8 +59,7 @@ export type ProductCard = {
weight?: number weight?: number
facetValueIds?: string[], facetValueIds?: string[],
collectionIds?: string[], collectionIds?: string[],
// TODO: collection collection?: string,
category?: string,
isNotSell?: boolean isNotSell?: boolean
} }

View File

@@ -1,6 +1,7 @@
import type { CommerceAPIConfig } from '@commerce/api' import type { CommerceAPIConfig } from '@commerce/api'
import { CommerceAPI, getCommerceApi as commerceApi } from '@commerce/api' import { CommerceAPI, getCommerceApi as commerceApi } from '@commerce/api'
import getAllFacets from './operations/get-all-facets' import getAllFacets from './operations/get-all-facets'
import getAllCollections from './operations/get-all-collection'
import getAllPages from './operations/get-all-pages' import getAllPages from './operations/get-all-pages'
import getAllProductPaths from './operations/get-all-product-paths' import getAllProductPaths from './operations/get-all-product-paths'
import getAllProducts from './operations/get-all-products' import getAllProducts from './operations/get-all-products'
@@ -42,6 +43,7 @@ const operations = {
getAllProducts, getAllProducts,
getProduct, getProduct,
getAllFacets, getAllFacets,
getAllCollections,
} }
export const provider = { config, operations } export const provider = { config, operations }

View File

@@ -0,0 +1,45 @@
import { OperationContext } from '@commerce/api/operations'
import { Collection } from '@commerce/types/collection'
import { Provider, VendureConfig } from '..'
import { GetAllCollectionsQuery } from '../../schema'
import { getAllCollectionsQuery } from '../../utils/queries/get-all-collections-query'
export type CollectionVariables = { first?: number }
export default function getAllCollectionsOperation({
commerce,
}: OperationContext<Provider>) {
async function getAllCollections(opts?: {
variables?: CollectionVariables
config?: Partial<VendureConfig>
preview?: boolean
}): Promise<{ collections: Collection[] }>
async function getAllCollections({
query = getAllCollectionsQuery,
variables: { ...vars } = {},
config: cfg,
}: {
query?: string
variables?: CollectionVariables
config?: Partial<VendureConfig>
preview?: boolean
} = {}): Promise<{ collections: Collection[] | any[] }> {
const config = commerce.getConfig(cfg)
const variables = {
input: {
take: vars.first,
groupByCollection: true,
},
}
const { data } = await config.fetch<GetAllCollectionsQuery>(query, {
variables,
})
return {
collections: data.collections.items,
}
}
return getAllCollections
}

View File

@@ -3240,6 +3240,23 @@ export type GetAllFacetsQuery = { __typename?: 'Query' } & {
} }
} }
export type GetAllCollectionsQuery = { __typename?: 'Query' } & {
collections: { __typename?: 'CollectionList' } & {
items: Array<
{ __typename?: 'Collection' } & Pick<
Collection,
'id' | 'name' | 'slug'
> & {
parent?: Maybe<{ __typename?: 'Collection' } & Pick<Collection, 'id'>>
children?: Maybe<
Array<{ __typename?: 'Collection' } & Pick<Collection, 'id'>>
>
}
>,
'totalItems'
}
}
export type ActiveOrderQueryVariables = Exact<{ [key: string]: never }> export type ActiveOrderQueryVariables = Exact<{ [key: string]: never }>
export type ActiveOrderQuery = { __typename?: 'Query' } & { export type ActiveOrderQuery = { __typename?: 'Query' } & {

View File

@@ -18,7 +18,6 @@ export function normalizeSearchResult(item: SearchResultFragment): ProductCard {
// discount // discount
// isNotSell // isNotSell
// weight // weight
// category
} }
} }

View File

@@ -0,0 +1,12 @@
export const getAllCollectionsQuery = /* GraphQL */ `
query collections ($options: CollectionListOptions) {
collections (options: $options){
totalItems,
items {
id
name
slug
}
}
}
`

View File

@@ -1,28 +1,31 @@
import { ProductCard } from '@commerce/types/product'; import { ProductCard } from '@commerce/types/product';
import { ProductVariables } from '@framework/api/operations/get-all-products'; import { ProductVariables } from '@framework/api/operations/get-all-products';
import { FacetValue } from '@framework/schema'; import { Collection, FacetValue } from '@framework/schema';
import commerce from '@lib/api/commerce'; import commerce from '@lib/api/commerce';
import { GetStaticPropsContext } from 'next'; import { GetStaticPropsContext } from 'next';
import { Layout } from 'src/components/common'; import { Layout } from 'src/components/common';
import { FeaturedProductsCarousel, FreshProducts, HomeBanner, HomeCategories, HomeCollection, HomeCTA, HomeFeature, HomeRecipe, HomeSubscribe, HomeVideo } from 'src/components/modules/home'; import { FeaturedProductsCarousel, FreshProducts, HomeBanner, HomeCategories, HomeCollection, HomeCTA, HomeFeature, HomeRecipe, HomeSubscribe, HomeVideo } from 'src/components/modules/home';
import HomeSpice from 'src/components/modules/home/HomeSpice/HomeSpice'; import HomeSpice from 'src/components/modules/home/HomeSpice/HomeSpice';
import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED } from 'src/utils/constanst.utils'; import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED } from 'src/utils/constanst.utils';
import { getAllFacetValueIdsByParentCode, getAllFacetValuesForFeatuedProducts, getFreshFacetId } from 'src/utils/funtion.utils'; import { getAllFacetValueIdsByParentCode, getAllFacetValuesForFeatuedProducts, getAllPromies, getFreshFacetId } from 'src/utils/funtion.utils';
import { PromiseWithKey } from 'src/utils/types.utils';
interface Props { interface Props {
featuredAndDiscountFacetsValue: FacetValue[], featuredAndDiscountFacetsValue: FacetValue[],
freshProducts: ProductCard[], freshProducts: ProductCard[],
featuredProducts: ProductCard[], featuredProducts: ProductCard[],
collections: Collection[]
} }
export default function Home({ featuredAndDiscountFacetsValue, export default function Home({ featuredAndDiscountFacetsValue,
freshProducts, featuredProducts }: Props) { freshProducts, featuredProducts,
collections }: Props) {
return ( return (
<> <>
<HomeBanner /> <HomeBanner />
<HomeFeature /> <HomeFeature />
<HomeCategories /> <HomeCategories />
<FreshProducts data={freshProducts} /> <FreshProducts data={freshProducts} collections={collections} />
<HomeCollection /> <HomeCollection />
<HomeVideo /> <HomeVideo />
<HomeSpice /> <HomeSpice />
@@ -44,13 +47,15 @@ export async function getStaticProps({
locales, locales,
}: GetStaticPropsContext) { }: GetStaticPropsContext) {
const config = { locale, locales } const config = { locale, locales }
let promisesWithKey = [] as PromiseWithKey[]
let props = {} as any
const { facets } = await commerce.getAllFacets({ const { facets } = await commerce.getAllFacets({
variables: {}, variables: {},
config, config,
preview, preview,
}) })
const featuredAndDiscountFacetsValue = getAllFacetValuesForFeatuedProducts(facets) props.featuredAndDiscountFacetsValue = getAllFacetValuesForFeatuedProducts(facets)
// fresh products // fresh products
const freshProductvariables: ProductVariables = {} const freshProductvariables: ProductVariables = {}
@@ -63,6 +68,8 @@ export async function getStaticProps({
config, config,
preview, preview,
}) })
promisesWithKey.push({ key: 'freshProducts', promise: freshProductsPromise, keyResult: 'products' })
// featured products // featured products
const allFeaturedFacetIds = getAllFacetValueIdsByParentCode(facets, CODE_FACET_FEATURED) const allFeaturedFacetIds = getAllFacetValueIdsByParentCode(facets, CODE_FACET_FEATURED)
@@ -75,28 +82,33 @@ export async function getStaticProps({
config, config,
preview, preview,
}) })
promisesWithKey.push({ key: 'featuredProducts', promise: featuredProductsPromise, keyResult: 'products' })
// collection
const collectionsPromise = commerce.getAllCollections({
variables: {},
config,
preview,
})
promisesWithKey.push({ key: 'collections', promise: collectionsPromise, keyResult: 'collections' })
try { try {
const rs = await Promise.all([ const promises = getAllPromies(promisesWithKey)
freshProductsPromise, const rs = await Promise.all(promises)
featuredProductsPromise,
]) promisesWithKey.map((item, index) => {
props[item.key] = item.keyResult ? rs[index][item.keyResult] : rs[index]
return null
})
return { return {
props: { props,
featuredAndDiscountFacetsValue,
freshProducts: freshFacetId ? rs[0].products : [],
featuredProducts: facetValueIdsForFeaturedProducts.length > 0 ? rs[1].products : []
},
revalidate: 60, revalidate: 60,
} }
} catch (err) { } catch (err) {
} }
} }

View File

@@ -2,6 +2,7 @@ import classNames from 'classnames'
import React from 'react' import React from 'react'
import s from './LabelCommon.module.scss' import s from './LabelCommon.module.scss'
interface LabelCommonProps extends React.HTMLAttributes<HTMLDivElement> { interface LabelCommonProps extends React.HTMLAttributes<HTMLDivElement> {
children?: React.ReactNode
size?: 'default' | 'large' size?: 'default' | 'large'
shape?: 'half' | 'round' | 'default' shape?: 'half' | 'round' | 'default'
type?: 'default' | 'discount' | 'waiting' | 'delivering' | 'delivered' type?: 'default' | 'discount' | 'waiting' | 'delivering' | 'delivered'

View File

@@ -17,7 +17,7 @@ export interface ProductCardProps extends ProductCard {
} }
const ProductCardComponent = ({ const ProductCardComponent = ({
category, collection,
name, name,
slug, slug,
weight, weight,
@@ -45,9 +45,9 @@ const ProductCardComponent = ({
</a> </a>
</Link> </Link>
{ {
category && collection &&
<div className={s.productLabel}> <div className={s.productLabel}>
<LabelCommon shape="half">{category}</LabelCommon> <LabelCommon shape="half">{collection}</LabelCommon>
</div> </div>
} }
</div> </div>

View File

@@ -12,7 +12,7 @@ interface ColectionCarcouselProps extends CollectionHeadingProps {
data: ProductCardProps[] data: ProductCardProps[]
itemKey: string itemKey: string
viewAllLink?: string, viewAllLink?: string,
category:string category?: string
} }
const ColectionCarcousel = ({ const ColectionCarcousel = ({
@@ -21,7 +21,8 @@ const ColectionCarcousel = ({
title, title,
subtitle, subtitle,
type, type,
category category,
viewAllLink = ROUTE.PRODUCTS,
}: ColectionCarcouselProps) => { }: ColectionCarcouselProps) => {
return ( return (
<div className={s.colectionCarcoucelWarpper}> <div className={s.colectionCarcoucelWarpper}>
@@ -34,7 +35,7 @@ const ColectionCarcousel = ({
></CollectionHeading> ></CollectionHeading>
</div> </div>
<div className={s.right}> <div className={s.right}>
<ViewAllItem link={`${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=${category}`}/> <ViewAllItem link={category ? `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=${category}` : viewAllLink} />
</div> </div>
</div> </div>
<div className={s.bot}> <div className={s.bot}>

View File

@@ -1,12 +1,32 @@
import { ProductCard } from '@commerce/types/product' import { ProductCard } from '@commerce/types/product'
import { Product } from '@framework/schema' import { Collection } from '@framework/schema'
import React from 'react' import React, { useMemo } from 'react'
import { OPTION_ALL, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'
import { CollectionCarcousel } from '..' import { CollectionCarcousel } from '..'
interface FreshProductsProps { interface FreshProductsProps {
data: ProductCard[] data: ProductCard[]
collections: Collection[]
} }
const FreshProducts = ({ data }: FreshProductsProps) => { const getCategoryNameFromCollectionId = (colelctions: Collection[], collectionId?: string ) => {
if (!collectionId) {
return ''
}
const collection = colelctions.find(item => item.id === collectionId)
return collection?.name || ''
}
const FreshProducts = ({ data, collections }: FreshProductsProps) => {
const dataWithCategory = useMemo(() => {
return data.map(item => {
return {
...item,
collection: getCategoryNameFromCollectionId(collections, item.collectionIds ? item.collectionIds[0] : undefined)
}
})
}, [data, collections])
if (data.length === 0) { if (data.length === 0) {
return null return null
} }
@@ -14,11 +34,11 @@ const FreshProducts = ({ data }: FreshProductsProps) => {
<div className="w-full"> <div className="w-full">
<CollectionCarcousel <CollectionCarcousel
type="highlight" type="highlight"
data={data} data={dataWithCategory}
itemKey="product-1" itemKey="product-1"
title="Fresh Products Today" title="Fresh Products Today"
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can." subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
category={"veggie"} viewAllLink={`${ROUTE.PRODUCTS}/?${QUERY_KEY.FEATURED}=${OPTION_ALL}`}
/> />
</div> </div>
) )

View File

@@ -1,6 +1,7 @@
import { Facet } from "@commerce/types/facet"; import { Facet } from "@commerce/types/facet";
import { FacetValue } from './../../framework/vendure/schema.d'; import { FacetValue } from './../../framework/vendure/schema.d';
import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED, CODE_FACET_FEATURED_VARIANT } from "./constanst.utils"; import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED, CODE_FACET_FEATURED_VARIANT } from "./constanst.utils";
import { PromiseWithKey } from "./types.utils";
export function isMobile() { export function isMobile() {
return window.innerWidth < 768 return window.innerWidth < 768
@@ -55,4 +56,8 @@ export function getFacetNamesFromIds(facets: FacetValue[], ids?: string[]): stri
const facetItems = facets.filter((item: FacetValue) => ids.includes(item.id)) const facetItems = facets.filter((item: FacetValue) => ids.includes(item.id))
const names = facetItems.map((item: FacetValue) => item.name) const names = facetItems.map((item: FacetValue) => item.name)
return names.join(", ") return names.join(", ")
} }
export function getAllPromies (promies: PromiseWithKey[]) {
return promies.map(item => item.promise)
}

View File

@@ -50,4 +50,10 @@ export type filterContextType = {
visible: boolean; visible: boolean;
open: () => void; open: () => void;
close: () => void; close: () => void;
}; };
export type PromiseWithKey = {
key: string
promise: PromiseLike<any>
keyResult?: string,
}