From 111e831d3d400fbbfe6db40a412d7357721d808e Mon Sep 17 00:00:00 2001 From: Quangnhankie Date: Fri, 15 Oct 2021 11:54:14 +0700 Subject: [PATCH] feat: get-blog-list --- framework/commerce/api/operations.ts | 23 ++- framework/commerce/types/blogs.ts | 26 +++ framework/vendure/api/index.ts | 3 +- .../vendure/api/operations/get-all-blogs.ts | 52 +++++ framework/vendure/schema.d.ts | 41 ++++ .../utils/queries/get-all-blog-query.ts | 20 ++ pages/blogs.tsx | 68 ++++++- pages/index.tsx | 3 +- src/components/common/CardBlog/CardBlog.tsx | 6 +- .../ListProductCardSkeleton.module.scss | 2 +- .../ListProductCardSkeleton.tsx | 2 +- src/components/hooks/blog/index.ts | 1 + src/components/hooks/blog/useGetBlogList.tsx | 24 +++ .../modules/blogs/BlogsList/BlogsList.tsx | 188 ++++++------------ src/utils/types.utils.ts | 2 +- 15 files changed, 321 insertions(+), 140 deletions(-) create mode 100644 framework/commerce/types/blogs.ts create mode 100644 framework/vendure/api/operations/get-all-blogs.ts create mode 100644 framework/vendure/utils/queries/get-all-blog-query.ts create mode 100644 src/components/hooks/blog/index.ts create mode 100644 src/components/hooks/blog/useGetBlogList.tsx diff --git a/framework/commerce/api/operations.ts b/framework/commerce/api/operations.ts index ca9b325c2..1065653bd 100644 --- a/framework/commerce/api/operations.ts +++ b/framework/commerce/api/operations.ts @@ -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

= { ): Promise } + + getAllBlogs: { + (opts: { + variables?: T['variables'] + config?: P['config'] + preview?: boolean + }): Promise + + ( + opts: { + variables?: T['variables'] + config?: P['config'] + preview?: boolean + } & OperationOptions + ): Promise + } + + getProduct: { (opts: { variables: T['variables'] diff --git a/framework/commerce/types/blogs.ts b/framework/commerce/types/blogs.ts new file mode 100644 index 000000000..f138b964e --- /dev/null +++ b/framework/commerce/types/blogs.ts @@ -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 + isHidden:Boolean + translations: Array + authorName: string + authorAvatarAsset:Array + relevantProducts: Product +} +export type BlogsType = { + items: BlogList + totalItems: number +} +export type GetAllBlogsOperation = { + data: { items: T['items'][] } + variables: { + take?: number + skip?: number + sort?: SearchResultSortParameter + } + } + + \ No newline at end of file diff --git a/framework/vendure/api/index.ts b/framework/vendure/api/index.ts index 95ec47d66..6f73c680e 100644 --- a/framework/vendure/api/index.ts +++ b/framework/vendure/api/index.ts @@ -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 } diff --git a/framework/vendure/api/operations/get-all-blogs.ts b/framework/vendure/api/operations/get-all-blogs.ts new file mode 100644 index 000000000..3612a751d --- /dev/null +++ b/framework/vendure/api/operations/get-all-blogs.ts @@ -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) { + async function getAllBlogs(opts?: { + variables?: BlogVariables + config?: Partial + preview?: boolean + }): Promise<{ blogs: GetAllBlogsQuery,totalItems:number }> + + async function getAllBlogs({ + query = getAllBlogsQuery, + variables: { ...vars } = {}, + config: cfg, + }: { + query?: string + variables?: BlogVariables + config?: Partial + 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(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 +} diff --git a/framework/vendure/schema.d.ts b/framework/vendure/schema.d.ts index 9ab352944..362436170 100644 --- a/framework/vendure/schema.d.ts +++ b/framework/vendure/schema.d.ts @@ -2312,6 +2312,46 @@ export type Product = Node & { customFields?: Maybe } +export type BlogList = Node &{ + id: ID! + createdAt: DateTime! + updatedAt: DateTime! + featuredAsset?: Maybe + isHidden:Boolean + translations: Array + authorName: Scalars['String'] + authorAvatarAsset:Array + 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 + take?: Maybe +} + export type ProductTranslation = { __typename?: 'ProductTranslation' id: Scalars['ID'] @@ -3364,3 +3404,4 @@ export type SearchQuery = { __typename?: 'Query' } & { 'totalItems' > & { items: Array<{ __typename?: 'SearchResult' } & SearchResultFragment> } } + diff --git a/framework/vendure/utils/queries/get-all-blog-query.ts b/framework/vendure/utils/queries/get-all-blog-query.ts new file mode 100644 index 000000000..d7a4a294a --- /dev/null +++ b/framework/vendure/utils/queries/get-all-blog-query.ts @@ -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 + } + } + } +} +` diff --git a/pages/blogs.tsx b/pages/blogs.tsx index 84e56b55d..f46a2214b 100644 --- a/pages/blogs.tsx +++ b/pages/blogs.tsx @@ -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( <> - - + + ) } -BlogsPage.Layout = Layout \ No newline at end of file + + +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 diff --git a/pages/index.tsx b/pages/index.tsx index 010094de4..f4619a195 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -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) diff --git a/src/components/common/CardBlog/CardBlog.tsx b/src/components/common/CardBlog/CardBlog.tsx index a44f45494..c5c87ba12 100644 --- a/src/components/common/CardBlog/CardBlog.tsx +++ b/src/components/common/CardBlog/CardBlog.tsx @@ -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 (

- +
- +
diff --git a/src/components/common/ListProductCardSkeleton/ListProductCardSkeleton.module.scss b/src/components/common/ListProductCardSkeleton/ListProductCardSkeleton.module.scss index f8dfec116..5503dccf7 100644 --- a/src/components/common/ListProductCardSkeleton/ListProductCardSkeleton.module.scss +++ b/src/components/common/ListProductCardSkeleton/ListProductCardSkeleton.module.scss @@ -1,7 +1,7 @@ .listProductCardSkeleton { display: flex; overflow: hidden; - + &.wrap { flex-wrap: wrap; overflow: unset; diff --git a/src/components/common/ListProductCardSkeleton/ListProductCardSkeleton.tsx b/src/components/common/ListProductCardSkeleton/ListProductCardSkeleton.tsx index 218f9fb11..9ab255477 100644 --- a/src/components/common/ListProductCardSkeleton/ListProductCardSkeleton.tsx +++ b/src/components/common/ListProductCardSkeleton/ListProductCardSkeleton.tsx @@ -6,7 +6,7 @@ type Props = { count?: number isWrap?: boolean } -const ListProductCardSkeleton = ({ count = 5, isWrap }: Props) => { +const ListProductCardSkeleton = ({ count = 3, isWrap }: Props) => { return (
diff --git a/src/components/hooks/blog/index.ts b/src/components/hooks/blog/index.ts new file mode 100644 index 000000000..e8359671a --- /dev/null +++ b/src/components/hooks/blog/index.ts @@ -0,0 +1 @@ +export {default as useGetBlogList }from "./useGetBlogList" \ No newline at end of file diff --git a/src/components/hooks/blog/useGetBlogList.tsx b/src/components/hooks/blog/useGetBlogList.tsx new file mode 100644 index 000000000..ebbe6162b --- /dev/null +++ b/src/components/hooks/blog/useGetBlogList.tsx @@ -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, 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 diff --git a/src/components/modules/blogs/BlogsList/BlogsList.tsx b/src/components/modules/blogs/BlogsList/BlogsList.tsx index 9b7ddc1e0..4586f5acd 100644 --- a/src/components/modules/blogs/BlogsList/BlogsList.tsx +++ b/src/components/modules/blogs/BlogsList/BlogsList.tsx @@ -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(true) + + const [optionQueryBlog, setOptionQueryBlog] = useState(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 (
+ {(!initialQueryFlag && loading && !blogs) && }
+ { - data.slice(currentPage*DEFAULT_BLOG_PAGE_SIZE,(currentPage+1)*DEFAULT_BLOG_PAGE_SIZE).map((product,index)=> { - return( + data?.map((product,index)=> { + return(
- + {!product.isHidden && }
) }) }
- +
) } -export default BlogsList \ No newline at end of file +export default BlogsList + + diff --git a/src/utils/types.utils.ts b/src/utils/types.utils.ts index 59243b80f..a07f23aab 100644 --- a/src/utils/types.utils.ts +++ b/src/utils/types.utils.ts @@ -29,7 +29,7 @@ export interface BlogProps { title: string slug: string description: string - imageSrc: string + imageSrc: string | null, } export interface CheckOutForm {