diff --git a/framework/commerce/api/operations.ts b/framework/commerce/api/operations.ts
index 2910a2d82..342d6bbc9 100644
--- a/framework/commerce/api/operations.ts
+++ b/framework/commerce/api/operations.ts
@@ -1,3 +1,4 @@
+import { GetAllFacetsOperation } from './../types/facet';
import type { ServerResponse } from 'http'
import type { LoginOperation } from '../types/login'
import type { GetAllPagesOperation, GetPageOperation } from '../types/page'
@@ -23,6 +24,8 @@ export const OPERATIONS = [
'getAllProductPaths',
'getAllProducts',
'getProduct',
+ 'getAllFacets',
+
] as const
export const defaultOperations = OPERATIONS.reduce((ops, k) => {
@@ -154,8 +157,27 @@ export type Operations
= {
} & OperationOptions
): Promise
}
+
+ getAllFacets: {
+ (opts: {
+ variables?: T['variables']
+ config?: P['config']
+ preview?: boolean
+ }): Promise
+
+ (
+ opts: {
+ variables?: T['variables']
+ config?: P['config']
+ preview?: boolean
+ } & OperationOptions
+ ): Promise
+ }
+
+
}
+
export type APIOperations = {
[K in keyof Operations
]?: (ctx: OperationContext
) => Operations
[K]
}
diff --git a/framework/commerce/new-provider.md b/framework/commerce/new-provider.md
index 8c2feeab2..a48e6961b 100644
--- a/framework/commerce/new-provider.md
+++ b/framework/commerce/new-provider.md
@@ -15,6 +15,7 @@ Adding a commerce provider means adding a new folder in `framework` with a folde
- useSearch
- getProduct
- getAllProducts
+ - getAllFacets
- `wishlist`
- useWishlist
- useAddItem
diff --git a/framework/commerce/types/facet.ts b/framework/commerce/types/facet.ts
new file mode 100644
index 000000000..adfe1e061
--- /dev/null
+++ b/framework/commerce/types/facet.ts
@@ -0,0 +1,52 @@
+import { FacetValue } from './../../vendure/schema.d';
+
+export type Facet = {
+ id: string
+ name: string
+ code: string
+ values: FacetValue[]
+}
+
+export type SearchFacetsBody = {
+ search?: string
+ sort?: string
+ locale?: string
+}
+
+export type FacetTypes = {
+ facet: Facet
+ searchBody: SearchFacetsBody
+}
+
+export type SearchFacetsHook = {
+ data: {
+ facets: T['facet'][]
+ found: boolean
+ }
+ body: T['searchBody']
+ input: T['searchBody']
+ fetcherInput: T['searchBody']
+}
+
+export type FacetsSchema = {
+ endpoint: {
+ options: {}
+ handlers: {
+ getFacets: SearchFacetsHook
+ }
+ }
+}
+
+
+export type GetAllFacetsOperation = {
+ data: { facets: T['facet'][] }
+ variables: {
+ ids?: string[]
+ first?: number
+ }
+}
+
+export type GetFacetOperation = {
+ data: { facet?: T['facet'] }
+ variables: { code: string; } | { code?: never; }
+}
diff --git a/framework/commerce/types/product.ts b/framework/commerce/types/product.ts
index 6a68d8ad1..fec48a63d 100644
--- a/framework/commerce/types/product.ts
+++ b/framework/commerce/types/product.ts
@@ -1,3 +1,5 @@
+import { FacetValueFilterInput, LogicalOperator, SearchResultSortParameter } from "@framework/schema"
+
export type ProductImage = {
url: string
alt?: string
@@ -40,7 +42,6 @@ export type Product = {
slug?: string
path?: string
images: ProductImage[]
- variants: ProductVariant[]
price: ProductPrice
options: ProductOption[]
}
@@ -79,17 +80,24 @@ export type ProductsSchema = {
export type GetAllProductPathsOperation<
T extends ProductTypes = ProductTypes
-> = {
- data: { products: Pick[] }
- variables: { first?: number }
-}
+ > = {
+ data: { products: Pick[] }
+ variables: { first?: number }
+ }
export type GetAllProductsOperation = {
data: { products: T['product'][] }
variables: {
- relevance?: 'featured' | 'best_selling' | 'newest'
- ids?: string[]
- first?: number
+ term?: String
+ facetValueIds?: string[]
+ facetValueOperator?: LogicalOperator
+ facetValueFilters?: FacetValueFilterInput[]
+ collectionId?: string
+ collectionSlug?: string
+ groupByProduct?: Boolean
+ take?: number
+ skip?: number
+ sort?: SearchResultSortParameter
}
}
diff --git a/framework/vendure/api/index.ts b/framework/vendure/api/index.ts
index 6762ee6aa..7e49d6c1f 100644
--- a/framework/vendure/api/index.ts
+++ b/framework/vendure/api/index.ts
@@ -1,15 +1,16 @@
-import type { APIProvider, CommerceAPIConfig } from '@commerce/api'
+import type { CommerceAPIConfig } from '@commerce/api'
import { CommerceAPI, getCommerceApi as commerceApi } from '@commerce/api'
-import fetchGraphqlApi from './utils/fetch-graphql-api'
-
-import login from './operations/login'
+import getAllFacets from './operations/get-all-facets'
import getAllPages from './operations/get-all-pages'
-import getPage from './operations/get-page'
-import getSiteInfo from './operations/get-site-info'
-import getCustomerWishlist from './operations/get-customer-wishlist'
import getAllProductPaths from './operations/get-all-product-paths'
import getAllProducts from './operations/get-all-products'
+import getCustomerWishlist from './operations/get-customer-wishlist'
+import getPage from './operations/get-page'
import getProduct from './operations/get-product'
+import getSiteInfo from './operations/get-site-info'
+import login from './operations/login'
+import fetchGraphqlApi from './utils/fetch-graphql-api'
+
export interface VendureConfig extends CommerceAPIConfig {}
@@ -40,6 +41,7 @@ const operations = {
getAllProductPaths,
getAllProducts,
getProduct,
+ getAllFacets,
}
export const provider = { config, operations }
diff --git a/framework/vendure/api/operations/get-all-facets.ts b/framework/vendure/api/operations/get-all-facets.ts
new file mode 100644
index 000000000..c4b002744
--- /dev/null
+++ b/framework/vendure/api/operations/get-all-facets.ts
@@ -0,0 +1,45 @@
+import { OperationContext } from '@commerce/api/operations'
+import { Facet } from '@commerce/types/facet'
+import { Provider, VendureConfig } from '../'
+import { GetAllFacetsQuery } from '../../schema'
+import { getAllFacetsQuery } from '../../utils/queries/get-all-facets-query'
+
+export type FacetVariables = { first?: number }
+
+export default function getAllFacetsOperation({
+ commerce,
+}: OperationContext) {
+ async function getAllFacets(opts?: {
+ variables?: FacetVariables
+ config?: Partial
+ preview?: boolean
+ }): Promise<{ facets: Facet[] }>
+
+ async function getAllFacets({
+ query = getAllFacetsQuery,
+ variables: { ...vars } = {},
+ config: cfg,
+ }: {
+ query?: string
+ variables?: FacetVariables
+ config?: Partial
+ preview?: boolean
+ } = {}): Promise<{ facets: Facet[] | any[] }> {
+ const config = commerce.getConfig(cfg)
+ const variables = {
+ input: {
+ take: vars.first,
+ groupByFacet: true,
+ },
+ }
+ const { data } = await config.fetch(query, {
+ variables,
+ })
+
+ return {
+ facets: data.facets.items,
+ }
+ }
+
+ return getAllFacets
+}
diff --git a/framework/vendure/api/operations/get-all-products.ts b/framework/vendure/api/operations/get-all-products.ts
index 68d4ce9b7..1f558a7cb 100644
--- a/framework/vendure/api/operations/get-all-products.ts
+++ b/framework/vendure/api/operations/get-all-products.ts
@@ -5,7 +5,7 @@ import { normalizeSearchResult } from '../../utils/normalize'
import { getAllProductsQuery } from '../../utils/queries/get-all-products-query'
import { OperationContext } from '@commerce/api/operations'
-export type ProductVariables = { first?: number }
+export type ProductVariables = { first?: number, facetValueIds?: string[] }
export default function getAllProductsOperation({
commerce,
@@ -30,6 +30,7 @@ export default function getAllProductsOperation({
const variables = {
input: {
take: vars.first,
+ facetValueIds: vars.facetValueIds,
groupByProduct: true,
},
}
diff --git a/framework/vendure/schema.d.ts b/framework/vendure/schema.d.ts
index b0b0170d7..a817d6799 100644
--- a/framework/vendure/schema.d.ts
+++ b/framework/vendure/schema.d.ts
@@ -93,6 +93,10 @@ export type QueryProductsArgs = {
options?: Maybe
}
+export type QueryFacetsArgs = {
+ options?: Maybe
+}
+
export type QuerySearchArgs = {
input: SearchInput
}
@@ -2727,6 +2731,13 @@ export type ProductListOptions = {
filter?: Maybe
}
+export type FacetListOptions = {
+ skip?: Maybe
+ take?: Maybe
+ sort?: Maybe
+ filter?: Maybe
+}
+
export type UpdateOrderItemsResult =
| Order
| OrderModificationError
@@ -2884,6 +2895,23 @@ export type ProductVariantSortParameter = {
discountPrice?: Maybe
}
+
+export type FacetFilterParameter = {
+ createdAt?: Maybe
+ updatedAt?: Maybe
+ languageCode?: Maybe
+ name?: Maybe
+ code?: Maybe
+}
+
+export type FacetSortParameter = {
+ id?: Maybe
+ createdAt?: Maybe
+ updatedAt?: Maybe
+ name?: Maybe
+ code?: Maybe
+}
+
export type CustomerFilterParameter = {
createdAt?: Maybe
updatedAt?: Maybe
@@ -3008,7 +3036,9 @@ export type CartFragment = { __typename?: 'Order' } & Pick<
export type SearchResultFragment = { __typename?: 'SearchResult' } & Pick<
SearchResult,
- 'productId' | 'productName' | 'description' | 'slug' | 'sku' | 'currencyCode'
+ 'productId' | 'sku' | 'productName' | 'description' | 'slug' | 'sku' | 'currencyCode'
+ | 'productAsset' | 'price' | 'priceWithTax' | 'currencyCode'
+ | 'collectionIds'
> & {
productAsset?: Maybe<
{ __typename?: 'SearchResultAsset' } & Pick<
@@ -3192,6 +3222,23 @@ export type GetAllProductsQuery = { __typename?: 'Query' } & {
}
}
+export type GetAllFacetsQuery = { __typename?: 'Query' } & {
+ facets: { __typename?: 'FacetList' } & {
+ items: Array<
+ { __typename?: 'Facet' } & Pick<
+ Facet,
+ 'id' | 'name' | 'code'
+ > & {
+ parent?: Maybe<{ __typename?: 'Facet' } & Pick>
+ children?: Maybe<
+ Array<{ __typename?: 'Facet' } & Pick>
+ >
+ }
+ >,
+ 'totalItems'
+ }
+}
+
export type ActiveOrderQueryVariables = Exact<{ [key: string]: never }>
export type ActiveOrderQuery = { __typename?: 'Query' } & {
diff --git a/framework/vendure/utils/normalize.ts b/framework/vendure/utils/normalize.ts
index 09c1c6e42..b99aa5096 100644
--- a/framework/vendure/utils/normalize.ts
+++ b/framework/vendure/utils/normalize.ts
@@ -3,18 +3,20 @@ import { Cart } from '@commerce/types/cart'
import { CartFragment, SearchResultFragment } from '../schema'
export function normalizeSearchResult(item: SearchResultFragment): Product {
+ const imageUrl = item.productAsset?.preview ? item.productAsset?.preview + '?w=800&mode=crop' : ''
return {
id: item.productId,
name: item.productName,
description: item.description,
slug: item.slug,
path: item.slug,
- images: [{ url: item.productAsset?.preview + '?w=800&mode=crop' || '' }],
- variants: [],
+ images: imageUrl ? [{ url: imageUrl }] : [],
price: {
+ // TODO: check price
value: (item.priceWithTax as any).min / 100,
currencyCode: item.currencyCode,
},
+ // TODO: check product option
options: [],
sku: item.sku,
}
diff --git a/framework/vendure/utils/queries/get-all-facets-query.ts b/framework/vendure/utils/queries/get-all-facets-query.ts
new file mode 100644
index 000000000..906507c69
--- /dev/null
+++ b/framework/vendure/utils/queries/get-all-facets-query.ts
@@ -0,0 +1,17 @@
+export const getAllFacetsQuery = /* GraphQL */ `
+query facets ($options: FacetListOptions) {
+ facets (options: $options){
+ totalItems,
+ items {
+ id
+ name
+ code
+ values {
+ id
+ name
+ code
+ }
+ }
+ }
+}
+`
diff --git a/pages/index.tsx b/pages/index.tsx
index 3fa86079d..3082d1790 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -1,8 +1,20 @@
+import { ProductVariables } from '@framework/api/operations/get-all-products';
+import { Product } from '@framework/schema';
+import commerce from '@lib/api/commerce';
+import { GetStaticPropsContext } from 'next';
import { Layout } from 'src/components/common';
import { FeaturedProductsCarousel, HomeBanner, HomeCategories, HomeCollection, HomeCTA, HomeFeature, HomeRecipe, HomeSubscribe, HomeVideo } from 'src/components/modules/home';
import HomeSpice from 'src/components/modules/home/HomeSpice/HomeSpice';
+import { getAllFeaturedFacetId, getFreshProductFacetId } from 'src/utils/funtion.utils';
-export default function Home() {
+interface Props {
+ freshProducts: Product[],
+ featuredProducts: Product[],
+
+}
+export default function Home({ freshProducts, featuredProducts }: Props) {
+ console.log("total: ", freshProducts.length, featuredProducts.length)
+ console.log("rs: ", freshProducts, featuredProducts)
return (
<>
@@ -10,10 +22,10 @@ export default function Home() {
-
-
+
+
-
+
{/* // todo: uncomment
@@ -22,4 +34,59 @@ export default function Home() {
)
}
+
+export async function getStaticProps({
+ preview,
+ locale,
+ locales,
+}: GetStaticPropsContext) {
+ const config = { locale, locales }
+ const { facets } = await commerce.getAllFacets({
+ variables: {},
+ config,
+ preview,
+ })
+
+
+ const freshProductvariables: ProductVariables = {}
+ const freshFacetId = getFreshProductFacetId(facets)
+
+ if (freshFacetId) {
+ freshProductvariables.facetValueIds = [freshFacetId]
+ }
+ const freshProductsPromise = commerce.getAllProducts({
+ variables: freshProductvariables,
+ config,
+ preview,
+ })
+
+ const allFeaturedFacetId = getAllFeaturedFacetId(facets)
+ const featuredProductsPromise = commerce.getAllProducts({
+ variables: {
+ facetValueIds: allFeaturedFacetId
+ },
+ config,
+ preview,
+ })
+
+
+ try {
+ const rs = await Promise.all([freshProductsPromise, featuredProductsPromise])
+
+ return {
+ props: {
+ freshProducts: rs[0].products,
+ featuredProducts: rs[1].products
+ },
+ revalidate: 60,
+ }
+ } catch (err) {
+
+ }
+
+
+
+}
+
+
Home.Layout = Layout
diff --git a/pages/product/[slug].tsx b/pages/product/[slug].tsx
index ab9a1c17c..a8e925df9 100644
--- a/pages/product/[slug].tsx
+++ b/pages/product/[slug].tsx
@@ -4,6 +4,7 @@ import { ProductInfoDetail, ReleventProducts, ViewedProducts } from 'src/compone
import { BLOGS_DATA_TEST, INGREDIENT_DATA_TEST, RECIPE_DATA_TEST } from 'src/utils/demo-data'
export default function Slug() {
+
return <>
diff --git a/pages/test.tsx b/pages/test.tsx
index b60fe63c7..6244c3dd6 100644
--- a/pages/test.tsx
+++ b/pages/test.tsx
@@ -1,18 +1,51 @@
-import { Layout } from 'src/components/common'
-import { useMessage } from 'src/components/contexts'
-
-export default function Test() {
- const { showMessageError } = useMessage()
-
- const handleClick = () => {
- showMessageError("Create account successfully")
- }
+import commerce from '@lib/api/commerce';
+import { GetStaticPropsContext } from 'next';
+import { Layout } from 'src/components/common';
+interface Props {
+ products: any
+}
+export default function Home({ products }: Props) {
return (
<>
-
+
+ TOTAL: {products?.length}
+
+ {JSON.stringify(products[0])}
>
)
}
-Test.Layout = Layout
+
+export async function getServerSideProps({
+ preview,
+ locale,
+ locales,
+}: GetStaticPropsContext) {
+ const config = { locale, locales }
+ const productsPromise = commerce.getAllProducts({
+ // const productsPromise = commerce.getAllFacets({
+ variables: {
+ first: 70,
+ // filter: {
+ // name: {
+ // contains: 'ca'
+ // }
+ // }
+ },
+ config,
+ preview,
+ // Saleor provider only
+ ...({ featured: true } as any),
+ })
+
+ const { products } = await productsPromise
+
+
+ return {
+ props: { products },
+ }
+}
+
+
+Home.Layout = Layout
diff --git a/src/components/hooks/facets/index.ts b/src/components/hooks/facets/index.ts
new file mode 100644
index 000000000..f039373e3
--- /dev/null
+++ b/src/components/hooks/facets/index.ts
@@ -0,0 +1,3 @@
+export { default as useFacets } from './useFacets'
+
+
diff --git a/src/components/hooks/facets/useFacets.tsx b/src/components/hooks/facets/useFacets.tsx
new file mode 100644
index 000000000..c9a4e85ab
--- /dev/null
+++ b/src/components/hooks/facets/useFacets.tsx
@@ -0,0 +1,11 @@
+import { GetAllFacetsQuery, QueryFacetsArgs } from '@framework/schema'
+import { getAllFacetsQuery } from '@framework/utils/queries/get-all-facets-query'
+import gglFetcher from 'src/utils/gglFetcher'
+import useSWR from 'swr'
+
+const useFacets = (options?: QueryFacetsArgs) => {
+ const { data, isValidating, ...rest } = useSWR([getAllFacetsQuery, options], gglFetcher)
+ return { items: data?.facets.items, totalItems: data?.facets.totalItems, loading: isValidating, ...rest }
+}
+
+export default useFacets
diff --git a/src/components/modules/home/FreshProducts/FreshProducts.tsx b/src/components/modules/home/FreshProducts/FreshProducts.tsx
new file mode 100644
index 000000000..b06138079
--- /dev/null
+++ b/src/components/modules/home/FreshProducts/FreshProducts.tsx
@@ -0,0 +1,148 @@
+import { Product } from '@framework/schema'
+import React from 'react'
+import { CollectionCarcousel } from '..'
+import image5 from '../../../../../public/assets/images/image5.png'
+import image6 from '../../../../../public/assets/images/image6.png'
+import image7 from '../../../../../public/assets/images/image7.png'
+import image8 from '../../../../../public/assets/images/image8.png'
+interface FreshProductsProps {
+ data: Product[]
+}
+const dataTest = [
+ {
+ name: 'Tomato',
+ weight: '250g',
+ category: 'VEGGIE',
+ price: 'Rp 27.500',
+ imageSrc: image5.src,
+ },
+ {
+ name: 'Cucumber',
+ weight: '250g',
+ category: 'VEGGIE',
+ price: 'Rp 27.500',
+ imageSrc: image6.src,
+ },
+ {
+ name: 'Carrot',
+ weight: '250g',
+ category: 'VEGGIE',
+ price: 'Rp 27.500',
+ imageSrc: image7.src,
+ },
+ {
+ name: 'Salad',
+ weight: '250g',
+ category: 'VEGGIE',
+ price: 'Rp 27.500',
+ imageSrc: image8.src,
+ },
+ {
+ name: 'Tomato',
+ weight: '250g',
+ category: 'VEGGIE',
+ price: 'Rp 27.500',
+ imageSrc: image5.src,
+ },
+ {
+ name: 'Cucumber',
+ weight: '250g',
+ category: 'VEGGIE',
+ price: 'Rp 27.500',
+ imageSrc: image6.src,
+ },
+ {
+ name: 'Tomato',
+ weight: '250g',
+ category: 'VEGGIE',
+ price: 'Rp 27.500',
+ imageSrc: image5.src,
+ },
+ {
+ name: 'Cucumber',
+ weight: '250g',
+ category: 'VEGGIE',
+ price: 'Rp 27.500',
+ imageSrc: image6.src,
+ },
+ {
+ name: 'Carrot',
+ weight: '250g',
+ category: 'VEGGIE',
+ price: 'Rp 27.500',
+ imageSrc: image7.src,
+ },
+ {
+ name: 'Salad',
+ weight: '250g',
+ category: 'VEGGIE',
+ price: 'Rp 27.500',
+ imageSrc: image8.src,
+ },
+ {
+ name: 'Tomato',
+ weight: '250g',
+ category: 'VEGGIE',
+ price: 'Rp 27.500',
+ imageSrc: image5.src,
+ },
+ {
+ name: 'Cucumber',
+ weight: '250g',
+ category: 'VEGGIE',
+ price: 'Rp 27.500',
+ imageSrc: image6.src,
+ },
+]
+
+const FreshProducts = ({data}: FreshProductsProps) => {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+export default FreshProducts
diff --git a/src/components/modules/home/index.ts b/src/components/modules/home/index.ts
index 82a0e3d8a..0618812c5 100644
--- a/src/components/modules/home/index.ts
+++ b/src/components/modules/home/index.ts
@@ -4,6 +4,7 @@ export { default as HomeCategories } from './HomeCategories/HomeCategories'
export { default as HomeCTA } from './HomeCTA/HomeCTA'
export { default as HomeSubscribe } from './HomeSubscribe/HomeSubscribe'
export { default as HomeVideo } from './HomeVideo/HomeVideo'
+export { default as FreshProducts } from './FreshProducts/FreshProducts'
export { default as HomeCollection } from './HomeCollection/HomeCollection'
export { default as HomeRecipe } from './HomeRecipe/HomeRecipe'
export { default as FeaturedProductsCarousel } from './FeaturedProductsCarousel/FeaturedProductsCarousel'
diff --git a/src/utils/constanst.utils.ts b/src/utils/constanst.utils.ts
index f77991604..f66d6a7ea 100644
--- a/src/utils/constanst.utils.ts
+++ b/src/utils/constanst.utils.ts
@@ -107,7 +107,15 @@ export const CATEGORY = [
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=chinsu`,
},
]
-
+
+export const FACET = {
+ FEATURE: {
+ PARENT_NAME: 'Featured',
+ FRESH: 'Fresh',
+ BEST_SELLERS: 'Best seller'
+ }
+}
+
export const FEATURED = [
{
name: 'Best Sellers',
@@ -141,3 +149,4 @@ export const STATE_OPTIONS = [
value: 'Hà Nội',
},
]
+
diff --git a/src/utils/funtion.utils.ts b/src/utils/funtion.utils.ts
index 43d517589..d2ad83be1 100644
--- a/src/utils/funtion.utils.ts
+++ b/src/utils/funtion.utils.ts
@@ -1,11 +1,29 @@
+import { FacetValue } from './../../framework/vendure/schema.d';
+import { Facet } from "@commerce/types/facet";
+import { FACET } from "./constanst.utils";
+
export function isMobile() {
- return window.innerWidth < 768
+ return window.innerWidth < 768
}
-export function removeItem(arr: Array, value: T): Array {
- const index = arr.indexOf(value);
- if (index > -1) {
- arr.splice(index, 1);
- }
- return [...arr];
+export function removeItem(arr: Array, value: T): Array {
+ const index = arr.indexOf(value);
+ if (index > -1) {
+ arr.splice(index, 1);
+ }
+ return [...arr];
+}
+
+export function getFreshProductFacetId(facets: Facet[]) {
+ const featuredFacet = facets.find((item: Facet) => item.name === FACET.FEATURE.PARENT_NAME)
+ const freshFacetValue = featuredFacet?.values.find((item: FacetValue) => item.name === FACET.FEATURE.FRESH)
+
+ return freshFacetValue?.id
+}
+
+export function getAllFeaturedFacetId(facets: Facet[]) {
+ const featuredFacet = facets.find((item: Facet) => item.name === FACET.FEATURE.PARENT_NAME)
+ const rs = featuredFacet?.values.map((item: FacetValue) => item.id)
+
+ return rs
}
\ No newline at end of file