This commit is contained in:
Quangnhankie
2021-10-18 22:54:55 +07:00
parent 111e831d3d
commit dd31beb260
18 changed files with 236 additions and 54 deletions

View File

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

View File

@@ -19,8 +19,14 @@ export type GetAllBlogsOperation<T extends BlogsType = BlogsType> = {
variables: { variables: {
take?: number take?: number
skip?: number skip?: number
sort?: SearchResultSortParameter
} }
} }
export type GetFeaturedOperation<T extends BlogsType = BlogsType> = {
data: { items: T['items'][] }
variables: {
take?: number
skip?: number
}
}

View File

@@ -12,6 +12,7 @@ import getSiteInfo from './operations/get-site-info'
import login from './operations/login' import login from './operations/login'
import fetchGraphqlApi from './utils/fetch-graphql-api' import fetchGraphqlApi from './utils/fetch-graphql-api'
import getAllBlogs from './operations/get-all-blogs' import getAllBlogs from './operations/get-all-blogs'
import getFeaturedBlog from './operations/get-featured-blog'
export interface VendureConfig extends CommerceAPIConfig {} export interface VendureConfig extends CommerceAPIConfig {}
@@ -44,7 +45,8 @@ const operations = {
getProduct, getProduct,
getAllFacets, getAllFacets,
getAllCollections, getAllCollections,
getAllBlogs getAllBlogs,
getFeaturedBlog
} }
export const provider = { config, operations } export const provider = { config, operations }

View File

@@ -3,7 +3,11 @@ import { Provider, VendureConfig } from '..'
import { GetAllBlogsQuery,BlogList } from '../../schema' import { GetAllBlogsQuery,BlogList } from '../../schema'
import { getAllBlogsQuery } from '../../utils/queries/get-all-blog-query' import { getAllBlogsQuery } from '../../utils/queries/get-all-blog-query'
export type BlogVariables = { take?: number,skip?:number } export type BlogVariables = {
excludeBlogIds?: string[],
take?: number,
skip?:number
}
export default function getAllBlogsOperation({ export default function getAllBlogsOperation({
commerce, commerce,
@@ -24,8 +28,10 @@ export default function getAllBlogsOperation({
config?: Partial<VendureConfig> config?: Partial<VendureConfig>
preview?: boolean preview?: boolean
} = {}): Promise<{ blogs: GetAllBlogsQuery | any[] ,totalItems?:number }> { } = {}): Promise<{ blogs: GetAllBlogsQuery | any[] ,totalItems?:number }> {
console.log(vars.excludeBlogIds)
const config = commerce.getConfig(cfg) const config = commerce.getConfig(cfg)
const variables = { const variables = {
excludeBlogIds: vars.excludeBlogIds,
options: { options: {
take: vars.take, take: vars.take,
skip: vars.skip, skip: vars.skip,
@@ -34,7 +40,6 @@ export default function getAllBlogsOperation({
const { data } = await config.fetch<GetAllBlogsQuery>(query, { const { data } = await config.fetch<GetAllBlogsQuery>(query, {
variables, variables,
}) })
return { return {
blogs: data?.blogs?.items?.map((val:BlogList)=>({ blogs: data?.blogs?.items?.map((val:BlogList)=>({
id: val.id, id: val.id,
@@ -42,7 +47,11 @@ export default function getAllBlogsOperation({
imageSrc: val.featuredAsset?.preview ?? null, imageSrc: val.featuredAsset?.preview ?? null,
slug: val.translations[0]?.slug, slug: val.translations[0]?.slug,
description: val.translations[0]?.description, description: val.translations[0]?.description,
isHidden: val.isHidden isPublish: val.isPublish,
isFeatured: val.isFeatured,
authorName: val.authorName,
authorAvatarAsset : val.authorAvatarAsset?.preview,
createdAt: val.createdAt
})), })),
totalItems: data?.blogs?.totalItems || null totalItems: data?.blogs?.totalItems || null
} }

View File

@@ -0,0 +1,56 @@
import { OperationContext } from '@commerce/api/operations'
import { Provider, VendureConfig } from '..'
import { GetFeaturedBlogQuery,BlogList } from '../../schema'
import { getFeatuedBlogQuery } 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 = getFeatuedBlogQuery,
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

@@ -2317,11 +2317,12 @@ export type BlogList = Node &{
createdAt: DateTime! createdAt: DateTime!
updatedAt: DateTime! updatedAt: DateTime!
featuredAsset?: Maybe<Asset> featuredAsset?: Maybe<Asset>
isHidden:Boolean isPublish:Boolean
translations: Array<BlogTranslation> translations: Array<BlogTranslation>
authorName: Scalars['String'] authorName: Scalars['String']
authorAvatarAsset:Array<Asset> authorAvatarAsset:Asset
relevantProducts: Product relevantProducts: Product
isFeatured: Boolean
} }
export type BlogTranslation = { export type BlogTranslation = {
@@ -2342,8 +2343,15 @@ export type GetAllBlogsQuery = PaginatedList & {
'totalItems' 'totalItems'
} }
} }
export type GetFeaturedBlogQuery = PaginatedList & {
featuredBlogs: { __typename?: 'BlogList' } & {
items: Array<{ __typename?: 'Blog' } & BlogList!>,
'totalItems'
}
}
export type QueryBlogs = { export type QueryBlogs = {
excludeBlogIds:Array,
options: BlogListOptions options: BlogListOptions
} }

View File

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

View File

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

View File

@@ -3,26 +3,33 @@ import { GetStaticPropsContext } from 'next';
import { Layout } from 'src/components/common'; import { Layout } from 'src/components/common';
import { BlogCardProps } from 'src/components/common/CardBlog/CardBlog'; import { BlogCardProps } from 'src/components/common/CardBlog/CardBlog';
import { BlogBreadCrumb, BlogHeading, BlogsList, FeaturedCardBlog } from 'src/components/modules/blogs'; import { BlogBreadCrumb, BlogHeading, BlogsList, FeaturedCardBlog } from 'src/components/modules/blogs';
import { DEFAULT_BLOG_PAGE_SIZE } from "src/utils/constanst.utils"; import { DEFAULT_BLOG_PAGE_SIZE,DEFAULT_FEATURED_BLOG_PAGE_SIZE } from "src/utils/constanst.utils";
import { getAllPromies } from 'src/utils/funtion.utils'; import { getAllPromies } from 'src/utils/funtion.utils';
import { PromiseWithKey } from 'src/utils/types.utils'; import { PromiseWithKey } from 'src/utils/types.utils';
import { getIdFeaturedBlog } from 'src/utils/funtion.utils';
interface Props { interface Props {
blogsResult: { blogs: BlogCardProps[] }, blogsResult: { blogs?: BlogCardProps[],featuredBlogs?: BlogCardProps[] },
totalItems: number totalItems: number
} }
export default function BlogsPage({blogsResult,totalItems}:Props) { export default function BlogsPage({blogsResult,totalItems}:Props) {
// let date = new Date(blogsResult?.featuredBlogs[0]?.createdAt ?? '' );
// let fullDate = date.toLocaleString('en-us', { month: 'long' }) + " " + date.getDate()+","+date.getFullYear();
return( return(
<> <>
<BlogBreadCrumb /> <BlogBreadCrumb />
<BlogHeading /> <BlogHeading />
<FeaturedCardBlog {/* <FeaturedCardBlog
title={blogsResult.blogs[0].title} title={blogsResult?.featuredBlogs[0]?.title}
imgSrc={blogsResult.blogs[0].imageSrc ?? ''} imgSrc={blogsResult?.featuredBlogs[0]?.imageSrc ?? ''}
content={blogsResult.blogs[0].description} content={blogsResult?.featuredBlogs[0]?.description}
/> imgAuthor={blogsResult?.featuredBlogs[0]?.authorAvatarAsset}
authorName={blogsResult?.featuredBlogs[0]?.authorName}
date={fullDate}
/> */}
<BlogsList blogList={blogsResult.blogs} total={totalItems} /> <BlogsList blogList={blogsResult.blogs} total={totalItems} />
</> </>
) )
@@ -39,16 +46,32 @@ export async function getStaticProps({
let props = {} as any; let props = {} as any;
const {featuredBlogs} = await commerce.getFeaturedBlog({
variables: {
take: DEFAULT_BLOG_PAGE_SIZE
},
config,
preview,
})
// console.log(featuredBlogs[0].id);
// promisesWithKey.push({ key: 'blogsResult', promise: FeaturedBlogPromise })
// Blogs // Blogs
const idBlog = featuredBlogs[0].id;
const blogsPromise = commerce.getAllBlogs({ const blogsPromise = commerce.getAllBlogs({
variables: { variables: {
take: DEFAULT_BLOG_PAGE_SIZE excludeBlogIds: [idBlog],
take: DEFAULT_BLOG_PAGE_SIZE
}, },
config, config,
preview, preview,
}) })
promisesWithKey.push({ key: 'blogsResult', promise: blogsPromise }) promisesWithKey.push({ key: 'blogsResult', promise: blogsPromise })
try { try {
const promises = getAllPromies(promisesWithKey) const promises = getAllPromies(promisesWithKey)
@@ -58,7 +81,8 @@ export async function getStaticProps({
props[item.key] = item.keyResult ? rs[index][item.keyResult] : rs[index] props[item.key] = item.keyResult ? rs[index][item.keyResult] : rs[index]
return null return null
}) })
props['blogsResult']['featuredBlog'] = featuredBlogs;
return { return {
props, props,
revalidate: 60 revalidate: 60

View File

@@ -56,7 +56,7 @@ export async function getStaticProps({
preview, preview,
}) })
props.featuredAndDiscountFacetsValue = getAllFacetValuesForFeatuedProducts(facets) props.featuredAndDiscountFacetsValue = getAllFacetValuesForFeatuedProducts(facets)
// fresh products // fresh products
const freshProductvariables: ProductVariables = {} const freshProductvariables: ProductVariables = {}
const freshFacetId = getFreshFacetId(facets) const freshFacetId = getFreshFacetId(facets)

View File

@@ -6,8 +6,11 @@ import { ImgWithLink } from '..'
import s from './CardBlog.module.scss' import s from './CardBlog.module.scss'
export interface BlogCardProps extends BlogProps { export interface BlogCardProps extends BlogProps {
// todo: edit when intergrate API isPublish?:Boolean,
isHidden:Boolean isFeatured?:Boolean,
authorAvatarAsset?:string,
authorName?:string,
createdAt?:string
} }
const CardBlog = ({ imageSrc, title, description, slug }: BlogCardProps) => { const CardBlog = ({ imageSrc, title, description, slug }: BlogCardProps) => {

View File

@@ -9,4 +9,11 @@
margin-bottom: 1.6rem; margin-bottom: 1.6rem;
} }
} }
&.isBlog{
width: 90%;
justify-content: space-between;
div{
min-width: 32rem;
}
}
} }

View File

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

View File

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

View File

@@ -5,7 +5,7 @@ import useSWR from 'swr'
const useGetBlogList = (options?: QueryBlogs) => { const useGetBlogList = (options?: QueryBlogs) => {
const { data, isValidating, ...rest } = useSWR<GetAllBlogsQuery>([getAllBlogsQuery, options], gglFetcher) const { data, isValidating, ...rest } = useSWR<GetAllBlogsQuery>([getAllBlogsQuery, options], gglFetcher)
console.log(isValidating);
return { return {
blogs: data?.blogs?.items?.map((val:BlogList)=>({ blogs: data?.blogs?.items?.map((val:BlogList)=>({
id: val.id, id: val.id,
@@ -13,7 +13,11 @@ const useGetBlogList = (options?: QueryBlogs) => {
imageSrc: val.featuredAsset?.preview ?? null, imageSrc: val.featuredAsset?.preview ?? null,
slug: val.translations[0]?.slug, slug: val.translations[0]?.slug,
description: val.translations[0]?.description, description: val.translations[0]?.description,
isHidden: val.isHidden isPublish: val.isPublish,
isFeatured:val.isFeatured,
authorName: val.authorName,
authorAvatarAsset : val.authorAvatarAsset?.preview,
createdAt: val.createdAt
})), })),
totalItems: data?.blogs?.totalItems || null, totalItems: data?.blogs?.totalItems || null,
loading: isValidating, loading: isValidating,

View File

@@ -15,19 +15,20 @@ interface BlogsListProps {
} }
const DEFAULT_BLOGS_ARGS = { const DEFAULT_BLOGS_ARGS = {
excludeBlogIds: ["28"],
options:{ options:{
skip: 1, take: DEFAULT_BLOG_PAGE_SIZE skip: 1, take: DEFAULT_BLOG_PAGE_SIZE
} }
} }
const BlogsList = ({ blogList,total }:BlogsListProps) => { const BlogsList = ({ blogList,total }:BlogsListProps) => {
const router = useRouter(); const router = useRouter();
const [initialQueryFlag, setInitialQueryFlag] = useState<boolean>(true) const [initialQueryFlag, setInitialQueryFlag] = useState<boolean>(true)
const [optionQueryBlog, setOptionQueryBlog] = useState<QueryBlogs>(DEFAULT_BLOGS_ARGS) const [optionQueryBlog, setOptionQueryBlog] = useState<QueryBlogs>(DEFAULT_BLOGS_ARGS)
const { blogs, totalItems, loading } = useGetBlogList(optionQueryBlog); const { blogs, totalItems, loading } = useGetBlogList(optionQueryBlog);
// console.log(blogs);
const onPageChange = (page:number) => { const onPageChange = (page:number) => {
@@ -49,7 +50,7 @@ const BlogsList = ({ blogList,total }:BlogsListProps) => {
query.options.skip = page * DEFAULT_BLOG_PAGE_SIZE; query.options.skip = page * DEFAULT_BLOG_PAGE_SIZE;
setOptionQueryBlog(query); setOptionQueryBlog(query);
setInitialQueryFlag(false); setInitialQueryFlag(false);
console.log(query)
},[router.query]) },[router.query])
@@ -59,18 +60,19 @@ const BlogsList = ({ blogList,total }:BlogsListProps) => {
}else{ }else{
data = blogs data = blogs
} }
console.log(blogList);
return ( return (
<section> <section>
<div className={s.wrapper}> <div className={s.wrapper}>
{(!initialQueryFlag && loading && !blogs) && <ListProductCardSkeleton count={DEFAULT_BLOG_PAGE_SIZE} isWrap />} {(!initialQueryFlag && loading && !blogs) && <ListProductCardSkeleton count={DEFAULT_BLOG_PAGE_SIZE} isWrap isBlog={true} />}
<div className={s.list}> <div className={s.list}>
{ {
data?.map((product,index)=> { data?.map((product,index)=> {
return( return(
<div className={s.card} key={`${product.title}-${index}`}> <div className={s.card} key={`${product.title}-${index}`}>
{!product.isHidden && <CardBlog {...product} /> } {product.isPublish && <CardBlog {...product} /> }
</div> </div>
) )
}) })

View File

@@ -171,6 +171,8 @@ export const FEATURED = [
export const DEFAULT_BLOG_PAGE_SIZE = 6; 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 FILTER_PAGE = [ROUTE.HOME, ROUTE.PRODUCTS]
export const STATE_OPTIONS = [ export const STATE_OPTIONS = [

View File

@@ -1,3 +1,4 @@
import { BlogList } from '@framework/schema';
import { Facet } from "@commerce/types/facet"; import { Facet } from "@commerce/types/facet";
import { Collection, FacetValue, SearchResultSortParameter } from './../../framework/vendure/schema.d'; import { Collection, FacetValue, SearchResultSortParameter } from './../../framework/vendure/schema.d';
import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED, CODE_FACET_FEATURED_VARIANT, PRODUCT_SORT_OPTION_VALUE } from "./constanst.utils"; import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED, CODE_FACET_FEATURED_VARIANT, PRODUCT_SORT_OPTION_VALUE } from "./constanst.utils";
@@ -127,4 +128,9 @@ export const getCategoryNameFromCollectionId = (colelctions: Collection[], colle
export function getAllPromies(promies: PromiseWithKey[]) { export function getAllPromies(promies: PromiseWithKey[]) {
return promies.map(item => item.promise) return promies.map(item => item.promise)
} }
export function getIdFeaturedBlog(blog: BlogList) {
return blog?.id
}