feat: get-blog-list

This commit is contained in:
Quangnhankie
2021-10-15 11:54:14 +07:00
parent 4b3fabb186
commit 111e831d3d
15 changed files with 321 additions and 140 deletions

View File

@@ -9,6 +9,9 @@ import type {
GetAllProductsOperation,
GetProductOperation,
} from '../types/product'
import type {
GetAllBlogsOperation
} from '../types/blogs'
import type { APIProvider, CommerceAPI } from '.'
import { GetAllCollectionsOperation } from '@commerce/types/collection';
@@ -27,7 +30,7 @@ export const OPERATIONS = [
'getProduct',
'getAllFacets',
'getAllCollections',
'getAllBlogs'
] as const
export const defaultOperations = OPERATIONS.reduce((ops, k) => {
@@ -144,6 +147,24 @@ 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']>
}
getProduct: {
<T extends GetProductOperation>(opts: {
variables: T['variables']

View File

@@ -0,0 +1,26 @@
import { SearchResultSortParameter } from "@framework/schema";
import { Asset, BlogTranslation, Maybe, Product } from './../../vendure/schema.d';
export type BlogList = Node &{
id: string
featuredAsset?: Maybe<Asset>
isHidden:Boolean
translations: Array<BlogTranslation>
authorName: string
authorAvatarAsset:Array<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
sort?: SearchResultSortParameter
}
}

View File

@@ -11,7 +11,7 @@ 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'
export interface VendureConfig extends CommerceAPIConfig {}
@@ -44,6 +44,7 @@ const operations = {
getProduct,
getAllFacets,
getAllCollections,
getAllBlogs
}
export const provider = { config, operations }

View File

@@ -0,0 +1,52 @@
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 = { 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 = {
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,
isHidden: val.isHidden
})),
totalItems: data?.blogs?.totalItems || null
}
}
return getAllBlogs
}

View File

@@ -2312,6 +2312,46 @@ export type Product = Node & {
customFields?: Maybe<Scalars['JSON']>
}
export type BlogList = Node &{
id: ID!
createdAt: DateTime!
updatedAt: DateTime!
featuredAsset?: Maybe<Asset>
isHidden:Boolean
translations: Array<BlogTranslation>
authorName: Scalars['String']
authorAvatarAsset:Array<Asset>
relevantProducts: Product
}
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 QueryBlogs = {
options: BlogListOptions
}
export type BlogListOptions = {
skip?: Maybe<Scalars['Int']>
take?: Maybe<Scalars['Int']>
}
export type ProductTranslation = {
__typename?: 'ProductTranslation'
id: Scalars['ID']
@@ -3364,3 +3404,4 @@ export type SearchQuery = { __typename?: 'Query' } & {
'totalItems'
> & { items: Array<{ __typename?: 'SearchResult' } & SearchResultFragment> }
}

View File

@@ -0,0 +1,20 @@
export const getAllBlogsQuery = /* GraphQL */ `
query GetBlogs ($options: BlogListOptions){
blogs(options: $options){
totalItems
items {
id
isHidden
featuredAsset {
preview
}
translations {
title
slug
description
content
}
}
}
}
`

View File

@@ -1,14 +1,72 @@
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 {
blogsResult: { blogs: BlogCardProps[] },
totalItems: number
}
export default function BlogsPage({blogsResult,totalItems}:Props) {
return(
<>
<BlogBreadCrumb />
<BlogHeading />
<FeaturedCardBlog />
<BlogsList />
<FeaturedCardBlog
title={blogsResult.blogs[0].title}
imgSrc={blogsResult.blogs[0].imageSrc ?? ''}
content={blogsResult.blogs[0].description}
/>
<BlogsList blogList={blogsResult.blogs} total={totalItems} />
</>
)
}
BlogsPage.Layout = Layout
export async function getStaticProps({
preview,
locale,
locales,
}: GetStaticPropsContext) {
const config = { locale, locales }
let promisesWithKey = [] as PromiseWithKey[]
let props = {} as any;
// Blogs
const blogsPromise = commerce.getAllBlogs({
variables: {
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
})
return {
props,
revalidate: 60
}
} catch (err) {
}
}
BlogsPage.Layout = Layout

View File

@@ -10,12 +10,12 @@ import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED } from 'src/utils/constanst.ut
import { getAllFacetValueIdsByParentCode, getAllFacetValuesForFeatuedProducts, getAllPromies, getFreshFacetId } from 'src/utils/funtion.utils';
import { PromiseWithKey } from 'src/utils/types.utils';
interface Props {
featuredAndDiscountFacetsValue: FacetValue[],
freshProducts: ProductCard[],
featuredProducts: ProductCard[],
collections: Collection[]
}
export default function Home({ featuredAndDiscountFacetsValue,
freshProducts, featuredProducts,
@@ -99,7 +99,6 @@ export async function getStaticProps({
})
promisesWithKey.push({ key: 'collections', promise: collectionsPromise, keyResult: 'collections' })
try {
const promises = getAllPromies(promisesWithKey)
const rs = await Promise.all(promises)

View File

@@ -7,16 +7,16 @@ import s from './CardBlog.module.scss'
export interface BlogCardProps extends BlogProps {
// todo: edit when intergrate API
isHidden:Boolean
}
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="image cardblog" />
</div>
</a>
</Link>

View File

@@ -1,7 +1,7 @@
.listProductCardSkeleton {
display: flex;
overflow: hidden;
&.wrap {
flex-wrap: wrap;
overflow: unset;

View File

@@ -6,7 +6,7 @@ type Props = {
count?: number
isWrap?: boolean
}
const ListProductCardSkeleton = ({ count = 5, isWrap }: Props) => {
const ListProductCardSkeleton = ({ count = 3, isWrap }: Props) => {
return (
<div className={classNames(s.listProductCardSkeleton, { [s.wrap]: isWrap })}>

View File

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

View File

@@ -0,0 +1,24 @@
import { GetAllBlogsQuery, QueryBlogs,BlogList } from '@framework/schema'
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)
console.log(isValidating);
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,
isHidden: val.isHidden
})),
totalItems: data?.blogs?.totalItems || null,
loading: isValidating,
...rest
}
}
export default useGetBlogList

View File

@@ -1,151 +1,89 @@
import React, { useState } from 'react'
import { useRouter } from 'next/router'
import React, { useEffect, useState } 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 { ListProductCardSkeleton } from 'src/components/common'
interface BlogsListProps {
data?: BlogCardProps[],
blogList?: BlogCardProps[],
total?: number
}
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 onPageChange = (page:number) => {
setCurrentPage(page)
const DEFAULT_BLOGS_ARGS = {
options:{
skip: 1, take: DEFAULT_BLOG_PAGE_SIZE
}
}
const BlogsList = ({ blogList,total }:BlogsListProps) => {
const router = useRouter();
const [initialQueryFlag, setInitialQueryFlag] = useState<boolean>(true)
const [optionQueryBlog, setOptionQueryBlog] = useState<QueryBlogs>(DEFAULT_BLOGS_ARGS)
const { blogs, totalItems, loading } = useGetBlogList(optionQueryBlog);
// console.log(blogs);
const onPageChange = (page:number) => {
router.push({
pathname: ROUTE.BLOGS,
query: {
...router.query,
[QUERY_KEY.PAGE]: page
}
},
undefined, { shallow: true }
)
}
// skip
useEffect(() => {
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);
console.log(query)
},[router.query])
let data;
if(initialQueryFlag == true){
data = blogList;
}else{
data = blogs
}
return (
<section>
<div className={s.wrapper}>
{(!initialQueryFlag && loading && !blogs) && <ListProductCardSkeleton 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.isHidden && <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

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