diff --git a/framework/commerce/api/operations.ts b/framework/commerce/api/operations.ts index efbbce35d..afd725081 100644 --- a/framework/commerce/api/operations.ts +++ b/framework/commerce/api/operations.ts @@ -1,7 +1,8 @@ +import { GetAllRecipePathsOperation, GetRecipeDetailOperation,GetAllRecipesOperation } from './../types/recipes'; import { GetAllFacetsOperation } from './../types/facet'; import type { ServerResponse } from 'http' import type { LoginOperation } from '../types/login' -import type { GetAllPagesOperation, GetPageOperation } from '../types/page' +import type { GetAllPagesOperation } from '../types/page' import type { GetSiteInfoOperation } from '../types/site' import type { GetCustomerWishlistOperation } from '../types/wishlist' import type { @@ -11,8 +12,13 @@ import type { } from '../types/product' import type { GetAllBlogsOperation, - GetFeaturedBlogsOperation + GetFeaturedBlogsOperation, + GetAllBlogPathsOperation, + GetBlogDetailOperation, + GetRelevantBlogsOperation } from '../types/blogs' + + import type { APIProvider, CommerceAPI } from '.' import { GetAllCollectionsOperation } from '@commerce/types/collection'; @@ -32,7 +38,13 @@ export const OPERATIONS = [ 'getAllFacets', 'getAllCollections', 'getAllBlogs', - 'getFeaturedBlog' + 'getFeaturedBlog', + 'getAllBlogPaths', + 'getBlogDetail', + 'getRelevantBlogs', + 'getAllRecipes', + 'getAllRecipePaths', + 'getRecipeDetail' ] as const export const defaultOperations = OPERATIONS.reduce((ops, k) => { @@ -73,14 +85,14 @@ export type Operations

= { ): Promise } - getPage: { - (opts: { + getRecipeDetail: { + (opts: { variables: T['variables'] config?: P['config'] preview?: boolean }): Promise - ( + ( opts: { variables: T['variables'] config?: P['config'] @@ -133,6 +145,50 @@ export type Operations

= { ): Promise } + + getAllBlogPaths: { + (opts: { + variables?: T['variables'] + config?: P['config'] + }): Promise + + ( + opts: { + variables?: T['variables'] + config?: P['config'] + } & OperationOptions + ): Promise + } + + getAllRecipePaths: { + (opts: { + variables?: T['variables'] + config?: P['config'] + }): Promise + + ( + opts: { + variables?: T['variables'] + config?: P['config'] + } & OperationOptions + ): Promise + } + + getAllRecipe: { + (opts: { + variables?: T['variables'] + config?: P['config'] + }): Promise + + ( + opts: { + variables?: T['variables'] + config?: P['config'] + } & OperationOptions + ): Promise + } + + getAllProducts: { (opts: { variables?: T['variables'] @@ -166,6 +222,38 @@ export type Operations

= { ): Promise } + getAllRecipes: { + (opts: { + variables?: T['variables'] + config?: P['config'] + preview?: boolean + }): Promise + + ( + opts: { + variables?: T['variables'] + config?: P['config'] + preview?: boolean + } & OperationOptions + ): Promise + } + + getRelevantBlogs: { + (opts: { + variables?: T['variables'] + config?: P['config'] + preview?: boolean + }): Promise + + ( + opts: { + variables?: T['variables'] + config?: P['config'] + preview?: boolean + } & OperationOptions + ): Promise + } + getFeaturedBlog: { (opts: { variables?: T['variables'] @@ -182,6 +270,21 @@ export type Operations

= { ): Promise } + getBlogDetail: { + (opts: { + variables?: T['variables'] + config?: P['config'] + preview?: boolean + }): Promise + + ( + opts: { + variables?: T['variables'] + config?: P['config'] + preview?: boolean + } & OperationOptions + ): Promise + } getProduct: { diff --git a/framework/commerce/types/blogs.ts b/framework/commerce/types/blogs.ts index fcdca972d..7957908b6 100644 --- a/framework/commerce/types/blogs.ts +++ b/framework/commerce/types/blogs.ts @@ -14,13 +14,27 @@ export type BlogsType = { totalItems: number } export type GetAllBlogsOperation = { - data: { items: T['items'][] } + data: { items: T['items'][], totalItems: number } + variables: { + productId: number, + take?: number + skip?: number + } +} +export type GetRelevantBlogsOperation = { + data: { items: T['items'][], totalItems: number } variables: { take?: number skip?: number } } +export type GetBlogDetailOperation = { + data: T['items'], + variables: { + slug?: string + } +} export type GetFeaturedBlogsOperation = { data: { items: T['items'][] } @@ -28,4 +42,10 @@ export type GetFeaturedBlogsOperation = { take?: number skip?: number } -} \ No newline at end of file +} +export type GetAllBlogPathsOperation< +T extends BlogsType = BlogsType +> = { + data: { blogs: Pick[] } + variables: { first?: number } +} diff --git a/framework/commerce/types/product.ts b/framework/commerce/types/product.ts index e31f34df9..421100581 100644 --- a/framework/commerce/types/product.ts +++ b/framework/commerce/types/product.ts @@ -1,3 +1,4 @@ +import { BlogsType } from './blogs'; import { CurrencyCode } from './../../vendure/schema.d'; import { FacetValueFilterInput, LogicalOperator, SearchResultSortParameter } from "@framework/schema" @@ -110,6 +111,8 @@ export type GetAllProductPathsOperation< variables: { first?: number } } + + export type GetAllProductsOperation = { data: { products: T['product'][] } variables: { diff --git a/framework/commerce/types/recipes.ts b/framework/commerce/types/recipes.ts new file mode 100644 index 000000000..037855f32 --- /dev/null +++ b/framework/commerce/types/recipes.ts @@ -0,0 +1,45 @@ +import { Asset, BlogTranslation, Maybe, Product } from './../../vendure/schema.d'; + +export type RecipeList = Node &{ + id: string + featuredAsset?: Maybe + isPublish:Boolean + translations: Array + authorName: string + authorAvatarAsset:Array + relevantProducts: Product + link:String + minutes:Number + people:Number +} +export type RecipesType = { + items: RecipeList + totalItems: number +} + +export enum SortRecipes { + ASC = 'ASC', + DESC = 'DESC', +} + +export type GetAllRecipesOperation = { + data: { items: T['items'][], totalItems: number } + variables: { + excludeBlogIds:Array, + take?: number + id?: SortRecipes + } +} +export type GetAllRecipePathsOperation< +T extends RecipesType = RecipesType +> = { + data: { recipes: Pick[] } + variables: { first?: number } +} + +export type GetRecipeDetailOperation = { + data: T['items'], + variables: { + slug?: string + } +} \ No newline at end of file diff --git a/framework/vendure/api/index.ts b/framework/vendure/api/index.ts index edf67d14f..3d9c9219f 100644 --- a/framework/vendure/api/index.ts +++ b/framework/vendure/api/index.ts @@ -1,18 +1,25 @@ import type { CommerceAPIConfig } from '@commerce/api' import { CommerceAPI, getCommerceApi as commerceApi } from '@commerce/api' -import getAllFacets from './operations/get-all-facets' +import getAllBlogs from './operations/get-all-blogs' import getAllCollections from './operations/get-all-collection' +import getAllFacets from './operations/get-all-facets' import getAllPages from './operations/get-all-pages' import getAllProductPaths from './operations/get-all-product-paths' import getAllProducts from './operations/get-all-products' +import getBlogDetail from './operations/get-blog-detail' import getCustomerWishlist from './operations/get-customer-wishlist' +import getFeaturedBlog from './operations/get-featured-blog' import getPage from './operations/get-page' import getProduct from './operations/get-product' import getSiteInfo from './operations/get-site-info' +import getAllBlogPaths from './operations/get-all-blog-paths' +import getRelevantBlogs from './operations/get-relevant-blogs' +import getAllRecipes from './operations/get-all-recipes' +import getAllRecipePaths from './operations/get-all-recipe-paths' +import getRecipeDetail from './operations/get-recipe-detail' + import login from './operations/login' import fetchGraphqlApi from './utils/fetch-graphql-api' -import getAllBlogs from './operations/get-all-blogs' -import getFeaturedBlog from './operations/get-featured-blog' export interface VendureConfig extends CommerceAPIConfig {} @@ -46,7 +53,13 @@ const operations = { getAllFacets, getAllCollections, getAllBlogs, - getFeaturedBlog + getFeaturedBlog, + getBlogDetail, + getAllBlogPaths, + getRelevantBlogs, + getAllRecipes, + getAllRecipePaths, + getRecipeDetail } export const provider = { config, operations } diff --git a/framework/vendure/api/operations/get-all-blog-paths.ts b/framework/vendure/api/operations/get-all-blog-paths.ts new file mode 100644 index 000000000..cdfce36f7 --- /dev/null +++ b/framework/vendure/api/operations/get-all-blog-paths.ts @@ -0,0 +1,53 @@ +import { BlogList } from './../../schema.d'; +import { OperationContext,OperationOptions } from '@commerce/api/operations'; +import { BigcommerceConfig } from '../../../bigcommerce/api'; +import type { GetAllBlogPathsQuery,BlogTranslation } from '../../schema'; +import { getAllBlogPathsQuery } from '../../utils/queries/get-all-blog-paths-query'; +import { Provider } from '../index'; +import { GetAllBlogPathsOperation } from './../../../commerce/types/blogs'; + +export type GetAllBlogPathsResult = { + blogs: Array<{ node: { path: string } }> +} + +export default function getAllBlogPathsOperation({ + commerce, +}: OperationContext) { + async function getAllBlogPaths< + T extends GetAllBlogPathsOperation + >(opts?: { + variables?: T['variables'] + config?: BigcommerceConfig + }): Promise + + async function getAllBlogPaths( + opts: { + variables?: T['variables'] + config?: BigcommerceConfig + } & OperationOptions + ): Promise + + async function getAllBlogPaths({ + query = getAllBlogPathsQuery, + variables, + config: cfg, + }: { + query?: string + variables?: T['variables'] + config?: BigcommerceConfig + } = {}): Promise { + const config = commerce.getConfig(cfg) + + const { data } = await config.fetch(query, { + variables, + }) + + const blogs = data.blogs.items; + + return { + blogs: blogs?.map(val=>val.translations.map((p:BlogTranslation) => ({ path: `/${p.slug}` }))) + } + } + + return getAllBlogPaths +} diff --git a/framework/vendure/api/operations/get-all-blogs.ts b/framework/vendure/api/operations/get-all-blogs.ts index 3dc6565fa..bf3ba7be0 100644 --- a/framework/vendure/api/operations/get-all-blogs.ts +++ b/framework/vendure/api/operations/get-all-blogs.ts @@ -6,7 +6,12 @@ import { getAllBlogsQuery } from '../../utils/queries/get-all-blog-query' export type BlogVariables = { excludeBlogIds?: string[], take?: number, - skip?:number + skip?:number, + filter?:{ + isFeatured?:{ + eq?:Boolean + } + }, } export default function getAllBlogsOperation({ @@ -34,7 +39,9 @@ export default function getAllBlogsOperation({ excludeBlogIds: vars.excludeBlogIds, options: { take: vars.take, - skip: vars.skip, + filter: { + isFeatured: vars.filter?.isFeatured + } }, } const { data } = await config.fetch(query, { @@ -51,7 +58,7 @@ export default function getAllBlogsOperation({ isPublish: val.isPublish, isFeatured: val.isFeatured, authorName: val.authorName, - authorAvatarAsset : val.authorAvatarAsset?.preview, + authorAvatarAsset : val.authorAvatarAsset?.preview ?? null, createdAt: val.createdAt })), totalItems: data?.blogs?.totalItems || null diff --git a/framework/vendure/api/operations/get-all-recipe-paths.ts b/framework/vendure/api/operations/get-all-recipe-paths.ts new file mode 100644 index 000000000..ac09dbd1c --- /dev/null +++ b/framework/vendure/api/operations/get-all-recipe-paths.ts @@ -0,0 +1,53 @@ +import { BlogList } from '../../schema'; +import { OperationContext,OperationOptions } from '@commerce/api/operations'; +import { BigcommerceConfig } from '../../../bigcommerce/api'; +import type { GetAllRecipePathsQuery,RecipeTranslation } from '../../schema'; +import { getAllBlogPathsQuery } from '../../utils/queries/get-all-blog-paths-query'; +import { Provider } from '../index'; +import { GetAllRecipesPathsOperation } from '../../../commerce/types/recipes'; + +export type GetAllBlogPathsResult = { + blogs: Array<{ node: { path: string } }> +} + +export default function getAllRecipePathsOperation({ + commerce, +}: OperationContext) { + async function getAllRecipePaths< + T extends GetAllRecipesPathsOperation + >(opts?: { + variables?: T['variables'] + config?: BigcommerceConfig + }): Promise + + async function getAllRecipePaths( + opts: { + variables?: T['variables'] + config?: BigcommerceConfig + } & OperationOptions + ): Promise + + async function getAllRecipePaths({ + query = getAllBlogPathsQuery, + variables, + config: cfg, + }: { + query?: string + variables?: T['variables'] + config?: BigcommerceConfig + } = {}): Promise { + const config = commerce.getConfig(cfg) + + const { data } = await config.fetch(query, { + variables, + }) + + const recipes = data.blogs.items; + + return { + recipes: recipes?.map(val=>val.translations.map((p:RecipeTranslation) => ({ path: `/${p.slug}` }))) + } + } + + return getAllRecipePaths +} diff --git a/framework/vendure/api/operations/get-all-recipes.ts b/framework/vendure/api/operations/get-all-recipes.ts new file mode 100644 index 000000000..4998d793e --- /dev/null +++ b/framework/vendure/api/operations/get-all-recipes.ts @@ -0,0 +1,70 @@ +import { OperationContext } from '@commerce/api/operations' +import { Provider, VendureConfig } from '..' +import { BlogList, GetAllRecipesQuery } from '../../schema' +import { getAllBlogsQuery } from '../../utils/queries/get-all-blog-query' + +export type RecipesVariables = { + excludeBlogIds?: string[], + take?:number, + id?: string, + isPublish?:Boolean +} + +export default function getAllRecipesOperation({ + commerce, +}: OperationContext) { + async function getAllRecipes(opts?: { + variables?: RecipesVariables + config?: Partial + preview?: boolean + }): Promise<{ recipes: GetAllRecipesQuery[],totalItems:number }> + + async function getAllRecipes({ + query = getAllBlogsQuery, + variables: { ...vars } = {}, + config: cfg, + }: { + query?: string + variables?: RecipesVariables + config?: Partial + preview?: boolean + } = {}): Promise<{ recipes: GetAllRecipesQuery[] | any[] ,totalItems?:number }> { + + const config = commerce.getConfig(cfg) + const variables = { + excludeBlogIds: vars.excludeBlogIds, + options: { + take: vars.take, + sort: { + id: vars?.id + }, + filter:{ + isPublish: { + eq:vars.isPublish + } + } + }, + } + + const { data } = await config.fetch(query, { + variables, + }) + + return { + recipes: data?.blogs?.items?.map((val:BlogList)=>({ + id: val.id, + title: val.translations[0]?.title, + imageSrc: val.featuredAsset?.preview ?? null, + slug: val.translations[0]?.slug, + description: val.translations[0]?.description, + isPublish: val.isPublish, + authorName: val.authorName, + authorAvatarAsset : val.authorAvatarAsset?.preview ?? null, + createdAt: val.createdAt + })), + totalItems: data?.blogs?.totalItems || null + } + } + + return getAllRecipes +} diff --git a/framework/vendure/api/operations/get-blog-detail.ts b/framework/vendure/api/operations/get-blog-detail.ts new file mode 100644 index 000000000..6b8e92967 --- /dev/null +++ b/framework/vendure/api/operations/get-blog-detail.ts @@ -0,0 +1,55 @@ +import { OperationContext } from '@commerce/api/operations' +import { Provider, VendureConfig } from '..' +import { GetBlogQuery,BlogList } from '../../schema' +import { getBlogDetailQuery } from '../../utils/queries/get-blog-detail' + +export type BlogVariables = { + slug?: string, +} + +export default function getBlogDetailOperation({ + commerce, +}: OperationContext) { + async function getBlogDetail(opts?: { + variables?: BlogVariables + config?: Partial + preview?: boolean + }): Promise<{ blogDetail: BlogList}> + + async function getBlogDetail({ + query = getBlogDetailQuery, + variables: { ...vars } = {}, + config: cfg, + }: { + query?: string + variables?: BlogVariables + config?: Partial + preview?: boolean + } = {}): Promise<{ blogDetail: BlogList | any }> { + + const config = commerce.getConfig(cfg) + const variables = { + slug: vars.slug + } + const { data } = await config.fetch(query, { + variables, + }) + return { + blogDetail: { + id:data?.blog?.id, + title: data?.blog?.translations[0].title, + imageSrc: data?.blog?.featuredAsset?.preview ?? null, + slug: data?.blog?.translations[0]?.slug, + description: data?.blog?.translations[0]?.description, + isPublish: data?.blog?.isPublish, + isFeatured: data?.blog?.isFeatured, + authorName: data?.blog?.authorName, + authorAvatarAsset : data?.blog?.authorAvatarAsset?.preview, + createdAt: data?.blog?.createdAt, + relevantProducts: data?.blog?.relevantProducts.map(val=>val.id) + } + } + } + + return getBlogDetail +} diff --git a/framework/vendure/api/operations/get-featured-blog.ts b/framework/vendure/api/operations/get-featured-blog.ts index 727db46ed..cb101d6c4 100644 --- a/framework/vendure/api/operations/get-featured-blog.ts +++ b/framework/vendure/api/operations/get-featured-blog.ts @@ -5,7 +5,12 @@ import { getFeatuedBlogsQuery } from '../../utils/queries/get-featued-query' export type BlogVariables = { take?: number, - skip?:number + skip?:number, + filter?:{ + isFeatured?:{ + eq?:Boolean + } + }, } export default function getFeaturedBlogOperation({ @@ -31,6 +36,9 @@ export default function getFeaturedBlogOperation({ const variables = { options: { take: vars.take, + filter: { + isFeatured: vars.filter?.isFeatured + } }, } const { data } = await config.fetch(query, { @@ -46,7 +54,7 @@ export default function getFeaturedBlogOperation({ isPublish: val.isPublish, isFeatured: val.isFeatured, authorName: val.authorName, - authorAvatarAsset : val.authorAvatarAsset?.preview, + authorAvatarAsset : val.authorAvatarAsset?.preview ?? null, createdAt: val.createdAt })) } diff --git a/framework/vendure/api/operations/get-recipe-detail.ts b/framework/vendure/api/operations/get-recipe-detail.ts new file mode 100644 index 000000000..e8b2b167f --- /dev/null +++ b/framework/vendure/api/operations/get-recipe-detail.ts @@ -0,0 +1,98 @@ +import { OperationContext } from '@commerce/api/operations' +import { Provider, VendureConfig } from '..' +import { GetRecipeQuery, RecipeList } from '../../schema' +import { getBlogDetailQuery } from '../../utils/queries/get-blog-detail' + + +export default function getRecipeDetailOperation({ + commerce, +}: OperationContext) { + + async function getRecipeDetail({ + query = getBlogDetailQuery, + variables, + config: cfg, + }: { + query?: string + variables: { slug: string } + config?: Partial + preview?: boolean + }): Promise<{recipeDetail: RecipeList | any }> { + const config = commerce.getConfig(cfg) + const { data } = await config.fetch(query, { + variables, + }) + const recipes = data.blog + + if (recipes) { + return { + recipeDetail: { + id: data?.blog?.id, + title: data?.blog?.translations[0].title, + imageSrc: data?.blog?.featuredAsset?.preview ?? null, + slug: data?.blog?.translations[0]?.slug, + description: data?.blog?.translations[0]?.description, + isPublish: data?.blog?.isPublish, + authorName: data?.blog?.authorName, + authorAvatarAsset : data?.blog?.authorAvatarAsset?.preview, + createdAt: data?.blog?.createdAt, + relevantProducts: data?.blog?.relevantProducts.map(val=>val.id) + } + } + }else{ + return {recipeDetail:null} + } + } + + return getRecipeDetail +} + + +// export type RecipeVariables = { +// slug?: string, +// } + +// export default function getRecipeDetailOperation({ +// commerce, +// }: OperationContext) { + +// async function getRecipeDetail(opts?: { +// variables?: RecipeVariables +// config?: Partial +// preview?: boolean +// }): Promise<{ recipeDetail: RecipeList}> + +// async function getRecipeDetail({ +// query = getBlogDetailQuery, +// variables: { ...vars } = {}, +// config: cfg, +// }: { +// query?: string +// variables?: RecipeVariables +// config?: Partial +// preview?: boolean +// } = {}): Promise<{ recipeDetail: RecipeList | any }> { + +// const config = commerce.getConfig(cfg) +// const variables = { +// slug: vars.slug +// } +// const { data } = await config.fetch(query, { +// variables, +// }) +// return { +// id: data?.blog?.id, +// title: data?.blog?.translations[0].title, +// imageSrc: data?.blog?.featuredAsset?.preview ?? null, +// slug: data?.blog?.translations[0]?.slug, +// description: data?.blog?.translations[0]?.description, +// isPublish: data?.blog?.isPublish, +// authorName: data?.blog?.authorName, +// authorAvatarAsset : data?.blog?.authorAvatarAsset?.preview, +// createdAt: data?.blog?.createdAt, +// relevantProducts: data?.blog?.relevantProducts.map(val=>val.id) +// } +// } + +// return getRecipeDetail +// } diff --git a/framework/vendure/api/operations/get-relevant-blogs.ts b/framework/vendure/api/operations/get-relevant-blogs.ts new file mode 100644 index 000000000..3b9259c6a --- /dev/null +++ b/framework/vendure/api/operations/get-relevant-blogs.ts @@ -0,0 +1,54 @@ +import { OperationContext } from '@commerce/api/operations' +import { Provider, VendureConfig } from '..' +import { BlogList,GetRelevantBlogsQuery } from '../../schema' +import { getRelevantBlogsQuery } from '../../utils/queries/get-relevant-blogs' + +export type BlogVariables = { + productId?: number, +} + +export default function getRelevantBlogsOperation({ + commerce, +}: OperationContext) { + async function getRelevantBlogs(opts?: { + variables?: BlogVariables + config?: Partial + preview?: boolean + }): Promise<{ relevantBlogs: GetRelevantBlogsQuery[]}> + + async function getRelevantBlogs({ + query = getRelevantBlogsQuery, + variables: { ...vars } = {}, + config: cfg, + }: { + query?: string + variables?: BlogVariables + config?: Partial + preview?: boolean + } = {}): Promise<{ relevantBlogs: GetRelevantBlogsQuery[] | any[] }> { + + const config = commerce.getConfig(cfg) + const variables = { + productId: vars.productId, + } + const { data } = await config.fetch(query, { + variables, + }) + return { + relevantBlogs: data?.relevantBlogs?.items?.map((val:BlogList)=>({ + id: val.id, + title: val.translations[0]?.title, + imageSrc: val.featuredAsset?.preview ?? null, + slug: val.translations[0]?.slug, + description: val.translations[0]?.description, + isPublish: val.isPublish, + isFeatured: val.isFeatured, + authorName: val.authorName, + authorAvatarAsset : val.authorAvatarAsset?.preview, + createdAt: val.createdAt + })), + } + } + + return getRelevantBlogs +} diff --git a/framework/vendure/schema.d.ts b/framework/vendure/schema.d.ts index a1a6b7b49..cb3a56ef3 100644 --- a/framework/vendure/schema.d.ts +++ b/framework/vendure/schema.d.ts @@ -2388,10 +2388,66 @@ export type BlogList = Node &{ translations: Array authorName: Scalars['String'] authorAvatarAsset:Asset - relevantProducts: Product + relevantProducts: Product[] isFeatured: Boolean } +export type RecipeList = Node &{ + id: ID! + createdAt: DateTime! + updatedAt: DateTime! + featuredAsset?: Maybe + isPublish:Boolean + translations: Array + authorName: Scalars['String'] + authorAvatarAsset:Asset + relevantProducts: Product[] + link:String + minutes:Number + people:Number +} + +export enum SortRecipes { + ASC = 'ASC', + DESC = 'DESC', +} + +export type RecipeTranslation = { + __typename?: 'RecipeTranslation' + id: Scalars['ID'] + createdAt: Scalars['DateTime'] + updatedAt: Scalars['DateTime'] + languageCode: LanguageCode + title: Scalars['String'] + slug: Scalars['String'] + description: Scalars['String'] + content: Scalars['String'] + Ingredients:Scalars['String'] + Preparation:Scalars['String'] +} + +export type IngredientProducts = { + __typename?: 'IngredientProduct' + id: Scalars['ID'] + createdAt: Scalars['DateTime'] + updatedAt: Scalars['DateTime'] + product: Product[] +} + + +export type GetBlogQuery = { __typename?: 'Query' } & { + blog?: Maybe< + { __typename?: 'Blog' } & BlogList + > +} + +export type GetRecipeQuery = { __typename?: 'Query' } & { + recipeDetail?: Maybe< + { __typename?: 'Recipe' } & RecipeList + > +} + + export type BlogTranslation = { __typename?: 'BlogTranslation' id: Scalars['ID'] @@ -2403,6 +2459,18 @@ export type BlogTranslation = { description: Scalars['String'] content: Scalars['String'] } +export type RecipeTranslation = { + __typename?: 'BlogTranslation' + id: Scalars['ID'] + createdAt: Scalars['DateTime'] + updatedAt: Scalars['DateTime'] + languageCode: LanguageCode + title: Scalars['String'] + slug: Scalars['String'] + description: Scalars['String'] + content: Scalars['String'] +} + export type GetAllBlogsQuery = PaginatedList & { blogs: { __typename?: 'BlogList' } & { @@ -2410,6 +2478,21 @@ export type GetAllBlogsQuery = PaginatedList & { 'totalItems' } } + +export type GetAllRecipesQuery = PaginatedList & { + recipes: { __typename?: 'RecipeList' } & { + items: Array<{ __typename?: 'Recipe' } & RecipeList!>, + 'totalItems' + } +} + + +export type GetRelevantBlogsQuery = PaginatedList & { + relevantBlogs: { __typename?: 'BlogList' } & { + items: Array<{ __typename?: 'Blog' } & BlogList!>, + } +} + export type GetFeaturedBlogQuery = PaginatedList & { id:string, featuredBlogs: { __typename?: 'BlogList' } & { @@ -2418,11 +2501,26 @@ export type GetFeaturedBlogQuery = PaginatedList & { } } + export type QueryBlogs = { excludeBlogIds:Array, options: BlogListOptions } +export type QueryRecipes = { + excludeBlogIds?:Maybe, + options: RecipeListOptions +} + +export type RecipeListOptions = { + skip?: Maybe + take?: Maybe + sort?: RecipesSort +} +export type RecipesSort = { + id?: Maybe +} + export type BlogListOptions = { skip?: Maybe take?: Maybe @@ -3448,6 +3546,19 @@ export type GetAllProductPathsQuery = { __typename?: 'Query' } & { items: Array<{ __typename?: 'Product' } & Pick> } } + +export type GetAllBlogPathsQuery = { __typename?: 'Query' } & { + blogs: { __typename?: 'BlogList' } & { + items: Array<{ __typename?: 'Blog' } & Pick> + } +} + +export type GetAllRecipePathsQuery = { __typename?: 'Query' } & { + recipes: { __typename?: 'Recipes' } & { + items: Array<{ __typename?: 'Recipe' } & Pick> + } +} + export type GetAllProductsQueryVariables = Exact<{ input: SearchInput diff --git a/framework/vendure/utils/normalize.ts b/framework/vendure/utils/normalize.ts index b9c55680e..1bda5f098 100644 --- a/framework/vendure/utils/normalize.ts +++ b/framework/vendure/utils/normalize.ts @@ -1,6 +1,6 @@ -import { Cart, CartCheckout } from '@commerce/types/cart' -import { Product, ProductCard } from '@commerce/types/product' -import { CartFragment, Favorite, SearchResultFragment, ShippingMethod, BlogList } from '../schema' +import { Cart } from '@commerce/types/cart' +import { ProductCard, Product } from '@commerce/types/product' +import { CartFragment, SearchResultFragment,Favorite, BlogList, RecipeList,ShippingMethod } from '../schema' export function normalizeSearchResult(item: SearchResultFragment): ProductCard { return { @@ -155,4 +155,18 @@ export function normalizeBlogList(blog: BlogList) { authorAvatarAsset : blog.authorAvatarAsset?.preview, createdAt: blog.createdAt } +} + +export function normalizeRecipeList(recipe: RecipeList) { + return { + id: recipe.id, + title: recipe.translations[0]?.title, + imageSrc: recipe.featuredAsset?.preview ?? null, + slug: recipe.translations[0]?.slug, + description: recipe.translations[0]?.description, + isPublish: recipe.isPublish, + authorName: recipe.authorName, + authorAvatarAsset : recipe.authorAvatarAsset?.preview, + createdAt: recipe.createdAt + } } \ No newline at end of file diff --git a/framework/vendure/utils/queries/get-all-blog-paths-query.ts b/framework/vendure/utils/queries/get-all-blog-paths-query.ts new file mode 100644 index 000000000..69d89ca36 --- /dev/null +++ b/framework/vendure/utils/queries/get-all-blog-paths-query.ts @@ -0,0 +1,11 @@ +export const getAllBlogPathsQuery = /* GraphQL */ ` +query getAllBlogPaths($excludeBlogIds: [ID]! = [],$options:BlogListOptions){ + blogs(excludeBlogIds: $excludeBlogIds,options: $options){ + items{ + translations { + slug + } + } + } +} +` diff --git a/framework/vendure/utils/queries/get-blog-detail.ts b/framework/vendure/utils/queries/get-blog-detail.ts new file mode 100644 index 000000000..a8b8fbe53 --- /dev/null +++ b/framework/vendure/utils/queries/get-blog-detail.ts @@ -0,0 +1,26 @@ +export const getBlogDetailQuery = /* GraphQL */ ` +query getBlog($slug: String ){ + blog(slug: $slug){ + id + isPublish + isFeatured + authorName + createdAt + authorAvatarAsset{ + preview + } + featuredAsset { + preview + } + translations { + title + slug + description + content + } + relevantProducts{ + id + } + } + } +` \ No newline at end of file diff --git a/framework/vendure/utils/queries/get-relevant-blogs.ts b/framework/vendure/utils/queries/get-relevant-blogs.ts new file mode 100644 index 000000000..91c8ebe16 --- /dev/null +++ b/framework/vendure/utils/queries/get-relevant-blogs.ts @@ -0,0 +1,25 @@ +export const getRelevantBlogsQuery = /* GraphQL */ ` +query relevantBlogs($productId: ID!){ + relevantBlogs(productId:$productId){ + items { + id + isPublish + isFeatured + authorName + createdAt + authorAvatarAsset{ + preview + } + featuredAsset { + preview + } + translations { + title + slug + description + content + } + } + } +} +` diff --git a/pages/blog/[slug].tsx b/pages/blog/[slug].tsx index 8aaca04a9..26325f5be 100644 --- a/pages/blog/[slug].tsx +++ b/pages/blog/[slug].tsx @@ -2,16 +2,111 @@ import { Layout, RelevantBlogPosts } from 'src/components/common'; import BlogContent from 'src/components/modules/blog-detail/BlogContent/BlogContent'; import BlogDetailImg from 'src/components/modules/blog-detail/BlogDetailImg/BlogDetailImg'; import { BLOGS_DATA_TEST } from 'src/utils/demo-data' +import { GetStaticPropsContext,GetStaticPathsContext } from 'next'; +import { PromiseWithKey } from 'src/utils/types.utils'; +import { getAllPromies } from 'src/utils/funtion.utils'; +import commerce from '@lib/api/commerce'; +import { BlogCardProps } from 'src/components/common/CardBlog/CardBlog'; +import { REVALIDATE_TIME } from 'src/utils/constanst.utils' +interface Props { + blog:{blogDetail?: BlogCardProps}, + relevant:{relevantBlogs?:BlogCardProps[]} +} +export default function BlogDetailPage({blog,relevant}:Props) { - -export default function BlogDetailPage() { + let date = new Date(blog?.blogDetail?.createdAt ?? '' ); + let fullDate = date.toLocaleString('en-us', { month: 'long' }) + " " + date.getDate()+","+date.getFullYear(); + return ( <> - - - + + + {relevant.relevantBlogs?.length> 0 && } ) } + +export async function getStaticProps({ + params, + locale, + locales, + preview, +}: GetStaticPropsContext<{ slug: string }> ) { + const config = { locale, locales } + let promisesWithKey = [] as PromiseWithKey[] + let props = {} as any + + // Blog detail + const blogDetailPromise = await commerce.getBlogDetail({ + variables: { slug: params!.slug }, + config, + preview, + }) + props.blog = blogDetailPromise; + + if (!blogDetailPromise) { + throw new Error(`Blog with slug '${params!.slug}' not found`) + } + + // Relevant Blogs + const relevantProductId = blogDetailPromise.blogDetail.relevantProducts?.[0]; + if (relevantProductId && blogDetailPromise.blogDetail.relevantProducts.length > 0) { + + const relevantBlogs = commerce.getRelevantBlogs({ + variables: { productId: relevantProductId }, + config, + preview, + }) + promisesWithKey.push({ key: 'relevant', promise: relevantBlogs}) + + }else { + props.relevantBlogs = []; + } + + + try { + const promises = getAllPromies(promisesWithKey) + const rs = await Promise.all(promises) + + promisesWithKey.map((item, index) => { + props[item.key] = item.keyResult ? rs[index][item.keyResult] : rs[index] + return null + }) + + console.log(props.relevantBlogs); + return { + props, + revalidate: REVALIDATE_TIME, + } + } catch (err) { + + } +} + +export async function getStaticPaths({ locales }: GetStaticPathsContext) { + + const { blogs } = await commerce.getAllBlogPaths() + + return { + paths: locales + ? locales.reduce((arr, locale) => { + blogs.forEach((blog: any) => { + arr.push(`/${locale}/blog/${blog.slug}`) + }) + return arr + }, []) + : blogs.map((product: any) => `/blog/${product.path}`), + fallback: 'blocking', + } +} + + + BlogDetailPage.Layout = Layout diff --git a/pages/blogs.tsx b/pages/blogs.tsx index a9ebf5866..f4be801aa 100644 --- a/pages/blogs.tsx +++ b/pages/blogs.tsx @@ -13,7 +13,7 @@ interface Props { totalItems: number } export default function BlogsPage({ blogs, featuredBlog, totalItems }:Props) { - + let date = new Date(featuredBlog?.[0]?.createdAt ?? '' ); let fullDate = date.toLocaleString('en-us', { month: 'long' }) + " " + date.getDate()+","+date.getFullYear(); @@ -21,16 +21,18 @@ export default function BlogsPage({ blogs, featuredBlog, totalItems }:Props) { <> - - + { (featuredBlog?.length !=0 ) && + + } + ) } @@ -47,7 +49,12 @@ export async function getStaticProps({ const {featuredBlogs} = await commerce.getFeaturedBlog({ variables: { - take: 1 + take: 1, + filter: { + isFeatured: { + eq:true + } + } }, config, preview, @@ -58,7 +65,12 @@ export async function getStaticProps({ const blogsPromise = commerce.getAllBlogs({ variables: { excludeBlogIds: [idFeaturedBlog], - take: DEFAULT_BLOG_PAGE_SIZE + take: DEFAULT_BLOG_PAGE_SIZE, + filter: { + isFeatured: { + eq:false + } + } }, config, preview, diff --git a/pages/products.tsx b/pages/products.tsx index 4a37ab097..035ab87e6 100644 --- a/pages/products.tsx +++ b/pages/products.tsx @@ -14,10 +14,10 @@ interface Props { facets: Facet[], collections: Collection[], productsResult: { products: ProductCard[], totalItems: number }, - } export default function Products({ facets, collections, productsResult }: Props) { + return ( <> diff --git a/pages/recipe/[slug].tsx b/pages/recipe/[slug].tsx index 1f71ba5be..d05de7210 100644 --- a/pages/recipe/[slug].tsx +++ b/pages/recipe/[slug].tsx @@ -1,12 +1,98 @@ +import { useRouter } from 'next/router' import { Layout, RecipeDetail, RecommendedRecipes } from 'src/components/common' import { INGREDIENT_DATA_TEST, RECIPE_DATA_TEST } from 'src/utils/demo-data' +import commerce from '@lib/api/commerce'; +import { PromiseWithKey } from 'src/utils/types.utils'; +import { GetStaticPropsContext,GetStaticPathsContext } from 'next'; +import { getAllPromies } from 'src/utils/funtion.utils'; +import { REVALIDATE_TIME } from 'src/utils/constanst.utils' +import { RecipeCardProps } from 'src/components/common/RecipeCard/RecipeCard'; +interface Props { + recipe:{recipeDetail?: RecipeCardProps}, + relevant:{relevantBlogs?:RecipeCardProps[]} +} +export default function Slug({recipe,relevant}:Props) { -export default function Slug() { return

- - + +
} + +export async function getStaticProps({ + params, + locale, + locales, + preview, +}: GetStaticPropsContext<{ slug: string }> ) { + const config = { locale, locales } + let promisesWithKey = [] as PromiseWithKey[] + let props = {} as any + + // Blog detail + const recipesPromise = await commerce.getRecipeDetail({ + variables: { slug: params!.slug }, + config, + preview, + }) + props.recipe = recipesPromise; + + if (recipesPromise.recipeDetail === null) { + return { notFound: true }; + } + + // // Relevant Blogs + const relevantProductId = recipesPromise?.recipeDetail?.relevantProducts?.[0]; + if (relevantProductId && recipesPromise?.recipeDetail?.relevantProducts?.length > 0) { + + const relevantBlogs = commerce.getRelevantBlogs({ + variables: { productId: relevantProductId }, + config, + preview, + }) + promisesWithKey.push({ key: 'relevant', promise: relevantBlogs}) + + }else { + props.relevantBlogs = []; + } + + + try { + const promises = getAllPromies(promisesWithKey) + const rs = await Promise.all(promises) + + promisesWithKey.map((item, index) => { + props[item.key] = item.keyResult ? rs[index][item.keyResult] : rs[index] + return null + }) + return { + props, + revalidate: REVALIDATE_TIME, + } + } catch (err) { + + } +} + +export async function getStaticPaths({ locales }: GetStaticPathsContext) { + + const { recipes } = await commerce.getAllRecipePaths() + return { + paths: locales + ? locales.reduce((arr, locale) => { + recipes.forEach((blog: any) => { + arr.push(`/${locale}/recipe/${blog.slug}`) + }) + return arr + }, []) + : recipes.map((product: any) => `/recipe/${product.path}`), + fallback: 'blocking', + } +} + + Slug.Layout = Layout diff --git a/pages/recipes.tsx b/pages/recipes.tsx index a4acece13..7df69a9da 100644 --- a/pages/recipes.tsx +++ b/pages/recipes.tsx @@ -1,15 +1,68 @@ import { Layout } from 'src/components/common'; import RecipeListBanner from 'src/components/modules/recipes-list/RecipeListBanner/RecipeListBanner'; import RecipesList from 'src/components/modules/recipes-list/RecipesList/RecipesList'; +import { GetStaticPropsContext } from 'next'; +import { PromiseWithKey } from 'src/utils/types.utils'; +import { DEFAULT_BLOG_PAGE_SIZE } from "src/utils/constanst.utils"; +import commerce from '@lib/api/commerce'; +import { getAllPromies } from 'src/utils/funtion.utils'; +import { RecipeCardProps } from 'src/components/common/RecipeCard/RecipeCard'; - -export default function RecipeListPage() { +interface Props { + recipesResult: {recipes: RecipeCardProps[] ,totalItems?: number}, + +} +export default function RecipeListPage({recipesResult}:Props) { return ( <> - + ) } +export async function getStaticProps({ + preview, + locale, + locales, +}: GetStaticPropsContext) { + + const config = { locale, locales } + let promisesWithKey = [] as PromiseWithKey[] + let props = {} as any; + + + const recipesPromise = commerce.getAllRecipes({ + variables: { + excludeBlogIds: [], + take: DEFAULT_BLOG_PAGE_SIZE, + id: 'DESC', + isPublish:true + }, + config, + preview, + }) + promisesWithKey.push({ key: 'recipesResult', promise: recipesPromise}) + + + + try { + const promises = getAllPromies(promisesWithKey) + const rs = await Promise.all(promises) + + promisesWithKey.map((item, index) => { + props[item.key] = item.keyResult ? rs[index][item.keyResult] : rs[index] + return null + }) + + return { + props, + revalidate: 60 + } + } catch (err) { + + } +} + + RecipeListPage.Layout = Layout diff --git a/src/components/common/Author/Author.module.scss b/src/components/common/Author/Author.module.scss index 5a2dad969..037d988e7 100644 --- a/src/components/common/Author/Author.module.scss +++ b/src/components/common/Author/Author.module.scss @@ -5,7 +5,9 @@ .authorImage { width:3.2rem; height:3.2rem; - border-radius:3.2rem; + img{ + border-radius:3.2rem !important; + } div{ min-width:3.2rem !important; } diff --git a/src/components/common/RecipeDetail/RecipeDetail.tsx b/src/components/common/RecipeDetail/RecipeDetail.tsx index cdec99994..84d3e4f0d 100644 --- a/src/components/common/RecipeDetail/RecipeDetail.tsx +++ b/src/components/common/RecipeDetail/RecipeDetail.tsx @@ -1,20 +1,21 @@ import React from 'react' +import { RecipeProps } from 'src/utils/types.utils' import { ProductCardProps } from '../ProductCard/ProductCard' import RecipeDetailInfo from './components/RecipeDetailInfo/RecipeDetailInfo' import RecipeIngredient from './components/RecipeIngredient/RecipeIngredient' import s from './RecipeDetail.module.scss' -interface Props { +interface Props extends RecipeProps { className?: string children?: any, - ingredients: ProductCardProps[], + ingredients: ProductCardProps[] } -const RecipeDetail = ({ ingredients }: Props) => { +const RecipeDetail = ({ ingredients,...rest }: Props) => { return (
- +
) diff --git a/src/components/common/RecipeDetail/components/RecipeDetailInfo/RecipeDetailInfo.tsx b/src/components/common/RecipeDetail/components/RecipeDetailInfo/RecipeDetailInfo.tsx index a853ad869..046fa2b55 100644 --- a/src/components/common/RecipeDetail/components/RecipeDetailInfo/RecipeDetailInfo.tsx +++ b/src/components/common/RecipeDetail/components/RecipeDetailInfo/RecipeDetailInfo.tsx @@ -1,24 +1,26 @@ import React from 'react' import { ImgWithLink } from 'src/components/common' +import { RecipeProps } from 'src/utils/types.utils' import RecipeBriefInfo from '../RecipeBriefInfo/RecipeBriefInfo' import s from './RecipeDetailInfo.module.scss' -interface Props { +interface Prop extends RecipeProps { className?: string children?: any } -const RecipeDetailInfo = ({ }: Props) => { +const RecipeDetailInfo = ({ ...rest}: Prop) => { + return (
- +

- Crispy Fried Calamari + {rest.title}

diff --git a/src/components/common/RecommendedRecipes/RecommendedRecipes.tsx b/src/components/common/RecommendedRecipes/RecommendedRecipes.tsx index aa1df57d4..1b8494eeb 100644 --- a/src/components/common/RecommendedRecipes/RecommendedRecipes.tsx +++ b/src/components/common/RecommendedRecipes/RecommendedRecipes.tsx @@ -30,10 +30,10 @@ const RESPONSIVE: ResponsiveType = { }, } interface Props { - data: RecipeCardProps[], + data?: RecipeCardProps[], } -const RecommendedRecipes = ({ data }: Props) => { +const RecommendedRecipes = ({ data=[] }: Props) => { return (
diff --git a/src/components/common/RelevantBlogPosts/RelevantBlogPosts.tsx b/src/components/common/RelevantBlogPosts/RelevantBlogPosts.tsx index ab21590c3..c99cd83ac 100644 --- a/src/components/common/RelevantBlogPosts/RelevantBlogPosts.tsx +++ b/src/components/common/RelevantBlogPosts/RelevantBlogPosts.tsx @@ -14,40 +14,7 @@ interface RelevantProps { bgcolor?: "default" | "cream" } -const recipe:BlogCardProps[] = [ -{ - title: "Want to Lose Weight? Here are 10 DEBM Diet Guidelines for Beginners", - slug: 'have-a-nice-lunch', - description:"The DEBM diet stands for "+'"Delicious Happy Fun Diet"'+". This diet was popularized by Robert...", - imageSrc: "https://user-images.githubusercontent.com/46085455/133185783-8100ef4e-7a72-4dc1-bb12-2ca46b56b393.png", -},{ - title: "9 Ways to Make an Aloe Vera Mask at Home", - slug: 'have-a-nice-lunch', - description:"Aloe vera or aloe vera is a green plant, has thorns on the side of the skin with yellowish patches and...", - imageSrc: "https://user-images.githubusercontent.com/46085455/133185911-df505d10-fdcd-4312-add3-7c62ad8af71e.png", -},{ - title: "Don't Buy Wrong, Here Are 7 Ways to Choose a Ripe Dragon Fruit", - slug: 'have-a-nice-lunch', - description:"Dragon fruit is a type of fruit that is a favorite for many people because of its delicious and fresh...", - imageSrc: "https://user-images.githubusercontent.com/46085455/133185959-7ad75580-ca6d-4684-83d9-3f64500bbc97.png", -},{ - title: "Want to Lose Weight? Here are 10 DEBM Diet Guidelines for Beginners", - slug: 'have-a-nice-lunch', - description:"The DEBM diet stands for "+'"Delicious Happy Fun Diet"'+". This diet was popularized by Robert...", - imageSrc: "https://user-images.githubusercontent.com/46085455/133185783-8100ef4e-7a72-4dc1-bb12-2ca46b56b393.png", -},{ - title: "9 Ways to Make an Aloe Vera Mask at Home", - slug: 'have-a-nice-lunch', - description:"Aloe vera or aloe vera is a green plant, has thorns on the side of the skin with yellowish patches and...", - imageSrc: "https://user-images.githubusercontent.com/46085455/133185911-df505d10-fdcd-4312-add3-7c62ad8af71e.png", -},{ - title: "Don't Buy Wrong, Here Are 7 Ways to Choose a Ripe Dragon Fruit", - slug: 'have-a-nice-lunch', - description:"Dragon fruit is a type of fruit that is a favorite for many people because of its delicious and fresh...", - imageSrc: "https://user-images.githubusercontent.com/46085455/133185959-7ad75580-ca6d-4684-83d9-3f64500bbc97.png", -}] - - const RelevantBlogPosts = ({ data = recipe, itemKey="detail-relevant", title="Relevant Blog Posts", bgcolor = "default" }: RelevantProps) => { + const RelevantBlogPosts = ({ data , itemKey="detail-relevant", title="Relevant Blog Posts", bgcolor = "default" }: RelevantProps) => { return (
- + }
) diff --git a/src/components/hooks/recipe/index.ts b/src/components/hooks/recipe/index.ts new file mode 100644 index 000000000..a702a9644 --- /dev/null +++ b/src/components/hooks/recipe/index.ts @@ -0,0 +1 @@ +export {default as useGetRecipeList } from "./useGetRecipeList" \ No newline at end of file diff --git a/src/components/hooks/recipe/useGetRecipeList.tsx b/src/components/hooks/recipe/useGetRecipeList.tsx new file mode 100644 index 000000000..1db9052da --- /dev/null +++ b/src/components/hooks/recipe/useGetRecipeList.tsx @@ -0,0 +1,18 @@ +import { GetAllRecipesQuery,QueryRecipes, RecipeList } from '@framework/schema' +import { normalizeRecipeList } from '@framework/utils/normalize' +import { getAllBlogsQuery } from '@framework/utils/queries/get-all-blog-query' +import gglFetcher from 'src/utils/gglFetcher' +import useSWR from 'swr' + +const useGetRecipeList = (options?: QueryRecipes) => { + const { data, isValidating, ...rest } = useSWR([getAllBlogsQuery, options], gglFetcher) + + return { + reicpes: data?.blogs?.items?.map((recipe:RecipeList)=>normalizeRecipeList(recipe)), + totalItems: data?.blogs?.totalItems || null, + loading: isValidating, + ...rest + } +} + +export default useGetRecipeList diff --git a/src/components/modules/blog-detail/BlogContent/BlogContent.tsx b/src/components/modules/blog-detail/BlogContent/BlogContent.tsx index cb9d6b8d8..4a38ab5b1 100644 --- a/src/components/modules/blog-detail/BlogContent/BlogContent.tsx +++ b/src/components/modules/blog-detail/BlogContent/BlogContent.tsx @@ -9,57 +9,24 @@ import Link from 'next/link'; interface BlogContentProps { className?: string children?: any, + title?: string, + content?: string, + imgAuthor?: string, + date?: string, + authorName?: string, } -const BlogContent = ({}:BlogContentProps) => { +const BlogContent = ({title,date='',content,imgAuthor='',authorName='' }:BlogContentProps) => { + return ( <>
- -

The Best Sesame Soy Broccoli Salad

+ +

{title}

- +
- -

When you’re trying to eat healthier but want something more substantial than a leafy green salad, broccoli salad is there for you. I love the crunch and heft of broccoli, especially when it’s cut up into bite size spoonable pieces. -
-
- - Some people aren’t into raw broccoli, but I love it! I always go for the raw broccoli on those vegetable platters that seem to be at every potluck/party you go to. -
-
- This is a simple broccoli salad: you have the bulk of it, raw broccoli; crunchy red onions for a bit of acidity and raw crunch, craisins for sweetness, almonds for a nutty counter point; and a sweet and tangy soy-rice vinegar-sesame dressing. -

- -
-
- -

What is broccoli salad

-
-

When you’re trying to eat healthier but want something more substantial than a leafy green salad, broccoli salad is there for you. I love the crunch and heft of broccoli, especially when it’s cut up into bite size spoonable pieces. -
-
- - Some people aren’t into raw broccoli, but I love it! I always go for the raw broccoli on those vegetable platters that seem to be at every potluck/party you go to. -
-
- This is a simple broccoli salad: you have the bulk of it, raw broccoli; crunchy red onions for a bit of acidity and raw crunch, craisins for sweetness, almonds for a nutty counter point; and a sweet and tangy soy-rice vinegar-sesame dressing. -

- -
-
- -

What about broccoli stems?

-
-

- You can eat broccoli stems. In fact, they are delicious. Just use a peeler to peel off the outsides and then trim the stalks into small 1/4”-1/2” cubes. -

-
- -
- -
- + {content}
diff --git a/src/components/modules/blog-detail/BlogDetailImg/BlogDetailImg.tsx b/src/components/modules/blog-detail/BlogDetailImg/BlogDetailImg.tsx index ed9b88a29..57180025a 100644 --- a/src/components/modules/blog-detail/BlogDetailImg/BlogDetailImg.tsx +++ b/src/components/modules/blog-detail/BlogDetailImg/BlogDetailImg.tsx @@ -5,7 +5,8 @@ import BreadcrumbCommon from 'src/components/common/BreadcrumbCommon/BreadcrumbC import s from './BlogDetailImg.module.scss'; interface Props { className?: string - children?: any + children?: any, + imgSrc?:string } const CRUMBS =[ @@ -14,14 +15,14 @@ const CRUMBS =[ link:"/blog" } ] -const BlogDetailImg = ({}:Props ) => { +const BlogDetailImg = ({imgSrc = ''}:Props ) => { return ( <>
- +
) diff --git a/src/components/modules/blogs/BlogsList/BlogsList.tsx b/src/components/modules/blogs/BlogsList/BlogsList.tsx index d9dbf26ed..245769b6e 100644 --- a/src/components/modules/blogs/BlogsList/BlogsList.tsx +++ b/src/components/modules/blogs/BlogsList/BlogsList.tsx @@ -92,5 +92,3 @@ const BlogsList = ({ blogList,total,idFeatured }:BlogsListProps) => { } export default BlogsList - - diff --git a/src/components/modules/product-detail/ViewedProducts/ViewedProducts.tsx b/src/components/modules/product-detail/ViewedProducts/ViewedProducts.tsx index 22190b49a..cb68e50f5 100644 --- a/src/components/modules/product-detail/ViewedProducts/ViewedProducts.tsx +++ b/src/components/modules/product-detail/ViewedProducts/ViewedProducts.tsx @@ -8,7 +8,7 @@ import { useLocalStorage } from 'src/components/hooks/useLocalStorage'; interface Props { data: ProductCardProps[] } -const ViewedProducts = ({data}:Props) => { +const ViewedProducts = ({data = []}:Props) => { if (data.length===0){ return
} diff --git a/src/components/modules/recipes-list/RecipesList/RecipesList.tsx b/src/components/modules/recipes-list/RecipesList/RecipesList.tsx index e91599a2c..1499292f0 100644 --- a/src/components/modules/recipes-list/RecipesList/RecipesList.tsx +++ b/src/components/modules/recipes-list/RecipesList/RecipesList.tsx @@ -1,224 +1,303 @@ -import React from 'react'; -import { SelectCommon } from 'src/components/common'; -import BreadcrumbCommon from 'src/components/common/BreadcrumbCommon/BreadcrumbCommon'; -import MenuNavigation from 'src/components/common/MenuNavigation/MenuNavigation'; -import PaginationCommon from 'src/components/common/PaginationCommon/PaginationCommon'; -import { RecipeCardProps } from 'src/components/common/RecipeCard/RecipeCard'; -import { OPTION_ALL, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'; -import HeadingCommon from "../../../common/HeadingCommon/HeadingCommon"; +import React, { useEffect, useState,useRef, useMemo } from 'react' +import { SelectCommon } from 'src/components/common' +import BreadcrumbCommon from 'src/components/common/BreadcrumbCommon/BreadcrumbCommon' +import MenuNavigation from 'src/components/common/MenuNavigation/MenuNavigation' +import PaginationCommon from 'src/components/common/PaginationCommon/PaginationCommon' +import { RecipeCardProps } from 'src/components/common/RecipeCard/RecipeCard' +import { OPTION_ALL, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils' +import HeadingCommon from '../../../common/HeadingCommon/HeadingCommon' import { RecipeCard } from 'src/components/common' +import { DEFAULT_BLOG_PAGE_SIZE } from 'src/utils/constanst.utils' +import s from './RecipesList.module.scss' +import { useRouter } from 'next/router' +import { QueryRecipes } from '@framework/schema' +import { useGetRecipeList } from 'src/components/hooks/recipe' +import { getPageFromQuery } from 'src/utils/funtion.utils' +import { ListBlogCardSkeleton } from 'src/components/common' -import s from './RecipesList.module.scss'; +const recipe: RecipeCardProps[] = [ + { + title: 'Special Recipe of Vietnamese Phở', + description: + 'Alright, before we get to the actual recipe, let’s chat for a sec about the ingredients. To make this pho soup recipe, you will need:', + imageSrc: + 'https://user-images.githubusercontent.com/76729908/132159257-f92574c7-d00d-4142-8ea7-0ca9515fb737.png', + slug: 'special-recipe-of-vietnamese-pho', + }, + { + title: 'Original Recipe of Curry', + description: + 'Chicken curry is common to several countries including India, countries in Asia and the Caribbean. My favorite of them though is this aromatic Indian...', + imageSrc: + 'https://user-images.githubusercontent.com/76729908/132159259-ae4c986d-ab53-4758-9137-d06bafdd15d0.png', + slug: 'original-recipe-of-curry', + }, + { + title: 'The Best Recipe of Beef Noodle Soup', + description: + 'The broth for Bun Bo Hue is prepared by slowly simmering various types of beef and pork bones (ox tail, beef shank, pork neck bones, pork feet,...', + imageSrc: + 'https://user-images.githubusercontent.com/76729908/132159262-f28a9fb9-4852-47e6-80b5-d600521b548a.png', + slug: 'the-best-recipe-of-beef-noodle-soup', + }, + { + title: 'Special Recipe of Vietnamese Phở', + description: + 'Alright, before we get to the actual recipe, let’s chat for a sec about the ingredients. To make this pho soup recipe, you will need:', + imageSrc: + 'https://user-images.githubusercontent.com/76729908/132159257-f92574c7-d00d-4142-8ea7-0ca9515fb737.png', + slug: 'special-recipe-of-vietnamese-pho', + }, + { + title: 'Original Recipe of Curry', + description: + 'Chicken curry is common to several countries including India, countries in Asia and the Caribbean. My favorite of them though is this aromatic Indian...', + imageSrc: + 'https://user-images.githubusercontent.com/76729908/132159259-ae4c986d-ab53-4758-9137-d06bafdd15d0.png', + slug: 'original-recipe-of-curry', + }, + { + title: 'The Best Recipe of Beef Noodle Soup', + description: + 'The broth for Bun Bo Hue is prepared by slowly simmering various types of beef and pork bones (ox tail, beef shank, pork neck bones, pork feet,...', + imageSrc: + 'https://user-images.githubusercontent.com/76729908/132159262-f28a9fb9-4852-47e6-80b5-d600521b548a.png', + slug: 'the-best-recipe-of-beef-noodle-soup', + }, +] -const recipe:RecipeCardProps[] = [ -{ - title: "Special Recipe of Vietnamese Phở", - description: "Alright, before we get to the actual recipe, let’s chat for a sec about the ingredients. To make this pho soup recipe, you will need:", - imageSrc: 'https://user-images.githubusercontent.com/76729908/132159257-f92574c7-d00d-4142-8ea7-0ca9515fb737.png', - slug: "special-recipe-of-vietnamese-pho" -}, -{ - title: "Original Recipe of Curry", - description: "Chicken curry is common to several countries including India, countries in Asia and the Caribbean. My favorite of them though is this aromatic Indian...", - imageSrc: 'https://user-images.githubusercontent.com/76729908/132159259-ae4c986d-ab53-4758-9137-d06bafdd15d0.png', - slug:"original-recipe-of-curry" -}, -{ - title: "The Best Recipe of Beef Noodle Soup", - description: "The broth for Bun Bo Hue is prepared by slowly simmering various types of beef and pork bones (ox tail, beef shank, pork neck bones, pork feet,...", - imageSrc: 'https://user-images.githubusercontent.com/76729908/132159262-f28a9fb9-4852-47e6-80b5-d600521b548a.png', - slug:"the-best-recipe-of-beef-noodle-soup" -}, -{ - title: "Special Recipe of Vietnamese Phở", - description: "Alright, before we get to the actual recipe, let’s chat for a sec about the ingredients. To make this pho soup recipe, you will need:", - imageSrc: 'https://user-images.githubusercontent.com/76729908/132159257-f92574c7-d00d-4142-8ea7-0ca9515fb737.png', - slug: "special-recipe-of-vietnamese-pho" -}, -{ - title: "Original Recipe of Curry", - description: "Chicken curry is common to several countries including India, countries in Asia and the Caribbean. My favorite of them though is this aromatic Indian...", - imageSrc: 'https://user-images.githubusercontent.com/76729908/132159259-ae4c986d-ab53-4758-9137-d06bafdd15d0.png', - slug:"original-recipe-of-curry" -}, -{ - title: "The Best Recipe of Beef Noodle Soup", - description: "The broth for Bun Bo Hue is prepared by slowly simmering various types of beef and pork bones (ox tail, beef shank, pork neck bones, pork feet,...", - imageSrc: 'https://user-images.githubusercontent.com/76729908/132159262-f28a9fb9-4852-47e6-80b5-d600521b548a.png', - slug:"the-best-recipe-of-beef-noodle-soup" -},]; - -const DEFAULT_PAGESIZE_RECIPELIST = 6; +const DEFAULT_PAGESIZE_RECIPELIST = 6 const BREADCRUMB = [ - { - name: 'Special Recipes', - link: `#`, - }, -]; + { + name: 'Special Recipes', + link: `#`, + }, +] const CATEGORY = [ - { - name: 'All', - link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=${OPTION_ALL}`, - }, - { - name: 'Malaysian', - link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=malaysia`, - }, - { - name: 'Vietnamese', - link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=vietnamese`, - }, - { - name: 'Thailand', - link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=thailand`, - }, - { - name: 'Indian', - link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=indian`, - }, - { - name: 'Lao', - link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=lao`, - }, - { - name: 'Chinese', - link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=chinese`, - }, - { - name: 'Korean', - link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=korean`, - }, - { - name: 'Japanese', - link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=japanese`, - }, - { - name: 'Western', - link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=western`, - }, - ]; + { + name: 'All', + link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=${OPTION_ALL}`, + }, + { + name: 'Malaysian', + link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=malaysia`, + }, + { + name: 'Vietnamese', + link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=vietnamese`, + }, + { + name: 'Thailand', + link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=thailand`, + }, + { + name: 'Indian', + link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=indian`, + }, + { + name: 'Lao', + link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=lao`, + }, + { + name: 'Chinese', + link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=chinese`, + }, + { + name: 'Korean', + link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=korean`, + }, + { + name: 'Japanese', + link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=japanese`, + }, + { + name: 'Western', + link: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=western`, + }, +] const CATEGORYSELECT = [ -{ + { name: 'All', value: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=${OPTION_ALL}`, -}, -{ + }, + { name: 'Malaysian', value: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=malaysia`, -}, -{ + }, + { name: 'Vietnamese', value: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=vietnamese`, -}, -{ + }, + { name: 'Thailand', value: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=thailand`, -}, -{ + }, + { name: 'Indian', value: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=indian`, -}, -{ + }, + { name: 'Lao', value: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=lao`, -}, -{ + }, + { name: 'Chinese', value: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=chinese`, -}, -{ + }, + { name: 'Korean', value: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=korean`, -}, -{ + }, + { name: 'Japanese', value: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=japanese`, -}, -{ + }, + { name: 'Western', value: `${ROUTE.RECIPES}/?${QUERY_KEY.RECIPES}=western`, -}, -]; - -const OPTIONSLECT=[ - { - name:"Most Viewed", - value:"most-viewed" - }, - { - name:"Lastest Blogs", - value:"lastest-blogs" - }, - { - name:"Recent Blogs", - value:"recent-blogs" - }, -]; + }, +] -interface Props{ - data?: RecipeCardProps[], - recipes?:{ - title:string, - imageSrc:string, - description:string, - slug:string - }[], +const OPTIONSLECT = [ + { + name: 'Most Viewed', + value: 'most-viewed', + }, + { + name: 'Lastest Blogs', + value: 'lastest-blogs', + }, + { + name: 'Recent Blogs', + value: 'recent-blogs', + }, +] + +interface Props { + recipeList?: RecipeCardProps[] + total: number } +const RecipesList = ({ recipeList, total }: Props) => { + const DEFAULT_BLOGS_ARGS = useMemo( + () => ({ + excludeBlogIds: [], + options:{ + take: DEFAULT_BLOG_PAGE_SIZE, + sort: { + id: 'DESC', + }, + filter:{ + isPublish: { + eq:true + } + } + } + }), + [] + ) + const router = useRouter() + const [initialQueryFlag, setInitialQueryFlag] = useState(true) + const [optionQueryBlog, setOptionQueryBlog] = useState(DEFAULT_BLOGS_ARGS) + const { reicpes, totalItems, loading } = useGetRecipeList(optionQueryBlog) + -const RecipesList = ({ data =recipe}:Props) => { - return ( - <> -
-
- -
-
- -
- -
- -
- SPECIAL RECIPES - -
-
- -
- -
-
-
- -
- -
-
-
- -
-
- {data?.map((item,index) => ( -
- -
- ))} -
-
-
- -
-
- -
- -
- + const onPageChange = (page: number) => { + router.push( + { + pathname: ROUTE.RECIPES, + query: { + ...router.query, + [QUERY_KEY.PAGE]: page, + }, + }, + undefined, + { shallow: true } ) + } + + // skip + const firstRender = useRef(true); + + useEffect(() => { + firstRender.current = false + const query = { ...DEFAULT_BLOGS_ARGS } as QueryRecipes + const page = getPageFromQuery(router.query[QUERY_KEY.PAGE] as string) + query.options.skip = page * DEFAULT_BLOG_PAGE_SIZE + setOptionQueryBlog(query) + setInitialQueryFlag(false) + }, [router.query]) + + + let data; + if(initialQueryFlag == true){ + data = recipeList; + }else{ + data = reicpes + } + + return ( + <> +
+
+ +
+
+
+ +
+ +
+ SPECIAL RECIPES + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ {(!initialQueryFlag && loading && !data) && } + {data?.map((item, index) => ( +
+ +
+ ))} +
+
+
+ +
+
+
+
+ + ) } export default RecipesList diff --git a/src/utils/types.utils.ts b/src/utils/types.utils.ts index e14a7eb59..2c2722c28 100644 --- a/src/utils/types.utils.ts +++ b/src/utils/types.utils.ts @@ -24,9 +24,14 @@ export interface RecipeProps { slug: string description: string imageSrc: string + content?: string, + imgAuthor?: string, + date?: string, + authorName?: string, } export interface BlogProps { + id:string, title: string slug: string description: string