mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 12:24:18 +00:00
feat: get-blog-detail and relevant blogs
This commit is contained in:
@@ -11,8 +11,12 @@ import type {
|
||||
} from '../types/product'
|
||||
import type {
|
||||
GetAllBlogsOperation,
|
||||
GetFeaturedOperation
|
||||
GetFeaturedOperation,
|
||||
GetAllBlogPathsOperation,
|
||||
GetBlogDetailOperation,
|
||||
GetRelevantBlogsOperation
|
||||
} from '../types/blogs'
|
||||
|
||||
import type { APIProvider, CommerceAPI } from '.'
|
||||
import { GetAllCollectionsOperation } from '@commerce/types/collection';
|
||||
|
||||
@@ -32,7 +36,10 @@ export const OPERATIONS = [
|
||||
'getAllFacets',
|
||||
'getAllCollections',
|
||||
'getAllBlogs',
|
||||
'getFeaturedBlog'
|
||||
'getFeaturedBlog',
|
||||
'getAllBlogPaths',
|
||||
'getBlogDetail',
|
||||
'getRelevantBlogs'
|
||||
] as const
|
||||
|
||||
export const defaultOperations = OPERATIONS.reduce((ops, k) => {
|
||||
@@ -133,6 +140,21 @@ export type Operations<P extends APIProvider> = {
|
||||
): Promise<T['data']>
|
||||
}
|
||||
|
||||
|
||||
getAllBlogPaths: {
|
||||
<T extends GetAllBlogPathsOperation>(opts: {
|
||||
variables?: T['variables']
|
||||
config?: P['config']
|
||||
}): Promise<T['data']>
|
||||
|
||||
<T extends GetAllBlogPathsOperation>(
|
||||
opts: {
|
||||
variables?: T['variables']
|
||||
config?: P['config']
|
||||
} & OperationOptions
|
||||
): Promise<T['data']>
|
||||
}
|
||||
|
||||
getAllProducts: {
|
||||
<T extends GetAllProductsOperation>(opts: {
|
||||
variables?: T['variables']
|
||||
@@ -166,6 +188,22 @@ export type Operations<P extends APIProvider> = {
|
||||
): Promise<T['data']>
|
||||
}
|
||||
|
||||
getRelevantBlogs: {
|
||||
<T extends GetRelevantBlogsOperation>(opts: {
|
||||
variables?: T['variables']
|
||||
config?: P['config']
|
||||
preview?: boolean
|
||||
}): Promise<T['data']>
|
||||
|
||||
<T extends GetRelevantBlogsOperation>(
|
||||
opts: {
|
||||
variables?: T['variables']
|
||||
config?: P['config']
|
||||
preview?: boolean
|
||||
} & OperationOptions
|
||||
): Promise<T['data']>
|
||||
}
|
||||
|
||||
getFeaturedBlog: {
|
||||
<T extends GetAllBlogsOperation>(opts: {
|
||||
variables?: T['variables']
|
||||
@@ -182,6 +220,21 @@ export type Operations<P extends APIProvider> = {
|
||||
): Promise<T['data']>
|
||||
}
|
||||
|
||||
getBlogDetail: {
|
||||
<T extends GetBlogDetailOperation>(opts: {
|
||||
variables?: T['variables']
|
||||
config?: P['config']
|
||||
preview?: boolean
|
||||
}): Promise<T['data']>
|
||||
|
||||
<T extends GetBlogDetailOperation>(
|
||||
opts: {
|
||||
variables?: T['variables']
|
||||
config?: P['config']
|
||||
preview?: boolean
|
||||
} & OperationOptions
|
||||
): Promise<T['data']>
|
||||
}
|
||||
|
||||
|
||||
getProduct: {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { SearchResultSortParameter } from "@framework/schema";
|
||||
|
||||
import { Asset, BlogTranslation, Maybe, Product } from './../../vendure/schema.d';
|
||||
|
||||
export type BlogList = Node &{
|
||||
@@ -15,13 +15,27 @@ export type BlogsType = {
|
||||
totalItems: number
|
||||
}
|
||||
export type GetAllBlogsOperation<T extends BlogsType = BlogsType> = {
|
||||
data: { items: T['items'][] }
|
||||
data: { items: T['items'][], totalItems: number }
|
||||
variables: {
|
||||
productId: number,
|
||||
take?: number
|
||||
skip?: number
|
||||
}
|
||||
}
|
||||
export type GetRelevantBlogsOperation<T extends BlogsType = BlogsType> = {
|
||||
data: { items: T['items'][], totalItems: number }
|
||||
variables: {
|
||||
take?: number
|
||||
skip?: number
|
||||
}
|
||||
}
|
||||
|
||||
export type GetBlogDetailOperation<T extends BlogsType = BlogsType> = {
|
||||
data: T['items'],
|
||||
variables: {
|
||||
slug?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type GetFeaturedOperation<T extends BlogsType = BlogsType> = {
|
||||
data: { items: T['items'][] }
|
||||
@@ -29,4 +43,10 @@ export type GetFeaturedOperation<T extends BlogsType = BlogsType> = {
|
||||
take?: number
|
||||
skip?: number
|
||||
}
|
||||
}
|
||||
}
|
||||
export type GetAllBlogPathsOperation<
|
||||
T extends BlogsType = BlogsType
|
||||
> = {
|
||||
data: { blogs: Pick<T['items'], 'translations'>[] }
|
||||
variables: { first?: number }
|
||||
}
|
||||
|
@@ -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<T extends ProductTypes = ProductTypes> = {
|
||||
data: { products: T['product'][] }
|
||||
variables: {
|
||||
|
@@ -1,18 +1,21 @@
|
||||
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 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 +49,10 @@ const operations = {
|
||||
getAllFacets,
|
||||
getAllCollections,
|
||||
getAllBlogs,
|
||||
getFeaturedBlog
|
||||
getFeaturedBlog,
|
||||
getBlogDetail,
|
||||
getAllBlogPaths,
|
||||
getRelevantBlogs
|
||||
}
|
||||
|
||||
export const provider = { config, operations }
|
||||
|
53
framework/vendure/api/operations/get-all-blog-paths.ts
Normal file
53
framework/vendure/api/operations/get-all-blog-paths.ts
Normal file
@@ -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<Provider>) {
|
||||
async function getAllBlogPaths<
|
||||
T extends GetAllBlogPathsOperation
|
||||
>(opts?: {
|
||||
variables?: T['variables']
|
||||
config?: BigcommerceConfig
|
||||
}): Promise<T['data']>
|
||||
|
||||
async function getAllBlogPaths<T extends GetAllBlogPathsOperation>(
|
||||
opts: {
|
||||
variables?: T['variables']
|
||||
config?: BigcommerceConfig
|
||||
} & OperationOptions
|
||||
): Promise<T['data']>
|
||||
|
||||
async function getAllBlogPaths<T extends GetAllBlogPathsOperation>({
|
||||
query = getAllBlogPathsQuery,
|
||||
variables,
|
||||
config: cfg,
|
||||
}: {
|
||||
query?: string
|
||||
variables?: T['variables']
|
||||
config?: BigcommerceConfig
|
||||
} = {}): Promise<T['data']> {
|
||||
const config = commerce.getConfig(cfg)
|
||||
|
||||
const { data } = await config.fetch<GetAllBlogPathsQuery>(query, {
|
||||
variables,
|
||||
})
|
||||
|
||||
const blogs = data.blogs.items;
|
||||
|
||||
return {
|
||||
blogs: blogs?.map(val=>val.translations.map((p:BlogTranslation) => ({ path: `/${p.slug}` })))
|
||||
}
|
||||
}
|
||||
|
||||
return getAllBlogPaths
|
||||
}
|
55
framework/vendure/api/operations/get-blog-detail.ts
Normal file
55
framework/vendure/api/operations/get-blog-detail.ts
Normal file
@@ -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<Provider>) {
|
||||
async function getBlogDetail(opts?: {
|
||||
variables?: BlogVariables
|
||||
config?: Partial<VendureConfig>
|
||||
preview?: boolean
|
||||
}): Promise<{ blogDetail: BlogList}>
|
||||
|
||||
async function getBlogDetail({
|
||||
query = getBlogDetailQuery,
|
||||
variables: { ...vars } = {},
|
||||
config: cfg,
|
||||
}: {
|
||||
query?: string
|
||||
variables?: BlogVariables
|
||||
config?: Partial<VendureConfig>
|
||||
preview?: boolean
|
||||
} = {}): Promise<{ blogDetail: BlogList | any }> {
|
||||
|
||||
const config = commerce.getConfig(cfg)
|
||||
const variables = {
|
||||
slug: vars.slug
|
||||
}
|
||||
const { data } = await config.fetch<GetBlogQuery>(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
|
||||
}
|
54
framework/vendure/api/operations/get-relevant-blogs.ts
Normal file
54
framework/vendure/api/operations/get-relevant-blogs.ts
Normal file
@@ -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<Provider>) {
|
||||
async function getRelevantBlogs(opts?: {
|
||||
variables?: BlogVariables
|
||||
config?: Partial<VendureConfig>
|
||||
preview?: boolean
|
||||
}): Promise<{ relevantBlogs: GetRelevantBlogsQuery[]}>
|
||||
|
||||
async function getRelevantBlogs({
|
||||
query = getRelevantBlogsQuery,
|
||||
variables: { ...vars } = {},
|
||||
config: cfg,
|
||||
}: {
|
||||
query?: string
|
||||
variables?: BlogVariables
|
||||
config?: Partial<VendureConfig>
|
||||
preview?: boolean
|
||||
} = {}): Promise<{ relevantBlogs: GetRelevantBlogsQuery[] | any[] }> {
|
||||
|
||||
const config = commerce.getConfig(cfg)
|
||||
const variables = {
|
||||
productId: vars.productId,
|
||||
}
|
||||
const { data } = await config.fetch<GetRelevantBlogsQuery>(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
|
||||
}
|
25
framework/vendure/schema.d.ts
vendored
25
framework/vendure/schema.d.ts
vendored
@@ -2337,10 +2337,19 @@ export type BlogList = Node &{
|
||||
translations: Array<BlogTranslation>
|
||||
authorName: Scalars['String']
|
||||
authorAvatarAsset:Asset
|
||||
relevantProducts: Product
|
||||
relevantProducts: Product[]
|
||||
isFeatured: Boolean
|
||||
}
|
||||
|
||||
export type GetBlogQuery = { __typename?: 'Query' } & {
|
||||
blog?: Maybe<
|
||||
{ __typename?: 'Blog' } & BlogList
|
||||
>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export type BlogTranslation = {
|
||||
__typename?: 'BlogTranslation'
|
||||
id: Scalars['ID']
|
||||
@@ -2359,6 +2368,13 @@ export type GetAllBlogsQuery = PaginatedList & {
|
||||
'totalItems'
|
||||
}
|
||||
}
|
||||
|
||||
export type GetRelevantBlogsQuery = PaginatedList & {
|
||||
relevantBlogs: { __typename?: 'BlogList' } & {
|
||||
items: Array<{ __typename?: 'Blog' } & BlogList!>,
|
||||
}
|
||||
}
|
||||
|
||||
export type GetFeaturedBlogQuery = PaginatedList & {
|
||||
id:string,
|
||||
featuredBlogs: { __typename?: 'BlogList' } & {
|
||||
@@ -2367,6 +2383,7 @@ export type GetFeaturedBlogQuery = PaginatedList & {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export type QueryBlogs = {
|
||||
excludeBlogIds:Array,
|
||||
options: BlogListOptions
|
||||
@@ -3339,6 +3356,12 @@ export type GetAllProductPathsQuery = { __typename?: 'Query' } & {
|
||||
items: Array<{ __typename?: 'Product' } & Pick<Product, 'slug'>>
|
||||
}
|
||||
}
|
||||
|
||||
export type GetAllBlogPathsQuery = { __typename?: 'Query' } & {
|
||||
blogs: { __typename?: 'BlogList' } & {
|
||||
items: Array<{ __typename?: 'Blog' } & Pick<BlogList,'slug','translations'>>
|
||||
}
|
||||
}
|
||||
|
||||
export type GetAllProductsQueryVariables = Exact<{
|
||||
input: SearchInput
|
||||
|
11
framework/vendure/utils/queries/get-all-blog-paths-query.ts
Normal file
11
framework/vendure/utils/queries/get-all-blog-paths-query.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export const getAllBlogPathsQuery = /* GraphQL */ `
|
||||
query getAllBlogPaths($excludeBlogIds: [ID]! = [],$options:BlogListOptions){
|
||||
blogs(excludeBlogIds: $excludeBlogIds,options: $options){
|
||||
items{
|
||||
translations {
|
||||
slug
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
26
framework/vendure/utils/queries/get-blog-detail.ts
Normal file
26
framework/vendure/utils/queries/get-blog-detail.ts
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
25
framework/vendure/utils/queries/get-relevant-blogs.ts
Normal file
25
framework/vendure/utils/queries/get-relevant-blogs.ts
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
@@ -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},
|
||||
relevantBlogs:{blogDetail?:BlogCardProps[]}
|
||||
}
|
||||
export default function BlogDetailPage({blog,relevantBlogs}: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 (
|
||||
<>
|
||||
<BlogDetailImg/>
|
||||
<BlogContent/>
|
||||
<RelevantBlogPosts data={BLOGS_DATA_TEST} title="You will like also" bgcolor="cream"/>
|
||||
<BlogDetailImg imgSrc={blog?.blogDetail?.imageSrc ?? ''} />
|
||||
<BlogContent
|
||||
title={blog?.blogDetail?.title}
|
||||
content={blog?.blogDetail?.description}
|
||||
imgAuthor={blog?.blogDetail?.authorAvatarAsset}
|
||||
authorName={blog?.blogDetail?.authorName}
|
||||
date={fullDate}
|
||||
/>
|
||||
{relevantBlogs.relevantBlogs?.length> 0 && <RelevantBlogPosts data={relevantBlogs.relevantBlogs} title="You will like also" bgcolor="cream"/>}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
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: 'relevantBlogs', 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<string[]>((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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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 (
|
||||
<div className={classNames({
|
||||
[s.blogPostWarpper] : true,
|
||||
@@ -63,7 +30,7 @@ const recipe:BlogCardProps[] = [
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.bot}>
|
||||
<BlogPostCarousel data={data} itemKey={itemKey} />
|
||||
<BlogPostCarousel data={data} itemKey={itemKey} />}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@@ -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 (
|
||||
<>
|
||||
<div className={s.blogContentWrapper}>
|
||||
<DateTime date="APRIL 30, 2021"/>
|
||||
<h1>The Best Sesame Soy Broccoli Salad</h1>
|
||||
<DateTime date={date}/>
|
||||
<h1>{title}</h1>
|
||||
<div className={s.author}>
|
||||
<Author image={imageAuthor.src} name="Alessandro Del Piero" />
|
||||
<Author image={imgAuthor} name={authorName} />
|
||||
</div>
|
||||
<section className={s.content}>
|
||||
|
||||
<p> 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.
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
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.
|
||||
<br/>
|
||||
<br/>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<h2 className={s.heading2}>What is broccoli salad</h2>
|
||||
<br/>
|
||||
<p> 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.
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
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.
|
||||
<br/>
|
||||
<br/>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<h2 className={s.heading2}>What about broccoli stems?</h2>
|
||||
<br/>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<br/>
|
||||
|
||||
<figure>
|
||||
<ImgWithLink src="https://user-images.githubusercontent.com/89437339/133046625-bdf9cc0d-6f22-43e5-a49d-d4d34df19cf2.jpg" alt="blog-detail" />
|
||||
</figure>
|
||||
|
||||
{content}
|
||||
</section>
|
||||
|
||||
<div className={s.boxShare}>
|
||||
|
@@ -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 (
|
||||
<>
|
||||
<div className={s.beadcrumb}>
|
||||
<BreadcrumbCommon crumbs={CRUMBS} />
|
||||
</div>
|
||||
<div className={s.image}>
|
||||
<ImgWithLink src="https://user-images.githubusercontent.com/89437339/133044532-8b5f9442-841b-4187-84b4-d6cc66676b52.png" alt="Ảnh đại diện"/>
|
||||
<ImgWithLink src={imgSrc} alt="avatar"/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
Reference in New Issue
Block a user