Merge pull request #88 from KieIO/feature/m4-get-blog-list

Feature/m4 get blog list
This commit is contained in:
Quangnhankie
2021-10-21 16:11:35 +07:00
committed by GitHub
26 changed files with 570 additions and 168 deletions

View File

@@ -9,6 +9,10 @@ import type {
GetAllProductsOperation,
GetProductOperation,
} from '../types/product'
import type {
GetAllBlogsOperation,
GetFeaturedBlogsOperation
} from '../types/blogs'
import type { APIProvider, CommerceAPI } from '.'
import { GetAllCollectionsOperation } from '@commerce/types/collection';
@@ -27,7 +31,8 @@ export const OPERATIONS = [
'getProduct',
'getAllFacets',
'getAllCollections',
'getAllBlogs',
'getFeaturedBlog'
] as const
export const defaultOperations = OPERATIONS.reduce((ops, k) => {
@@ -144,6 +149,41 @@ export type Operations<P extends APIProvider> = {
): Promise<T['data']>
}
getAllBlogs: {
<T extends GetAllBlogsOperation>(opts: {
variables?: T['variables']
config?: P['config']
preview?: boolean
}): Promise<T['data']>
<T extends GetAllBlogsOperation>(
opts: {
variables?: T['variables']
config?: P['config']
preview?: boolean
} & OperationOptions
): Promise<T['data']>
}
getFeaturedBlog: {
<T extends GetFeaturedBlogsOperation>(opts: {
variables?: T['variables']
config?: P['config']
preview?: boolean
}): Promise<T['data']>
<T extends GetFeaturedBlogsOperation>(
opts: {
variables?: T['variables']
config?: P['config']
preview?: boolean
} & OperationOptions
): Promise<T['data']>
}
getProduct: {
<T extends GetProductOperation>(opts: {
variables: T['variables']

View File

@@ -0,0 +1,31 @@
import { Asset, BlogTranslation, Maybe, Product } from './../../vendure/schema.d';
export type BlogList = Node &{
id: string
featuredAsset?: Maybe<Asset>
isPublic:Boolean
translations: BlogTranslation[]
authorName: string
authorAvatarAsset:Asset[]
relevantProducts: Product
}
export type BlogsType = {
items: BlogList
totalItems: number
}
export type GetAllBlogsOperation<T extends BlogsType = BlogsType> = {
data: { items: T['items'][] }
variables: {
take?: number
skip?: number
}
}
export type GetFeaturedBlogsOperation<T extends BlogsType = BlogsType> = {
data: { items: T['items'][] }
variables: {
take?: number
skip?: number
}
}

View File

@@ -11,7 +11,8 @@ 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'
import getAllBlogs from './operations/get-all-blogs'
import getFeaturedBlog from './operations/get-featured-blog'
export interface VendureConfig extends CommerceAPIConfig {}
@@ -44,6 +45,8 @@ const operations = {
getProduct,
getAllFacets,
getAllCollections,
getAllBlogs,
getFeaturedBlog
}
export const provider = { config, operations }

View File

@@ -0,0 +1,61 @@
import { OperationContext } from '@commerce/api/operations'
import { Provider, VendureConfig } from '..'
import { GetAllBlogsQuery,BlogList } from '../../schema'
import { getAllBlogsQuery } from '../../utils/queries/get-all-blog-query'
export type BlogVariables = {
excludeBlogIds?: string[],
take?: number,
skip?:number
}
export default function getAllBlogsOperation({
commerce,
}: OperationContext<Provider>) {
async function getAllBlogs(opts?: {
variables?: BlogVariables
config?: Partial<VendureConfig>
preview?: boolean
}): Promise<{ blogs: GetAllBlogsQuery[],totalItems:number }>
async function getAllBlogs({
query = getAllBlogsQuery,
variables: { ...vars } = {},
config: cfg,
}: {
query?: string
variables?: BlogVariables
config?: Partial<VendureConfig>
preview?: boolean
} = {}): Promise<{ blogs: GetAllBlogsQuery[] | any[] ,totalItems?:number }> {
const config = commerce.getConfig(cfg)
const variables = {
excludeBlogIds: vars.excludeBlogIds,
options: {
take: vars.take,
skip: vars.skip,
},
}
const { data } = await config.fetch<GetAllBlogsQuery>(query, {
variables,
})
return {
blogs: 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,
isFeatured: val.isFeatured,
authorName: val.authorName,
authorAvatarAsset : val.authorAvatarAsset?.preview,
createdAt: val.createdAt
})),
totalItems: data?.blogs?.totalItems || null
}
}
return getAllBlogs
}

View File

@@ -0,0 +1,56 @@
import { OperationContext } from '@commerce/api/operations'
import { Provider, VendureConfig } from '..'
import { GetFeaturedBlogQuery,BlogList } from '../../schema'
import { getFeatuedBlogsQuery } from '../../utils/queries/get-featued-query'
export type BlogVariables = {
take?: number,
skip?:number
}
export default function getFeaturedBlogOperation({
commerce,
}: OperationContext<Provider>) {
async function getFeaturedBlog(opts?: {
variables?: BlogVariables
config?: Partial<VendureConfig>
preview?: boolean
}): Promise<{ featuredBlogs: GetFeaturedBlogQuery[],totalItems:number }>
async function getFeaturedBlog({
query = getFeatuedBlogsQuery,
variables: { ...vars } = {},
config: cfg,
}: {
query?: string
variables?: BlogVariables
config?: Partial<VendureConfig>
preview?: boolean
} = {}): Promise<{ featuredBlogs: GetFeaturedBlogQuery[] | any[] ,totalItems?:number }> {
const config = commerce.getConfig(cfg)
const variables = {
options: {
take: vars.take,
},
}
const { data } = await config.fetch<GetFeaturedBlogQuery>(query, {
variables,
})
return {
featuredBlogs: data?.featuredBlogs?.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 getFeaturedBlog
}

View File

@@ -2371,6 +2371,55 @@ export type Product = Node & {
customFields?: Maybe<Scalars['JSON']>
}
export type BlogList = Node &{
id: ID!
createdAt: DateTime!
updatedAt: DateTime!
featuredAsset?: Maybe<Asset>
isPublish:Boolean
translations: Array<BlogTranslation>
authorName: Scalars['String']
authorAvatarAsset:Asset
relevantProducts: Product
isFeatured: Boolean
}
export type BlogTranslation = {
__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' } & {
items: Array<{ __typename?: 'Blog' } & BlogList!>,
'totalItems'
}
}
export type GetFeaturedBlogQuery = PaginatedList & {
id:string,
featuredBlogs: { __typename?: 'BlogList' } & {
items: Array<{ __typename?: 'Blog' } & BlogList!>,
'totalItems'
}
}
export type QueryBlogs = {
excludeBlogIds:Array,
options: BlogListOptions
}
export type BlogListOptions = {
skip?: Maybe<Scalars['Int']>
take?: Maybe<Scalars['Int']>
}
export type ProductTranslation = {
__typename?: 'ProductTranslation'
id: Scalars['ID']
@@ -3485,3 +3534,4 @@ export type SearchQuery = { __typename?: 'Query' } & {
'totalItems'
> & { items: Array<{ __typename?: 'SearchResult' } & SearchResultFragment> }
}

View File

@@ -1,6 +1,6 @@
import { Cart } from '@commerce/types/cart'
import { ProductCard, Product } from '@commerce/types/product'
import { CartFragment, SearchResultFragment,Favorite } from '../schema'
import { CartFragment, SearchResultFragment,Favorite, BlogList } from '../schema'
export function normalizeSearchResult(item: SearchResultFragment): ProductCard {
return {
@@ -83,4 +83,19 @@ export function normalizeProductCard(product: Product): ProductCard {
facetValueIds: product.facetValueIds,
collectionIds: product.collectionIds,
}
}
export function normalizeBlogList(blog: BlogList) {
return {
id: blog.id,
title: blog.translations[0]?.title,
imageSrc: blog.featuredAsset?.preview ?? null,
slug: blog.translations[0]?.slug,
description: blog.translations[0]?.description,
isPublish: blog.isPublish,
isFeatured:blog.isFeatured,
authorName: blog.authorName,
authorAvatarAsset : blog.authorAvatarAsset?.preview,
createdAt: blog.createdAt
}
}

View File

@@ -0,0 +1,26 @@
export const getAllBlogsQuery = /* GraphQL */ `
query GetBlogs($excludeBlogIds: [ID]!, $options: BlogListOptions) {
blogs(excludeBlogIds: $excludeBlogIds, options: $options) {
totalItems
items {
id
isPublish
isFeatured
authorName
createdAt
authorAvatarAsset{
preview
}
featuredAsset {
preview
}
translations {
title
slug
description
content
}
}
}
}
`

View File

@@ -0,0 +1,25 @@
export const getFeatuedBlogsQuery = /* GraphQL */ `
query GetFeaturedBlogs($options: BlogListOptions) {
featuredBlogs( options: $options){
items {
id
isPublish
isFeatured
authorName
createdAt
authorAvatarAsset{
preview
}
featuredAsset {
preview
}
translations {
title
slug
description
content
}
}
}
}
`

3
next-env.d.ts vendored
View File

@@ -1,6 +1,3 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@@ -1,14 +1,91 @@
import commerce from '@lib/api/commerce';
import { GetStaticPropsContext } from 'next';
import { Layout } from 'src/components/common';
import { BlogsList, FeaturedCardBlog, BlogHeading, BlogBreadCrumb } from 'src/components/modules/blogs';
import { BlogCardProps } from 'src/components/common/CardBlog/CardBlog';
import { BlogBreadCrumb, BlogHeading, BlogsList, FeaturedCardBlog } from 'src/components/modules/blogs';
import { DEFAULT_BLOG_PAGE_SIZE } from "src/utils/constanst.utils";
import { getAllPromies } from 'src/utils/funtion.utils';
import { PromiseWithKey } from 'src/utils/types.utils';
export default function BlogsPage() {
interface Props {
blogs?: BlogCardProps[],
featuredBlog?: BlogCardProps[],
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();
return(
<>
<BlogBreadCrumb />
<BlogHeading />
<FeaturedCardBlog />
<BlogsList />
<FeaturedCardBlog
title={featuredBlog?.[0]?.title}
slug={featuredBlog?.[0]?.slug}
imgSrc={featuredBlog?.[0]?.imageSrc ?? ''}
content={featuredBlog?.[0]?.description}
imgAuthor={featuredBlog?.[0]?.authorAvatarAsset}
authorName={featuredBlog?.[0]?.authorName}
date={fullDate}
/>
<BlogsList blogList={blogs} total={totalItems} idFeatured={featuredBlog?.[0]?.id} />
</>
)
}
BlogsPage.Layout = Layout
export async function getStaticProps({
preview,
locale,
locales,
}: GetStaticPropsContext) {
const config = { locale, locales }
let promisesWithKey = [] as PromiseWithKey[]
let props = {} as any;
const {featuredBlogs} = await commerce.getFeaturedBlog({
variables: {
take: 1
},
config,
preview,
})
// Blogs
const idFeaturedBlog = featuredBlogs?.[0]?.id;
const blogsPromise = commerce.getAllBlogs({
variables: {
excludeBlogIds: [idFeaturedBlog],
take: DEFAULT_BLOG_PAGE_SIZE
},
config,
preview,
})
promisesWithKey.push({ key: 'blogsResult', promise: blogsPromise })
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
})
props.featuredBlog = featuredBlogs;
console.log(props)
return {
props,
revalidate: 60
}
} catch (err) {
}
}
BlogsPage.Layout = Layout

View File

@@ -12,6 +12,7 @@ import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED,COLLECTION_SLUG_SPICE } from '
import { getAllFacetValueIdsByParentCode, getAllFacetValuesForFeatuedProducts, getAllPromies, getFreshFacetId } from 'src/utils/funtion.utils';
import { PromiseWithKey } from 'src/utils/types.utils';
interface Props {
featuredAndDiscountFacetsValue: FacetValue[],
freshProducts: ProductCard[],
@@ -63,7 +64,7 @@ export async function getStaticProps({
props.featuredAndDiscountFacetsValue = getAllFacetValuesForFeatuedProducts(facets)
// fresh products
const freshProductvariables: ProductVariables = {}
const freshFacetId = getFreshFacetId(facets)

View File

@@ -6,17 +6,20 @@ import { ImgWithLink } from '..'
import s from './CardBlog.module.scss'
export interface BlogCardProps extends BlogProps {
// todo: edit when intergrate API
isPublish?:Boolean,
isFeatured?:Boolean,
authorAvatarAsset?:string,
authorName?:string,
createdAt?:string
}
const CardBlog = ({ imageSrc, title, description, slug }: BlogCardProps) => {
return (
<div className={s.cardBlogWarpper}>
<Link href={`${ROUTE.BLOG_DETAIL}/${slug}`}>
<a>
<a>
<div className={s.image}>
<ImgWithLink src={imageSrc} alt="image cardblog" />
<ImgWithLink src={imageSrc ?? ''} alt={title} />
</div>
</a>
</Link>

View File

@@ -0,0 +1,18 @@
.listBlogCardSkeleton {
display: flex;
overflow: hidden;
width: 90%;
justify-content: space-between;
div{
min-width: 32rem;
}
&.wrap {
flex-wrap: wrap;
overflow: unset;
> div {
margin-bottom: 1.6rem;
}
}
}

View File

@@ -0,0 +1,20 @@
import classNames from 'classnames'
import { ProductCardSkeleton } from '..'
import s from './ListBlogCardSkeleton.module.scss'
type Props = {
count?: number
isWrap?: boolean,
}
const ListBlogCardSkeleton = ({ count = 3, isWrap }: Props) => {
return (
<div className={classNames(s.listBlogCardSkeleton, { [s.wrap]: isWrap })}>
{
Array.from(Array(count).keys()).map(item => <ProductCardSkeleton key={item} />)
}
</div>
)
}
export default ListBlogCardSkeleton

View File

@@ -1,7 +1,7 @@
.listProductCardSkeleton {
display: flex;
overflow: hidden;
&.wrap {
flex-wrap: wrap;
overflow: unset;
@@ -9,4 +9,11 @@
margin-bottom: 1.6rem;
}
}
&.isBlog{
width: 90%;
justify-content: space-between;
div{
min-width: 32rem;
}
}
}

View File

@@ -4,15 +4,14 @@ import s from './ListProductCardSkeleton.module.scss'
type Props = {
count?: number
isWrap?: boolean
isWrap?: boolean,
}
const ListProductCardSkeleton = ({ count = 5, isWrap }: Props) => {
const ListProductCardSkeleton = ({ count = 3, isWrap }: Props) => {
return (
<div className={classNames(s.listProductCardSkeleton, { [s.wrap]: isWrap })}>
{
Array.from(Array(count).keys()).map(item => <ProductCardSkeleton key={item} />)
}
</div>
)

View File

@@ -1,14 +1,16 @@
import SkeletonImage from "../SkeletonCommon/SkeletonImage/SkeletonImage"
import SkeletonParagraph from "../SkeletonCommon/SkeletonParagraph/SkeletonParagraph"
import s from './ProductCardSkeleton.module.scss'
const ProductCardSkeleton = ({ }) => {
type Props = {
isBlog?:boolean
}
const ProductCardSkeleton = ({isBlog=false }) => {
return (
<div className={s.productCardSkeleton}>
<SkeletonImage />
<div className={s.content}>
<SkeletonParagraph rows={3} />
<SkeletonParagraph rows={isBlog ? 2 : 3} />
</div>
</div>
)

View File

@@ -55,4 +55,5 @@ export { default as FormForgot} from './ForgotPassword/FormForgot/FormForgot'
export { default as FormResetPassword} from './ForgotPassword/FormResetPassword/FormResetPassword'
export { default as ProductCardSkeleton} from './ProductCardSkeleton/ProductCardSkeleton'
export { default as ListProductCardSkeleton} from './ListProductCardSkeleton/ListProductCardSkeleton'
export { default as ListBlogCardSkeleton} from './ListBlogCardSkeleton/ListBlogCardSkeleton'

View File

@@ -0,0 +1 @@
export {default as useGetBlogList }from "./useGetBlogList"

View File

@@ -0,0 +1,18 @@
import { GetAllBlogsQuery, QueryBlogs,BlogList } from '@framework/schema'
import { normalizeBlogList } 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 useGetBlogList = (options?: QueryBlogs) => {
const { data, isValidating, ...rest } = useSWR<GetAllBlogsQuery>([getAllBlogsQuery, options], gglFetcher)
return {
blogs: data?.blogs?.items?.map((blog:BlogList)=>normalizeBlogList(blog)),
totalItems: data?.blogs?.totalItems || null,
loading: isValidating,
...rest
}
}
export default useGetBlogList

View File

@@ -1,151 +1,96 @@
import React, { useState } from 'react'
import { useRouter } from 'next/router'
import React, { useEffect, useState,useRef, useMemo } from 'react'
import CardBlog, { BlogCardProps } from 'src/components/common/CardBlog/CardBlog'
import PaginationCommon from 'src/components/common/PaginationCommon/PaginationCommon'
import { DEFAULT_BLOG_PAGE_SIZE, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'
import s from "./BlogsList.module.scss"
import { DEFAULT_BLOG_PAGE_SIZE } from 'src/utils/constanst.utils'
import { QueryBlogs } from '@framework/schema'
import { useGetBlogList } from 'src/components/hooks/blog'
import { getPageFromQuery } from 'src/utils/funtion.utils'
import { ListBlogCardSkeleton } from 'src/components/common'
interface BlogsListProps {
data?: BlogCardProps[],
blogList?: BlogCardProps[],
total?: number,
idFeatured?:string
}
const BLOGSLIST_DATA = [
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133185783-8100ef4e-7a72-4dc1-bb12-2ca46b56b393.png",
title: "1",
description: "The DEBM diet stands for "+"Delicious Happy Fun Diet"+". This diet was popularized by Robert...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133185911-df505d10-fdcd-4312-add3-7c62ad8af71e.png",
title: "2",
description: "Aloe vera or aloe vera is a green plant, has thorns on the side of the skin with yellowish patches and...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133185959-7ad75580-ca6d-4684-83d9-3f64500bbc97.png",
title: "3",
description: "Dragon fruit is a type of fruit that is a favorite for many people because of its delicious and fresh...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133186410-d8718d90-82fb-46cb-a0f2-0ec96356ae89.png",
title: "4",
description: "The DEBM diet stands for "+"Delicious Happy Fun Diet"+". This diet was popularized by Robert...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133186474-b2d89bbc-32ed-4174-a05e-3d388c0a39ff.png",
title: "5",
description: "Aloe vera or aloe vera is a green plant, has thorns on the side of the skin with yellowish patches and...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133186545-d860f4ee-222c-4d72-a876-808af0f397a0.png",
title: "6",
description: "Dragon fruit is a type of fruit that is a favorite for many people because of its delicious and fresh...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133185783-8100ef4e-7a72-4dc1-bb12-2ca46b56b393.png",
title: "7",
description: "The DEBM diet stands for "+"Delicious Happy Fun Diet"+". This diet was popularized by Robert...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133185911-df505d10-fdcd-4312-add3-7c62ad8af71e.png",
title: "8",
description: "Aloe vera or aloe vera is a green plant, has thorns on the side of the skin with yellowish patches and...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133185959-7ad75580-ca6d-4684-83d9-3f64500bbc97.png",
title: "9",
description: "Dragon fruit is a type of fruit that is a favorite for many people because of its delicious and fresh...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133186545-d860f4ee-222c-4d72-a876-808af0f397a0.png",
title: "10",
description: "Dragon fruit is a type of fruit that is a favorite for many people because of its delicious and fresh...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133186410-d8718d90-82fb-46cb-a0f2-0ec96356ae89.png",
title: "11",
description: "The DEBM diet stands for "+"Delicious Happy Fun Diet"+". This diet was popularized by Robert...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133186474-b2d89bbc-32ed-4174-a05e-3d388c0a39ff.png",
title: "12",
description: "Aloe vera or aloe vera is a green plant, has thorns on the side of the skin with yellowish patches and...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133185783-8100ef4e-7a72-4dc1-bb12-2ca46b56b393.png",
title: "13",
description: "The DEBM diet stands for "+"Delicious Happy Fun Diet"+". This diet was popularized by Robert...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133185911-df505d10-fdcd-4312-add3-7c62ad8af71e.png",
title: "14",
description: "Aloe vera or aloe vera is a green plant, has thorns on the side of the skin with yellowish patches and...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133185959-7ad75580-ca6d-4684-83d9-3f64500bbc97.png",
title: "15",
description: "Dragon fruit is a type of fruit that is a favorite for many people because of its delicious and fresh...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133186410-d8718d90-82fb-46cb-a0f2-0ec96356ae89.png",
title: "16",
description: "The DEBM diet stands for "+"Delicious Happy Fun Diet"+". This diet was popularized by Robert...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133186545-d860f4ee-222c-4d72-a876-808af0f397a0.png",
title: "17",
description: "Dragon fruit is a type of fruit that is a favorite for many people because of its delicious and fresh...",
slug: "happy-diet"
},
{
imageSrc: "https://user-images.githubusercontent.com/46085455/133186474-b2d89bbc-32ed-4174-a05e-3d388c0a39ff.png",
title: "18",
description: "Aloe vera or aloe vera is a green plant, has thorns on the side of the skin with yellowish patches and...",
slug: "happy-diet"
},
]
const BlogsList = ({ data = BLOGSLIST_DATA }:BlogsListProps) => {
const [currentPage, setCurrentPage] = useState(0)
const BlogsList = ({ blogList,total,idFeatured }:BlogsListProps) => {
const DEFAULT_BLOGS_ARGS = useMemo(()=> ({
excludeBlogIds: [idFeatured],
options:{
skip: 1, take: DEFAULT_BLOG_PAGE_SIZE
}
}),[idFeatured]);
const router = useRouter();
const [initialQueryFlag, setInitialQueryFlag] = useState<boolean>(true)
const [optionQueryBlog, setOptionQueryBlog] = useState<QueryBlogs>(DEFAULT_BLOGS_ARGS)
const { blogs, totalItems, loading } = useGetBlogList(optionQueryBlog);
const onPageChange = (page:number) => {
setCurrentPage(page)
router.push({
pathname: ROUTE.BLOGS,
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 QueryBlogs;
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 = blogList;
}else{
data = blogs
}
return (
<section>
<div className={s.wrapper}>
{(!initialQueryFlag && loading && !blogs) && <ListBlogCardSkeleton count={DEFAULT_BLOG_PAGE_SIZE} isWrap />}
<div className={s.list}>
{
data.slice(currentPage*DEFAULT_BLOG_PAGE_SIZE,(currentPage+1)*DEFAULT_BLOG_PAGE_SIZE).map((product,index)=> {
return(
data?.map((product,index)=> {
return(
<div className={s.card} key={`${product.title}-${index}`}>
<CardBlog {...product} />
{product.isPublish && <CardBlog {...product} /> }
</div>
)
})
}
</div>
<div className={s.pagination}>
<PaginationCommon total={data.length} pageSize={DEFAULT_BLOG_PAGE_SIZE} onChange={onPageChange}/>
<PaginationCommon total={totalItems !== undefined ? totalItems : total} pageSize={DEFAULT_BLOG_PAGE_SIZE} onChange={onPageChange}/>
</div>
</div>
</section>
)
}
export default BlogsList
export default BlogsList

View File

@@ -1,8 +1,10 @@
import s from './FeaturedCardBlog.module.scss'
import { Author, DateTime, ImgWithLink } from 'src/components/common'
import Link from 'next/link'
import { ROUTE } from 'src/utils/constanst.utils'
interface FeaturedCardBlogProps{
title?: string,
slug?:string,
content?: string,
imgSrc?: string,
imgAuthor?: string,
@@ -10,33 +12,31 @@ interface FeaturedCardBlogProps{
authorName?: string,
}
const FEATURED_DATA = {
title: "Flammekueche with green asparagus",
content: "Traditionally, the Flammekueche is made with rapeseed oil, which, contrary to popular belief, is indeed an oil that can be cooked hot and is not limited to seasoning. It is important to vary the oils in the kitchen to take advantage of the benefits of each. Rapeseed oil is an oil rich in omega 3 which participate in the proper functioning of the cardiovascular system as well as in vitamins E which contributes to the protection of cells against oxidative stress. In short, oils are your friends 😉",
imgSrc: "https://user-images.githubusercontent.com/46085455/133186666-1ea8081f-4319-4617-8644-d20ed14b1825.png",
imgAuthor: "https://user-images.githubusercontent.com/46085455/133186783-d0c71d43-b7bc-44b6-b560-818c71bd162f.png",
date: "APRIL 30, 2021",
author: "Alessandro Del Piero"
}
const FeaturedCardBlog = ({
title = FEATURED_DATA.title,
content = FEATURED_DATA.content,
imgSrc = FEATURED_DATA.imgSrc,
imgAuthor = FEATURED_DATA.imgAuthor,
date = FEATURED_DATA.date,
authorName = FEATURED_DATA.author
title,
slug,
content,
imgSrc = '',
imgAuthor = '',
date = '',
authorName = ''
}: FeaturedCardBlogProps) => {
return (
<section className={s.featuredCard}>
<div className={s.featuredCardWrapper}>
<div className={s.left}>
<ImgWithLink src={imgSrc} alt="image feature card"/>
</div>
<Link href={`${ROUTE.BLOG_DETAIL}/${slug}`}>
<a>
<div className={s.left}>
<ImgWithLink src={imgSrc} alt="image feature card"/>
</div>
</a>
</Link>
<div className={s.right}>
<div className={s.titleWrapper}>
<DateTime date={date}/>
<a className={s.title}>{title}</a>
<DateTime date={date }/>
<Link href={`${ROUTE.BLOG_DETAIL}/${slug}`}>
<a className={s.title}>{title}</a>
</Link>
</div>
<Author name={authorName} image={imgAuthor}/>
<div className={s.content}>{content}</div>

View File

@@ -184,6 +184,8 @@ export const FEATURED = [
export const DEFAULT_BLOG_PAGE_SIZE = 6;
export const DEFAULT_FEATURED_BLOG_PAGE_SIZE = 1;
export const FILTER_PAGE = [ROUTE.HOME, ROUTE.PRODUCTS]
export const STATE_OPTIONS = [

View File

@@ -143,6 +143,10 @@ export function getAllPromies(promies: PromiseWithKey[]) {
return promies.map(item => item.promise)
}
export function getIdFeaturedBlog(blog: BlogList) {
return blog?.id
}
export const FilterOneVatiant = (products:ProductCard[]) => {
let idList:string[] = []
let filtedProduct: ProductCard[]=[]
@@ -173,4 +177,4 @@ export function getProductVariant(product: Product, opts: SelectedOptions) {
)
})
return variant
}
}

View File

@@ -30,7 +30,7 @@ export interface BlogProps {
title: string
slug: string
description: string
imageSrc: string
imageSrc: string | null,
}
export interface CheckOutForm {