mirror of
https://github.com/vercel/commerce.git
synced 2025-07-23 04:36:49 +00:00
Merge pull request #74 from KieIO/feature/m2-featured-product
Get featured product, fresh product
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import { GetAllFacetsOperation } from './../types/facet';
|
||||||
import type { ServerResponse } from 'http'
|
import type { ServerResponse } from 'http'
|
||||||
import type { LoginOperation } from '../types/login'
|
import type { LoginOperation } from '../types/login'
|
||||||
import type { GetAllPagesOperation, GetPageOperation } from '../types/page'
|
import type { GetAllPagesOperation, GetPageOperation } from '../types/page'
|
||||||
@@ -23,6 +24,8 @@ export const OPERATIONS = [
|
|||||||
'getAllProductPaths',
|
'getAllProductPaths',
|
||||||
'getAllProducts',
|
'getAllProducts',
|
||||||
'getProduct',
|
'getProduct',
|
||||||
|
'getAllFacets',
|
||||||
|
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
export const defaultOperations = OPERATIONS.reduce((ops, k) => {
|
export const defaultOperations = OPERATIONS.reduce((ops, k) => {
|
||||||
@@ -154,8 +157,27 @@ export type Operations<P extends APIProvider> = {
|
|||||||
} & OperationOptions
|
} & OperationOptions
|
||||||
): Promise<T['data']>
|
): Promise<T['data']>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAllFacets: {
|
||||||
|
<T extends GetAllFacetsOperation>(opts: {
|
||||||
|
variables?: T['variables']
|
||||||
|
config?: P['config']
|
||||||
|
preview?: boolean
|
||||||
|
}): Promise<T['data']>
|
||||||
|
|
||||||
|
<T extends GetAllFacetsOperation>(
|
||||||
|
opts: {
|
||||||
|
variables?: T['variables']
|
||||||
|
config?: P['config']
|
||||||
|
preview?: boolean
|
||||||
|
} & OperationOptions
|
||||||
|
): Promise<T['data']>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export type APIOperations<P extends APIProvider> = {
|
export type APIOperations<P extends APIProvider> = {
|
||||||
[K in keyof Operations<P>]?: (ctx: OperationContext<P>) => Operations<P>[K]
|
[K in keyof Operations<P>]?: (ctx: OperationContext<P>) => Operations<P>[K]
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,7 @@ Adding a commerce provider means adding a new folder in `framework` with a folde
|
|||||||
- useSearch
|
- useSearch
|
||||||
- getProduct
|
- getProduct
|
||||||
- getAllProducts
|
- getAllProducts
|
||||||
|
- getAllFacets
|
||||||
- `wishlist`
|
- `wishlist`
|
||||||
- useWishlist
|
- useWishlist
|
||||||
- useAddItem
|
- useAddItem
|
||||||
|
52
framework/commerce/types/facet.ts
Normal file
52
framework/commerce/types/facet.ts
Normal file
@@ -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<T extends FacetTypes = FacetTypes> = {
|
||||||
|
data: {
|
||||||
|
facets: T['facet'][]
|
||||||
|
found: boolean
|
||||||
|
}
|
||||||
|
body: T['searchBody']
|
||||||
|
input: T['searchBody']
|
||||||
|
fetcherInput: T['searchBody']
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FacetsSchema<T extends FacetTypes = FacetTypes> = {
|
||||||
|
endpoint: {
|
||||||
|
options: {}
|
||||||
|
handlers: {
|
||||||
|
getFacets: SearchFacetsHook<T>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type GetAllFacetsOperation<T extends FacetTypes = FacetTypes> = {
|
||||||
|
data: { facets: T['facet'][] }
|
||||||
|
variables: {
|
||||||
|
ids?: string[]
|
||||||
|
first?: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GetFacetOperation<T extends FacetTypes = FacetTypes> = {
|
||||||
|
data: { facet?: T['facet'] }
|
||||||
|
variables: { code: string; } | { code?: never; }
|
||||||
|
}
|
@@ -1,3 +1,6 @@
|
|||||||
|
import { CurrencyCode, FacetValue } from './../../vendure/schema.d';
|
||||||
|
import { FacetValueFilterInput, LogicalOperator, SearchResultSortParameter } from "@framework/schema"
|
||||||
|
|
||||||
export type ProductImage = {
|
export type ProductImage = {
|
||||||
url: string
|
url: string
|
||||||
alt?: string
|
alt?: string
|
||||||
@@ -40,11 +43,27 @@ export type Product = {
|
|||||||
slug?: string
|
slug?: string
|
||||||
path?: string
|
path?: string
|
||||||
images: ProductImage[]
|
images: ProductImage[]
|
||||||
variants: ProductVariant[]
|
|
||||||
price: ProductPrice
|
price: ProductPrice
|
||||||
options: ProductOption[]
|
options: ProductOption[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ProductCard = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
slug?: string
|
||||||
|
imageSrc: string
|
||||||
|
price: number
|
||||||
|
currencyCode: CurrencyCode
|
||||||
|
oldPrice?: number
|
||||||
|
discount?: number
|
||||||
|
weight?: number
|
||||||
|
facetValueIds?: string[],
|
||||||
|
collectionIds?: string[],
|
||||||
|
// TODO: collection
|
||||||
|
category?: string,
|
||||||
|
isNotSell?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export type SearchProductsBody = {
|
export type SearchProductsBody = {
|
||||||
search?: string
|
search?: string
|
||||||
categoryId?: string | number
|
categoryId?: string | number
|
||||||
@@ -79,17 +98,24 @@ export type ProductsSchema<T extends ProductTypes = ProductTypes> = {
|
|||||||
|
|
||||||
export type GetAllProductPathsOperation<
|
export type GetAllProductPathsOperation<
|
||||||
T extends ProductTypes = ProductTypes
|
T extends ProductTypes = ProductTypes
|
||||||
> = {
|
> = {
|
||||||
data: { products: Pick<T['product'], 'path'>[] }
|
data: { products: Pick<T['product'], 'path'>[] }
|
||||||
variables: { first?: number }
|
variables: { first?: number }
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GetAllProductsOperation<T extends ProductTypes = ProductTypes> = {
|
export type GetAllProductsOperation<T extends ProductTypes = ProductTypes> = {
|
||||||
data: { products: T['product'][] }
|
data: { products: T['product'][] }
|
||||||
variables: {
|
variables: {
|
||||||
relevance?: 'featured' | 'best_selling' | 'newest'
|
term?: String
|
||||||
ids?: string[]
|
facetValueIds?: string[]
|
||||||
first?: number
|
facetValueOperator?: LogicalOperator
|
||||||
|
facetValueFilters?: FacetValueFilterInput[]
|
||||||
|
collectionId?: string
|
||||||
|
collectionSlug?: string
|
||||||
|
groupByProduct?: Boolean
|
||||||
|
take?: number
|
||||||
|
skip?: number
|
||||||
|
sort?: SearchResultSortParameter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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 { CommerceAPI, getCommerceApi as commerceApi } from '@commerce/api'
|
||||||
import fetchGraphqlApi from './utils/fetch-graphql-api'
|
import getAllFacets from './operations/get-all-facets'
|
||||||
|
|
||||||
import login from './operations/login'
|
|
||||||
import getAllPages from './operations/get-all-pages'
|
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 getAllProductPaths from './operations/get-all-product-paths'
|
||||||
import getAllProducts from './operations/get-all-products'
|
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 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 {}
|
export interface VendureConfig extends CommerceAPIConfig {}
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ const operations = {
|
|||||||
getAllProductPaths,
|
getAllProductPaths,
|
||||||
getAllProducts,
|
getAllProducts,
|
||||||
getProduct,
|
getProduct,
|
||||||
|
getAllFacets,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const provider = { config, operations }
|
export const provider = { config, operations }
|
||||||
|
45
framework/vendure/api/operations/get-all-facets.ts
Normal file
45
framework/vendure/api/operations/get-all-facets.ts
Normal file
@@ -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<Provider>) {
|
||||||
|
async function getAllFacets(opts?: {
|
||||||
|
variables?: FacetVariables
|
||||||
|
config?: Partial<VendureConfig>
|
||||||
|
preview?: boolean
|
||||||
|
}): Promise<{ facets: Facet[] }>
|
||||||
|
|
||||||
|
async function getAllFacets({
|
||||||
|
query = getAllFacetsQuery,
|
||||||
|
variables: { ...vars } = {},
|
||||||
|
config: cfg,
|
||||||
|
}: {
|
||||||
|
query?: string
|
||||||
|
variables?: FacetVariables
|
||||||
|
config?: Partial<VendureConfig>
|
||||||
|
preview?: boolean
|
||||||
|
} = {}): Promise<{ facets: Facet[] | any[] }> {
|
||||||
|
const config = commerce.getConfig(cfg)
|
||||||
|
const variables = {
|
||||||
|
input: {
|
||||||
|
take: vars.first,
|
||||||
|
groupByFacet: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const { data } = await config.fetch<GetAllFacetsQuery>(query, {
|
||||||
|
variables,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
facets: data.facets.items,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getAllFacets
|
||||||
|
}
|
@@ -5,7 +5,7 @@ import { normalizeSearchResult } from '../../utils/normalize'
|
|||||||
import { getAllProductsQuery } from '../../utils/queries/get-all-products-query'
|
import { getAllProductsQuery } from '../../utils/queries/get-all-products-query'
|
||||||
import { OperationContext } from '@commerce/api/operations'
|
import { OperationContext } from '@commerce/api/operations'
|
||||||
|
|
||||||
export type ProductVariables = { first?: number }
|
export type ProductVariables = { first?: number, facetValueIds?: string[] }
|
||||||
|
|
||||||
export default function getAllProductsOperation({
|
export default function getAllProductsOperation({
|
||||||
commerce,
|
commerce,
|
||||||
@@ -30,6 +30,7 @@ export default function getAllProductsOperation({
|
|||||||
const variables = {
|
const variables = {
|
||||||
input: {
|
input: {
|
||||||
take: vars.first,
|
take: vars.first,
|
||||||
|
facetValueIds: vars.facetValueIds,
|
||||||
groupByProduct: true,
|
groupByProduct: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
50
framework/vendure/schema.d.ts
vendored
50
framework/vendure/schema.d.ts
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
import { FacetValue } from './schema.d';
|
||||||
export type Maybe<T> = T | null
|
export type Maybe<T> = T | null
|
||||||
export type Exact<T extends { [key: string]: unknown }> = {
|
export type Exact<T extends { [key: string]: unknown }> = {
|
||||||
[K in keyof T]: T[K]
|
[K in keyof T]: T[K]
|
||||||
@@ -93,6 +94,10 @@ export type QueryProductsArgs = {
|
|||||||
options?: Maybe<ProductListOptions>
|
options?: Maybe<ProductListOptions>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type QueryFacetsArgs = {
|
||||||
|
options?: Maybe<FacetListOptions>
|
||||||
|
}
|
||||||
|
|
||||||
export type QuerySearchArgs = {
|
export type QuerySearchArgs = {
|
||||||
input: SearchInput
|
input: SearchInput
|
||||||
}
|
}
|
||||||
@@ -2727,6 +2732,13 @@ export type ProductListOptions = {
|
|||||||
filter?: Maybe<ProductFilterParameter>
|
filter?: Maybe<ProductFilterParameter>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type FacetListOptions = {
|
||||||
|
skip?: Maybe<Scalars['Int']>
|
||||||
|
take?: Maybe<Scalars['Int']>
|
||||||
|
sort?: Maybe<FacetSortParameter>
|
||||||
|
filter?: Maybe<FacetFilterParameter>
|
||||||
|
}
|
||||||
|
|
||||||
export type UpdateOrderItemsResult =
|
export type UpdateOrderItemsResult =
|
||||||
| Order
|
| Order
|
||||||
| OrderModificationError
|
| OrderModificationError
|
||||||
@@ -2884,6 +2896,23 @@ export type ProductVariantSortParameter = {
|
|||||||
discountPrice?: Maybe<SortOrder>
|
discountPrice?: Maybe<SortOrder>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type FacetFilterParameter = {
|
||||||
|
createdAt?: Maybe<DateOperators>
|
||||||
|
updatedAt?: Maybe<DateOperators>
|
||||||
|
languageCode?: Maybe<StringOperators>
|
||||||
|
name?: Maybe<StringOperators>
|
||||||
|
code?: Maybe<StringOperators>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FacetSortParameter = {
|
||||||
|
id?: Maybe<SortOrder>
|
||||||
|
createdAt?: Maybe<SortOrder>
|
||||||
|
updatedAt?: Maybe<SortOrder>
|
||||||
|
name?: Maybe<SortOrder>
|
||||||
|
code?: Maybe<SortOrder>
|
||||||
|
}
|
||||||
|
|
||||||
export type CustomerFilterParameter = {
|
export type CustomerFilterParameter = {
|
||||||
createdAt?: Maybe<DateOperators>
|
createdAt?: Maybe<DateOperators>
|
||||||
updatedAt?: Maybe<DateOperators>
|
updatedAt?: Maybe<DateOperators>
|
||||||
@@ -3008,7 +3037,9 @@ export type CartFragment = { __typename?: 'Order' } & Pick<
|
|||||||
|
|
||||||
export type SearchResultFragment = { __typename?: 'SearchResult' } & Pick<
|
export type SearchResultFragment = { __typename?: 'SearchResult' } & Pick<
|
||||||
SearchResult,
|
SearchResult,
|
||||||
'productId' | 'productName' | 'description' | 'slug' | 'sku' | 'currencyCode'
|
'productId' | 'sku' | 'productName' | 'description' | 'slug' | 'sku' | 'currencyCode'
|
||||||
|
| 'productAsset' | 'price' | 'priceWithTax' | 'currencyCode'
|
||||||
|
| 'collectionIds' | 'facetValueIds' | 'collectionIds'
|
||||||
> & {
|
> & {
|
||||||
productAsset?: Maybe<
|
productAsset?: Maybe<
|
||||||
{ __typename?: 'SearchResultAsset' } & Pick<
|
{ __typename?: 'SearchResultAsset' } & Pick<
|
||||||
@@ -3192,6 +3223,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<Facet, 'id'>>
|
||||||
|
children?: Maybe<
|
||||||
|
Array<{ __typename?: 'Facet' } & Pick<Facet, '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' } & {
|
||||||
|
@@ -19,6 +19,8 @@ export const searchResultFragment = /* GraphQL */ `
|
|||||||
min
|
min
|
||||||
max
|
max
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
facetValueIds,
|
||||||
|
collectionIds,
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
@@ -1,22 +1,24 @@
|
|||||||
import { Product } from '@commerce/types/product'
|
|
||||||
import { Cart } from '@commerce/types/cart'
|
import { Cart } from '@commerce/types/cart'
|
||||||
|
import { ProductCard } from '@commerce/types/product'
|
||||||
import { CartFragment, SearchResultFragment } from '../schema'
|
import { CartFragment, SearchResultFragment } from '../schema'
|
||||||
|
|
||||||
export function normalizeSearchResult(item: SearchResultFragment): Product {
|
export function normalizeSearchResult(item: SearchResultFragment): ProductCard {
|
||||||
return {
|
return {
|
||||||
id: item.productId,
|
id: item.productId,
|
||||||
name: item.productName,
|
name: item.productName,
|
||||||
description: item.description,
|
|
||||||
slug: item.slug,
|
slug: item.slug,
|
||||||
path: item.slug,
|
imageSrc: item.productAsset?.preview ? item.productAsset?.preview + '?w=800&mode=crop' : '',
|
||||||
images: [{ url: item.productAsset?.preview + '?w=800&mode=crop' || '' }],
|
price: (item.priceWithTax as any).min / 100,
|
||||||
variants: [],
|
currencyCode: item.currencyCode,
|
||||||
price: {
|
facetValueIds: item.facetValueIds,
|
||||||
value: (item.priceWithTax as any).min / 100,
|
collectionIds: item.collectionIds,
|
||||||
currencyCode: item.currencyCode,
|
|
||||||
},
|
// TODO:
|
||||||
options: [],
|
// oldPrice: item.price
|
||||||
sku: item.sku,
|
// discount
|
||||||
|
// isNotSell
|
||||||
|
// weight
|
||||||
|
// category
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
17
framework/vendure/utils/queries/get-all-facets-query.ts
Normal file
17
framework/vendure/utils/queries/get-all-facets-query.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
export const getAllFacetsQuery = /* GraphQL */ `
|
||||||
|
query facets ($options: FacetListOptions) {
|
||||||
|
facets (options: $options){
|
||||||
|
totalItems,
|
||||||
|
items {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
code
|
||||||
|
values {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
@@ -1,19 +1,34 @@
|
|||||||
|
import { ProductCard } from '@commerce/types/product';
|
||||||
|
import { ProductVariables } from '@framework/api/operations/get-all-products';
|
||||||
|
import { FacetValue } from '@framework/schema';
|
||||||
|
import commerce from '@lib/api/commerce';
|
||||||
|
import { GetStaticPropsContext } from 'next';
|
||||||
import { Layout } from 'src/components/common';
|
import { Layout } from 'src/components/common';
|
||||||
import { FeaturedProductsCarousel, 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 { getAllFacetValueIdsByParentCode, getAllFacetValuesForFeatuedProducts, getFreshFacetId } from 'src/utils/funtion.utils';
|
||||||
|
|
||||||
export default function Home() {
|
interface Props {
|
||||||
|
featuredAndDiscountFacetsValue: FacetValue[],
|
||||||
|
freshProducts: ProductCard[],
|
||||||
|
featuredProducts: ProductCard[],
|
||||||
|
|
||||||
|
}
|
||||||
|
export default function Home({ featuredAndDiscountFacetsValue,
|
||||||
|
freshProducts, featuredProducts }: Props) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HomeBanner />
|
<HomeBanner />
|
||||||
<HomeFeature />
|
<HomeFeature />
|
||||||
<HomeCategories />
|
<HomeCategories />
|
||||||
|
<FreshProducts data={freshProducts} />
|
||||||
<HomeCollection />
|
<HomeCollection />
|
||||||
<HomeVideo />
|
<HomeVideo />
|
||||||
<HomeSpice/>
|
<HomeSpice />
|
||||||
<FeaturedProductsCarousel/>
|
<FeaturedProductsCarousel data={featuredProducts} featuredFacetsValue={featuredAndDiscountFacetsValue} />
|
||||||
<HomeCTA />
|
<HomeCTA />
|
||||||
<HomeRecipe />
|
<HomeRecipe />
|
||||||
<HomeSubscribe />
|
<HomeSubscribe />
|
||||||
|
|
||||||
{/* // todo: uncomment
|
{/* // todo: uncomment
|
||||||
@@ -22,4 +37,67 @@ 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 featuredAndDiscountFacetsValue = getAllFacetValuesForFeatuedProducts(facets)
|
||||||
|
|
||||||
|
|
||||||
|
// fresh products
|
||||||
|
const freshProductvariables: ProductVariables = {}
|
||||||
|
const freshFacetId = getFreshFacetId(facets)
|
||||||
|
if (freshFacetId) {
|
||||||
|
freshProductvariables.facetValueIds = [freshFacetId]
|
||||||
|
}
|
||||||
|
const freshProductsPromise = commerce.getAllProducts({
|
||||||
|
variables: freshProductvariables,
|
||||||
|
config,
|
||||||
|
preview,
|
||||||
|
})
|
||||||
|
|
||||||
|
// featured products
|
||||||
|
const allFeaturedFacetIds = getAllFacetValueIdsByParentCode(facets, CODE_FACET_FEATURED)
|
||||||
|
const allDiscountFacetIds = getAllFacetValueIdsByParentCode(facets, CODE_FACET_DISCOUNT)
|
||||||
|
const facetValueIdsForFeaturedProducts = [...allFeaturedFacetIds, ...allDiscountFacetIds]
|
||||||
|
const featuredProductsPromise = commerce.getAllProducts({
|
||||||
|
variables: {
|
||||||
|
facetValueIds: facetValueIdsForFeaturedProducts
|
||||||
|
},
|
||||||
|
config,
|
||||||
|
preview,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
const rs = await Promise.all([
|
||||||
|
freshProductsPromise,
|
||||||
|
featuredProductsPromise,
|
||||||
|
])
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
featuredAndDiscountFacetsValue,
|
||||||
|
freshProducts: freshFacetId ? rs[0].products : [],
|
||||||
|
featuredProducts: facetValueIdsForFeaturedProducts.length > 0 ? rs[1].products : []
|
||||||
|
},
|
||||||
|
revalidate: 60,
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Home.Layout = Layout
|
Home.Layout = Layout
|
||||||
|
@@ -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'
|
import { BLOGS_DATA_TEST, INGREDIENT_DATA_TEST, RECIPE_DATA_TEST } from 'src/utils/demo-data'
|
||||||
|
|
||||||
export default function Slug() {
|
export default function Slug() {
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<ProductInfoDetail />
|
<ProductInfoDetail />
|
||||||
<RecipeDetail ingredients={INGREDIENT_DATA_TEST} />
|
<RecipeDetail ingredients={INGREDIENT_DATA_TEST} />
|
||||||
|
@@ -1,18 +1,51 @@
|
|||||||
import { Layout } from 'src/components/common'
|
import commerce from '@lib/api/commerce';
|
||||||
import { useMessage } from 'src/components/contexts'
|
import { GetStaticPropsContext } from 'next';
|
||||||
|
import { Layout } from 'src/components/common';
|
||||||
export default function Test() {
|
|
||||||
const { showMessageError } = useMessage()
|
|
||||||
|
|
||||||
const handleClick = () => {
|
|
||||||
showMessageError("Create account successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
products: any
|
||||||
|
}
|
||||||
|
export default function Home({ products }: Props) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button onClick={handleClick}>Click me</button>
|
<p>
|
||||||
|
TOTAL: {products?.length}
|
||||||
|
</p>
|
||||||
|
{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
|
||||||
|
BIN
public/assets/images/default_img.jpg
Normal file
BIN
public/assets/images/default_img.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
@@ -21,6 +21,9 @@
|
|||||||
width: 24rem;
|
width: 24rem;
|
||||||
height: 24rem;
|
height: 24rem;
|
||||||
}
|
}
|
||||||
|
img {
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.right{
|
.right{
|
||||||
margin-left: 1.2rem;
|
margin-left: 1.2rem;
|
||||||
|
@@ -1,39 +1,55 @@
|
|||||||
|
import { ProductCard } from '@commerce/types/product'
|
||||||
|
import { Facet, FacetValue } from '@framework/schema'
|
||||||
|
import Link from 'next/link'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { FeaturedProductProps } from 'src/utils/types.utils'
|
import { ROUTE } from 'src/utils/constanst.utils'
|
||||||
import s from './FeaturedProductCard.module.scss'
|
|
||||||
import { LANGUAGE } from '../../../utils/language.utils'
|
|
||||||
import ButtonIconBuy from '../ButtonIconBuy/ButtonIconBuy'
|
|
||||||
import ButtonCommon from '../ButtonCommon/ButtonCommon'
|
|
||||||
import { ImgWithLink } from '..'
|
import { ImgWithLink } from '..'
|
||||||
export interface FeaturedProductCardProps extends FeaturedProductProps {
|
import { LANGUAGE } from '../../../utils/language.utils'
|
||||||
buttonText?: string
|
import ButtonCommon from '../ButtonCommon/ButtonCommon'
|
||||||
|
import ButtonIconBuy from '../ButtonIconBuy/ButtonIconBuy'
|
||||||
|
import s from './FeaturedProductCard.module.scss'
|
||||||
|
export interface FeaturedProductCardProps extends ProductCard {
|
||||||
|
buttonText?: string,
|
||||||
|
subText?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
const FeaturedProductCard = ({
|
const FeaturedProductCard = ({
|
||||||
imageSrc,
|
imageSrc,
|
||||||
title,
|
name,
|
||||||
subTitle,
|
slug,
|
||||||
price,
|
price,
|
||||||
originPrice,
|
subText,
|
||||||
|
currencyCode,
|
||||||
buttonText = LANGUAGE.BUTTON_LABEL.BUY_NOW,
|
buttonText = LANGUAGE.BUTTON_LABEL.BUY_NOW,
|
||||||
}: FeaturedProductCardProps) => {
|
}: FeaturedProductCardProps) => {
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={s.featuredProductCardWarpper}>
|
<div className={s.featuredProductCardWarpper}>
|
||||||
<div className={s.left}>
|
<div className={s.left}>
|
||||||
<ImgWithLink src={imageSrc} alt={title}/>
|
<Link href={`${ROUTE.PRODUCT_DETAIL}/${slug}`}>
|
||||||
|
<a>
|
||||||
|
<ImgWithLink src={imageSrc} alt={name} />
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className={s.right}>
|
<div className={s.right}>
|
||||||
<div className={s.rightTop}>
|
<div className={s.rightTop}>
|
||||||
<div className={s.title}>{title}</div>
|
<Link href={`${ROUTE.PRODUCT_DETAIL}/${slug}`}>
|
||||||
<div className={s.subTitle}>{subTitle}</div>
|
<a>
|
||||||
|
<div className={s.title}>{name}</div>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
<div className={s.subTitle}>{subText}</div>
|
||||||
<div className={s.priceWrapper}>
|
<div className={s.priceWrapper}>
|
||||||
<div className={s.price}>{price} </div>
|
<div className={s.price}>{price} {currencyCode}</div>
|
||||||
<div className={s.originPrice}>{originPrice} </div>
|
{/* TODO: */}
|
||||||
|
{/* <div className={s.originPrice}>{originPrice} </div> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={s.buttonWarpper}>
|
<div className={s.buttonWarpper}>
|
||||||
<div className={s.icon}>
|
<div className={s.icon}>
|
||||||
<ButtonIconBuy size='default'/>
|
<ButtonIconBuy size='default' />
|
||||||
</div>
|
</div>
|
||||||
<div className={s.button}>
|
<div className={s.button}>
|
||||||
<ButtonCommon>{buttonText}</ButtonCommon>
|
<ButtonCommon>{buttonText}</ButtonCommon>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import s from './ImgWithLink.module.scss'
|
import s from './ImgWithLink.module.scss'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { BLUR_DATA_IMG } from 'src/utils/constanst.utils'
|
import { BLUR_DATA_IMG, DEFAULT_IMG } from 'src/utils/constanst.utils'
|
||||||
|
|
||||||
export interface ImgWithLinkProps {
|
export interface ImgWithLinkProps {
|
||||||
src: string,
|
src: string,
|
||||||
@@ -12,7 +12,7 @@ export interface ImgWithLinkProps {
|
|||||||
const ImgWithLink = ({ src, alt, blurDataURL = BLUR_DATA_IMG }: ImgWithLinkProps) => {
|
const ImgWithLink = ({ src, alt, blurDataURL = BLUR_DATA_IMG }: ImgWithLinkProps) => {
|
||||||
return (
|
return (
|
||||||
<div className={s.imgWithLink}>
|
<div className={s.imgWithLink}>
|
||||||
<Image src={src} alt={alt}
|
<Image src={src || DEFAULT_IMG.src} alt={alt}
|
||||||
layout="fill"
|
layout="fill"
|
||||||
className={s.imgWithLink}
|
className={s.imgWithLink}
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
|
import { ProductCard } from '@commerce/types/product'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { IconBuy } from 'src/components/icons'
|
import { IconBuy } from 'src/components/icons'
|
||||||
import { ROUTE } from 'src/utils/constanst.utils'
|
import { ROUTE } from 'src/utils/constanst.utils'
|
||||||
import { ProductProps } from 'src/utils/types.utils'
|
|
||||||
import { ImgWithLink } from '..'
|
import { ImgWithLink } from '..'
|
||||||
import ButtonCommon from '../ButtonCommon/ButtonCommon'
|
import ButtonCommon from '../ButtonCommon/ButtonCommon'
|
||||||
import ButtonIconBuy from '../ButtonIconBuy/ButtonIconBuy'
|
import ButtonIconBuy from '../ButtonIconBuy/ButtonIconBuy'
|
||||||
@@ -11,16 +11,18 @@ import LabelCommon from '../LabelCommon/LabelCommon'
|
|||||||
import s from './ProductCard.module.scss'
|
import s from './ProductCard.module.scss'
|
||||||
import ProductNotSell from './ProductNotSell/ProductNotSell'
|
import ProductNotSell from './ProductNotSell/ProductNotSell'
|
||||||
|
|
||||||
export interface ProductCardProps extends ProductProps {
|
export interface ProductCardProps extends ProductCard {
|
||||||
buttonText?: string
|
buttonText?: string
|
||||||
isSingleButton?: boolean,
|
isSingleButton?: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProductCard = ({
|
const ProductCardComponent = ({
|
||||||
category,
|
category,
|
||||||
name,
|
name,
|
||||||
|
slug,
|
||||||
weight,
|
weight,
|
||||||
price,
|
price,
|
||||||
|
currencyCode,
|
||||||
buttonText = 'Buy Now',
|
buttonText = 'Buy Now',
|
||||||
imageSrc,
|
imageSrc,
|
||||||
isNotSell,
|
isNotSell,
|
||||||
@@ -35,24 +37,31 @@ const ProductCard = ({
|
|||||||
return (
|
return (
|
||||||
<div className={s.productCardWarpper}>
|
<div className={s.productCardWarpper}>
|
||||||
<div className={s.cardTop}>
|
<div className={s.cardTop}>
|
||||||
<Link href={`${ROUTE.PRODUCT_DETAIL}/test`}>
|
<Link href={`${ROUTE.PRODUCT_DETAIL}/${slug}`}>
|
||||||
<div className={s.productImage}>
|
<a>
|
||||||
<ImgWithLink src={imageSrc} alt={name}/>
|
<div className={s.productImage}>
|
||||||
</div>
|
<ImgWithLink src={imageSrc} alt={name}/>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
{
|
||||||
|
category &&
|
||||||
<div className={s.productLabel}>
|
<div className={s.productLabel}>
|
||||||
<LabelCommon shape="half">{category}</LabelCommon>
|
<LabelCommon shape="half">{category}</LabelCommon>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className={s.cardMid}>
|
<div className={s.cardMid}>
|
||||||
<div className={s.cardMidTop}>
|
<div className={s.cardMidTop}>
|
||||||
<Link href={`${ROUTE.PRODUCT_DETAIL}/test`}>
|
<Link href={`${ROUTE.PRODUCT_DETAIL}/${slug}`}>
|
||||||
<div className={s.productname}>{name} </div>
|
<a>
|
||||||
|
<div className={s.productname}>{name} </div>
|
||||||
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<div className={s.productWeight}>{weight}</div>
|
<div className={s.productWeight}>{weight}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={s.cardMidBot}>
|
<div className={s.cardMidBot}>
|
||||||
<div className={s.productPrice}>{price}</div>
|
<div className={s.productPrice}>{price} {currencyCode}</div>
|
||||||
<div className={s.wishList}>
|
<div className={s.wishList}>
|
||||||
<ItemWishList />
|
<ItemWishList />
|
||||||
</div>
|
</div>
|
||||||
@@ -80,4 +89,4 @@ const ProductCard = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ProductCard
|
export default ProductCardComponent
|
||||||
|
3
src/components/hooks/facets/index.ts
Normal file
3
src/components/hooks/facets/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export { default as useFacets } from './useFacets'
|
||||||
|
|
||||||
|
|
11
src/components/hooks/facets/useFacets.tsx
Normal file
11
src/components/hooks/facets/useFacets.tsx
Normal file
@@ -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>([getAllFacetsQuery, options], gglFetcher)
|
||||||
|
return { items: data?.facets.items, totalItems: data?.facets.totalItems, loading: isValidating, ...rest }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useFacets
|
@@ -1,92 +1,89 @@
|
|||||||
import React from 'react'
|
import { FacetValue } from '@framework/schema'
|
||||||
|
import React, { useMemo } from 'react'
|
||||||
import { ResponsiveType } from 'react-multi-carousel'
|
import { ResponsiveType } from 'react-multi-carousel'
|
||||||
import { CarouselCommon, FeaturedProductCard,HeadingCommon} from 'src/components/common'
|
import { CarouselCommon, FeaturedProductCard, HeadingCommon } from 'src/components/common'
|
||||||
import { FeaturedProductCardProps } from 'src/components/common/FeaturedProductCard/FeaturedProductCard'
|
import { FeaturedProductCardProps } from 'src/components/common/FeaturedProductCard/FeaturedProductCard'
|
||||||
|
import { getFacetNamesFromIds } from 'src/utils/funtion.utils'
|
||||||
import s from "./FeaturedProductsCarousel.module.scss"
|
import s from "./FeaturedProductsCarousel.module.scss"
|
||||||
interface FeaturedProductsCarouselProps {
|
interface FeaturedProductsCarouselProps {
|
||||||
title?: string
|
data: FeaturedProductCardProps[]
|
||||||
|
featuredFacetsValue: FacetValue[],
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataDemo:FeaturedProductCardProps[] = [{
|
const RESPONSIVE: ResponsiveType = {
|
||||||
title: "Sale 25% Coffee Bean",
|
hugeScreen: {
|
||||||
subTitle: "50 first Orders within a day",
|
breakpoint: { max: 9999, min: 1500 },
|
||||||
originPrice: "$20.00",
|
items: 2.25,
|
||||||
price: "$14.00",
|
slidesToSlide: 1, // optional, default to 1.
|
||||||
imageSrc: "https://user-images.githubusercontent.com/76099413/133043628-db7813f9-1bb7-4ee1-b028-dc4295563494.png"
|
},
|
||||||
},{
|
largeScreen: {
|
||||||
title: "Sale 20% Fruits",
|
breakpoint: { max: 1500, min: 1440 },
|
||||||
subTitle: "50 first Orders within a day",
|
items: 2.075,
|
||||||
originPrice: "$20.00",
|
slidesToSlide: 1, // optional, default to 1.
|
||||||
price: "$14.00",
|
},
|
||||||
imageSrc: "https://user-images.githubusercontent.com/76099413/133043630-07a353b9-573d-4c1d-b1de-2c932e3f14f7.png"
|
largeDesktop: {
|
||||||
},{
|
breakpoint: { max: 1440, min: 1280 },
|
||||||
title: "Sale 25% Coffee Bean",
|
items: 1.75,
|
||||||
subTitle: "50 first Orders within a day",
|
slidesToSlide: 1, // optional, default to 1.
|
||||||
originPrice: "$20.00",
|
},
|
||||||
price: "$14.00",
|
desktop: {
|
||||||
imageSrc: "https://user-images.githubusercontent.com/76099413/133043633-954c105b-c703-4e5c-8f5f-7943ad633ff0.png"
|
breakpoint: { max: 1280, min: 1148 },
|
||||||
}]
|
items: 1.5,
|
||||||
const RESPONSIVE: ResponsiveType = {
|
slidesToSlide: 1, // optional, default to 1.
|
||||||
hugeScreen: {
|
},
|
||||||
breakpoint: { max: 9999, min: 1500 },
|
smallDesktop: {
|
||||||
items: 2.25,
|
breakpoint: { max: 1148, min: 1024 },
|
||||||
slidesToSlide: 1, // optional, default to 1.
|
items: 1.375,
|
||||||
},
|
slidesToSlide: 1, // optional, default to 1.
|
||||||
largeScreen: {
|
},
|
||||||
breakpoint: { max: 1500, min: 1440 },
|
lap: {
|
||||||
items: 2.075,
|
breakpoint: { max: 1024, min: 968 },
|
||||||
slidesToSlide: 1, // optional, default to 1.
|
items: 1.7,
|
||||||
},
|
},
|
||||||
largeDesktop: {
|
tablet: {
|
||||||
breakpoint: { max: 1440, min: 1280 },
|
breakpoint: { max: 968, min: 768 },
|
||||||
items: 1.75,
|
items: 1.075,
|
||||||
slidesToSlide: 1, // optional, default to 1.
|
},
|
||||||
},
|
smallTablet: {
|
||||||
desktop: {
|
breakpoint: { max: 768, min: 640 },
|
||||||
breakpoint: { max: 1280, min: 1148 },
|
items: 1.25,
|
||||||
items: 1.5,
|
},
|
||||||
slidesToSlide: 1, // optional, default to 1.
|
largeMobile: {
|
||||||
},
|
breakpoint: { max: 640, min: 400 },
|
||||||
smallDesktop: {
|
items: 1.275,
|
||||||
breakpoint: { max: 1148, min: 1024 },
|
},
|
||||||
items: 1.375,
|
mobile: {
|
||||||
slidesToSlide: 1, // optional, default to 1.
|
breakpoint: { max: 400, min: 300 },
|
||||||
},
|
items: 1.1,
|
||||||
lap: {
|
},
|
||||||
breakpoint: { max: 1024, min: 968 },
|
smallMobile: {
|
||||||
items: 1.7,
|
breakpoint: { max: 300, min: 0 },
|
||||||
},
|
items: 1,
|
||||||
tablet: {
|
},
|
||||||
breakpoint: { max: 968, min: 768 },
|
}
|
||||||
items: 1.075,
|
|
||||||
},
|
|
||||||
smallTablet: {
|
|
||||||
breakpoint: { max: 768, min: 640 },
|
|
||||||
items: 1.25,
|
|
||||||
},
|
|
||||||
largeMobile: {
|
|
||||||
breakpoint: { max: 640, min: 400 },
|
|
||||||
items: 1.275,
|
|
||||||
},
|
|
||||||
mobile: {
|
|
||||||
breakpoint: { max: 400, min: 300 },
|
|
||||||
items: 1.1,
|
|
||||||
},
|
|
||||||
smallMobile: {
|
|
||||||
breakpoint: { max: 300, min: 0 },
|
|
||||||
items: 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const FeaturedProductsCarousel = ({title="Featured Products"}: FeaturedProductsCarouselProps) => {
|
const FeaturedProductsCarousel = ({ data, featuredFacetsValue }: FeaturedProductsCarouselProps) => {
|
||||||
return (
|
const featuredProducts = useMemo(() => {
|
||||||
<div className={s.warpper}>
|
return data.map(item => {
|
||||||
<div className={s.heading}>
|
return {
|
||||||
<HeadingCommon>{title}</HeadingCommon>
|
...item,
|
||||||
</div>
|
subText: getFacetNamesFromIds(featuredFacetsValue, item.facetValueIds)
|
||||||
<CarouselCommon<FeaturedProductCardProps> data={dataDemo} Component={FeaturedProductCard} itemKey="featured-products" responsive={RESPONSIVE}/>
|
}
|
||||||
</div>
|
})
|
||||||
)
|
}, [data, featuredFacetsValue])
|
||||||
|
return (
|
||||||
|
<div className={s.warpper}>
|
||||||
|
<div className={s.heading}>
|
||||||
|
<HeadingCommon>Featured Products</HeadingCommon>
|
||||||
|
</div>
|
||||||
|
<CarouselCommon<FeaturedProductCardProps>
|
||||||
|
data={featuredProducts}
|
||||||
|
defaultComponentProps={featuredFacetsValue}
|
||||||
|
Component={FeaturedProductCard}
|
||||||
|
itemKey="featured-products"
|
||||||
|
responsive={RESPONSIVE} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FeaturedProductsCarousel
|
export default FeaturedProductsCarousel
|
||||||
|
27
src/components/modules/home/FreshProducts/FreshProducts.tsx
Normal file
27
src/components/modules/home/FreshProducts/FreshProducts.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { ProductCard } from '@commerce/types/product'
|
||||||
|
import { Product } from '@framework/schema'
|
||||||
|
import React from 'react'
|
||||||
|
import { CollectionCarcousel } from '..'
|
||||||
|
interface FreshProductsProps {
|
||||||
|
data: ProductCard[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const FreshProducts = ({ data }: FreshProductsProps) => {
|
||||||
|
if (data.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="w-full">
|
||||||
|
<CollectionCarcousel
|
||||||
|
type="highlight"
|
||||||
|
data={data}
|
||||||
|
itemKey="product-1"
|
||||||
|
title="Fresh Products Today"
|
||||||
|
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||||
|
category={"veggie"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FreshProducts
|
@@ -95,14 +95,6 @@ const dataTest = [
|
|||||||
const HomeCollection = (props: HomeCollectionProps) => {
|
const HomeCollection = (props: HomeCollectionProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<CollectionCarcousel
|
|
||||||
type="highlight"
|
|
||||||
data={dataTest}
|
|
||||||
itemKey="product-1"
|
|
||||||
title="Fresh Products Today"
|
|
||||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
|
||||||
category={"veggie"}
|
|
||||||
/>
|
|
||||||
<CollectionCarcousel
|
<CollectionCarcousel
|
||||||
data={dataTest}
|
data={dataTest}
|
||||||
itemKey="product-2"
|
itemKey="product-2"
|
||||||
|
@@ -4,6 +4,7 @@ export { default as HomeCategories } from './HomeCategories/HomeCategories'
|
|||||||
export { default as HomeCTA } from './HomeCTA/HomeCTA'
|
export { default as HomeCTA } from './HomeCTA/HomeCTA'
|
||||||
export { default as HomeSubscribe } from './HomeSubscribe/HomeSubscribe'
|
export { default as HomeSubscribe } from './HomeSubscribe/HomeSubscribe'
|
||||||
export { default as HomeVideo } from './HomeVideo/HomeVideo'
|
export { default as HomeVideo } from './HomeVideo/HomeVideo'
|
||||||
|
export { default as FreshProducts } from './FreshProducts/FreshProducts'
|
||||||
export { default as HomeCollection } from './HomeCollection/HomeCollection'
|
export { default as HomeCollection } from './HomeCollection/HomeCollection'
|
||||||
export { default as HomeRecipe } from './HomeRecipe/HomeRecipe'
|
export { default as HomeRecipe } from './HomeRecipe/HomeRecipe'
|
||||||
export { default as FeaturedProductsCarousel } from './FeaturedProductsCarousel/FeaturedProductsCarousel'
|
export { default as FeaturedProductsCarousel } from './FeaturedProductsCarousel/FeaturedProductsCarousel'
|
||||||
|
@@ -1,41 +1,44 @@
|
|||||||
|
import DefaultImg from '../../public/assets/images/default_img.jpg'
|
||||||
|
|
||||||
export const BLUR_DATA_IMG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mN8fBIAApUBruKYvzsAAAAASUVORK5CYII='
|
export const BLUR_DATA_IMG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mN8fBIAApUBruKYvzsAAAAASUVORK5CYII='
|
||||||
|
export const DEFAULT_IMG = DefaultImg
|
||||||
|
|
||||||
export const SOCIAL_LINKS = {
|
export const SOCIAL_LINKS = {
|
||||||
FB: 'FB',
|
FB: 'FB',
|
||||||
TWITTER: 'TWITTER',
|
TWITTER: 'TWITTER',
|
||||||
YOUTUBE: 'YOUTUBE',
|
YOUTUBE: 'YOUTUBE',
|
||||||
IG: 'IG',
|
IG: 'IG',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ROUTE = {
|
export const ROUTE = {
|
||||||
HOME: '/',
|
HOME: '/',
|
||||||
ABOUT: '/about',
|
ABOUT: '/about',
|
||||||
ACCOUNT: '/account',
|
ACCOUNT: '/account',
|
||||||
|
|
||||||
PRODUCTS: '/products',
|
PRODUCTS: '/products',
|
||||||
PRODUCT_DETAIL: '/product',
|
PRODUCT_DETAIL: '/product',
|
||||||
|
|
||||||
BLOGS: '/blogs',
|
|
||||||
BLOG_DETAIL: '/blog',
|
|
||||||
|
|
||||||
RECIPES: '/recipes',
|
BLOGS: '/blogs',
|
||||||
RECIPE_DETAIL: '/recipe',
|
BLOG_DETAIL: '/blog',
|
||||||
|
|
||||||
NOTIFICATION: '/notifications',
|
RECIPES: '/recipes',
|
||||||
BUSSINESS: '/bussiness',
|
RECIPE_DETAIL: '/recipe',
|
||||||
CONTACT: '/contact',
|
|
||||||
CHECKOUT: '/checkout',
|
NOTIFICATION: '/notifications',
|
||||||
FAQ: '/faq',
|
BUSSINESS: '/bussiness',
|
||||||
CUSTOMER_SERVICE: '/customer-service',
|
CONTACT: '/contact',
|
||||||
TERM_CONDITION: '/term-condition',
|
CHECKOUT: '/checkout',
|
||||||
PRIVACY_POLICY: '/privacy-policy',
|
FAQ: '/faq',
|
||||||
FORGOT_PASSWORD: '/forgot-password'
|
CUSTOMER_SERVICE: '/customer-service',
|
||||||
|
TERM_CONDITION: '/term-condition',
|
||||||
|
PRIVACY_POLICY: '/privacy-policy',
|
||||||
|
FORGOT_PASSWORD: '/forgot-password'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ACCOUNT_TAB = {
|
export const ACCOUNT_TAB = {
|
||||||
CUSTOMER_INFO: '',
|
CUSTOMER_INFO: '',
|
||||||
ORDER: 'orders',
|
ORDER: 'orders',
|
||||||
FAVOURITE: 'wishlist',
|
FAVOURITE: 'wishlist',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LOCAL_STORAGE_KEY = {
|
export const LOCAL_STORAGE_KEY = {
|
||||||
@@ -43,93 +46,99 @@ export const LOCAL_STORAGE_KEY = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const QUERY_KEY = {
|
export const QUERY_KEY = {
|
||||||
TAB: 'tab',
|
TAB: 'tab',
|
||||||
CATEGORY: 'category',
|
CATEGORY: 'category',
|
||||||
BRAND: 'brand',
|
BRAND: 'brand',
|
||||||
FEATURED: 'feature',
|
FEATURED: 'feature',
|
||||||
SORTBY:'sortby',
|
SORTBY: 'sortby',
|
||||||
RECIPES:'recipes'
|
RECIPES: 'recipes'
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ProductFeature {
|
export enum ProductFeature {
|
||||||
BestSellers = 'Best Sellers',
|
BestSellers = 'Best Sellers',
|
||||||
Sales = 'Sales',
|
Sales = 'Sales',
|
||||||
NewItem = 'New Item',
|
NewItem = 'New Item',
|
||||||
Viewed = 'Viewed',
|
Viewed = 'Viewed',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const KEY = {
|
export const KEY = {
|
||||||
ENTER: 'Enter',
|
ENTER: 'Enter',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OPTION_ALL = 'all';
|
export const OPTION_ALL = 'all';
|
||||||
export const DEFAULT_PAGE_SIZE=20;
|
export const DEFAULT_PAGE_SIZE = 20;
|
||||||
|
|
||||||
|
|
||||||
export const CATEGORY = [
|
export const CATEGORY = [
|
||||||
{
|
{
|
||||||
name: 'All',
|
name: 'All',
|
||||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=${OPTION_ALL}`,
|
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=${OPTION_ALL}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Veggie',
|
name: 'Veggie',
|
||||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=veggie`,
|
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=veggie`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Seafood',
|
name: 'Seafood',
|
||||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=seafood`,
|
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=seafood`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Frozen',
|
name: 'Frozen',
|
||||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=frozen`,
|
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=frozen`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Coffee Bean',
|
name: 'Coffee Bean',
|
||||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=coffee_bean`,
|
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=coffee_bean`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Sauce',
|
name: 'Sauce',
|
||||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=sauce`,
|
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=sauce`,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export const BRAND = [
|
|
||||||
{
|
|
||||||
name: 'Maggi',
|
|
||||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=maggi`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Chomilex',
|
|
||||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=chomilex`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Chinsu',
|
|
||||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=chinsu`,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export const FEATURED = [
|
|
||||||
{
|
|
||||||
name: 'Best Sellers',
|
|
||||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.FEATURED}=best_sellers`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Sales',
|
|
||||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.FEATURED}=sales`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'New Item',
|
|
||||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.FEATURED}=new_item`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Viewed',
|
|
||||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.FEATURED}=viewed`,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export const DEFAULT_BLOG_PAGE_SIZE=6;
|
|
||||||
|
|
||||||
export const FILTER_PAGE = [ROUTE.HOME,ROUTE.PRODUCTS]
|
export const BRAND = [
|
||||||
|
{
|
||||||
|
name: 'Maggi',
|
||||||
|
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=maggi`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Chomilex',
|
||||||
|
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=chomilex`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Chinsu',
|
||||||
|
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=chinsu`,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const CODE_FACET_FEATURED = 'featured'
|
||||||
|
export const CODE_FACET_DISCOUNT = 'discount'
|
||||||
|
export const CODE_FACET_FEATURED_VARIANT = {
|
||||||
|
FRESH: 'fresh',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FEATURED = [
|
||||||
|
{
|
||||||
|
name: 'Best Sellers',
|
||||||
|
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.FEATURED}=best_sellers`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Sales',
|
||||||
|
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.FEATURED}=sales`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'New Item',
|
||||||
|
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.FEATURED}=new_item`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Viewed',
|
||||||
|
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.FEATURED}=viewed`,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const DEFAULT_BLOG_PAGE_SIZE = 6;
|
||||||
|
|
||||||
|
export const FILTER_PAGE = [ROUTE.HOME, ROUTE.PRODUCTS]
|
||||||
|
|
||||||
export const STATE_OPTIONS = [
|
export const STATE_OPTIONS = [
|
||||||
{
|
{
|
||||||
@@ -141,3 +150,4 @@ export const STATE_OPTIONS = [
|
|||||||
value: 'Hà Nội',
|
value: 'Hà Nội',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@@ -1,11 +1,58 @@
|
|||||||
|
import { Facet } from "@commerce/types/facet";
|
||||||
|
import { FacetValue } from './../../framework/vendure/schema.d';
|
||||||
|
import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED, CODE_FACET_FEATURED_VARIANT } from "./constanst.utils";
|
||||||
|
|
||||||
export function isMobile() {
|
export function isMobile() {
|
||||||
return window.innerWidth < 768
|
return window.innerWidth < 768
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeItem<T>(arr: Array<T>, value: T): Array<T> {
|
export function removeItem<T>(arr: Array<T>, value: T): Array<T> {
|
||||||
const index = arr.indexOf(value);
|
const index = arr.indexOf(value);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
arr.splice(index, 1);
|
arr.splice(index, 1);
|
||||||
}
|
}
|
||||||
return [...arr];
|
return [...arr];
|
||||||
|
}
|
||||||
|
|
||||||
|
function findFacetByCode(code: string, facets?: Facet) {
|
||||||
|
return facets?.values.find((item: FacetValue) => item.code === code)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFeaturedFacetId(facets: Facet[]) {
|
||||||
|
const featuredFacet = facets.find((item: Facet) => item.code === CODE_FACET_FEATURED)
|
||||||
|
return featuredFacet?.id
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFreshFacetId(facets: Facet[]) {
|
||||||
|
const featuredFacet = facets.find((item: Facet) => item.code === CODE_FACET_FEATURED)
|
||||||
|
const freshFacetValue = findFacetByCode(CODE_FACET_FEATURED_VARIANT.FRESH, featuredFacet)
|
||||||
|
|
||||||
|
return freshFacetValue?.id
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAllFacetValueIdsByParentCode(facets: Facet[], code: string) {
|
||||||
|
const featuredFacet = facets.find((item: Facet) => item.code === code)
|
||||||
|
const rs = featuredFacet?.values.map((item: FacetValue) => item.id)
|
||||||
|
|
||||||
|
return rs || []
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAllFacetValuesForFeatuedProducts(facets: Facet[]) {
|
||||||
|
const facetsRs = facets.filter((item: Facet) => item.code === CODE_FACET_FEATURED || item.code === CODE_FACET_DISCOUNT)
|
||||||
|
let rs = [] as FacetValue[]
|
||||||
|
facetsRs.map((item: Facet) => {
|
||||||
|
rs = rs.concat(item.values)
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
return rs
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFacetNamesFromIds(facets: FacetValue[], ids?: string[]): string {
|
||||||
|
if (!ids || ids?.length === 0) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const facetItems = facets.filter((item: FacetValue) => ids.includes(item.id))
|
||||||
|
const names = facetItems.map((item: FacetValue) => item.name)
|
||||||
|
return names.join(", ")
|
||||||
}
|
}
|
Reference in New Issue
Block a user