203 lines
5.9 KiB
TypeScript
203 lines
5.9 KiB
TypeScript
import React, { FC, useEffect } from 'react'
|
|
|
|
import { useSelector, useDispatch } from 'react-redux'
|
|
import { ApplicationState } from '../../../store'
|
|
|
|
import { actionCreators as contentActionCreators } from '../../../store/reducers/Content'
|
|
import { actionCreators as loaderActionCreators } from '../../../store/reducers/Loader'
|
|
import { actionCreators as blogCatalogActionCreators } from '../../../store/reducers/BlogCatalog'
|
|
import { actionCreators as blogFeaturedActionCreators } from '../../../store/reducers/BlogFeatured'
|
|
import { actionCreators as blogCategoriesActionCreators } from '../../../store/reducers/BlogCategories'
|
|
|
|
import { Link, useNavigate, useParams } from 'react-router-dom'
|
|
import { Card, CardBody, CardFooter, CardImg, Col, Container, Row } from 'reactstrap'
|
|
|
|
import { dateFormat, findRoutes } from '../../../functions'
|
|
|
|
import { Categories, Empty, Search } from '../../../components/SideWidgets'
|
|
import { Pagination } from '../../../components/Pagination'
|
|
|
|
import { BlogItemModel } from '../../../models'
|
|
import { TitleSectionModel } from '../../../models/pageSections'
|
|
|
|
const TitleSection: FC<TitleSectionModel> = (props) => {
|
|
const { title, text } = props
|
|
return <header className="py-5 bg-light border-bottom mb-4">
|
|
<Container fluid>
|
|
<div className="text-center my-5">
|
|
<h1 className="fw-bolder">{title ? title : ''}</h1>
|
|
<p className="lead mb-0">{text ? text : ''}</p>
|
|
</div>
|
|
</Container>
|
|
</header>
|
|
}
|
|
|
|
interface FeaturedBlog {
|
|
path?: string,
|
|
currentPage?: number
|
|
item?: BlogItemModel,
|
|
readTime?: string
|
|
}
|
|
|
|
const FeaturedBlogSection: FC<FeaturedBlog> = ({
|
|
currentPage = 1,
|
|
path = "",
|
|
item = {
|
|
id: "",
|
|
slug: "demo-post",
|
|
|
|
badges: [],
|
|
|
|
image: {
|
|
src: "https://dummyimage.com/850x350/dee2e6/6c757d.jpg",
|
|
alt: "..."
|
|
},
|
|
title: "",
|
|
shortText: "",
|
|
author: {
|
|
id: "",
|
|
nickName: "",
|
|
image: {
|
|
src: "https://dummyimage.com/40x40/ced4da/6c757d",
|
|
alt: "..."
|
|
}
|
|
},
|
|
created: new Date().toString(),
|
|
tags: [],
|
|
likes: 0
|
|
},
|
|
readTime = ""
|
|
}) => {
|
|
|
|
return <Card className="mb-4 shadow border-0">
|
|
<CardImg top {...item.image} />
|
|
<CardBody className="p-4">
|
|
{item.badges.map((badge, index) => <div key={index} className="badge bg-primary bg-gradient rounded-pill mb-2">{badge}</div>)}
|
|
|
|
<Link className="text-decoration-none link-dark stretched-link" to={`${path}/${currentPage}/${item.slug}`}>
|
|
<h5 className="card-title mb-3">{item.title}</h5>
|
|
</Link>
|
|
<p className="card-text mb-0" dangerouslySetInnerHTML={{ __html: item.shortText ? item.shortText : '' }}></p>
|
|
</CardBody>
|
|
<CardFooter className="p-4 pt-0 bg-transparent border-top-0">
|
|
<div className="d-flex align-items-end justify-content-between">
|
|
<div className="d-flex align-items-center">
|
|
<img className="rounded-circle me-3" {...item.author.image} />
|
|
<div className="small">
|
|
<div className="fw-bold">{item.author.nickName}</div>
|
|
<div className="text-muted">{readTime}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardFooter>
|
|
</Card>
|
|
}
|
|
|
|
interface BlogItems {
|
|
path?: string
|
|
totalPages?: number,
|
|
currentPage?: number,
|
|
items?: BlogItemModel []
|
|
}
|
|
|
|
const BlogItemsSection: FC<BlogItems> = ({
|
|
path = "",
|
|
totalPages = 1,
|
|
currentPage = 1,
|
|
items = []
|
|
}) => {
|
|
|
|
const dispatch = useDispatch()
|
|
const navigate = useNavigate()
|
|
|
|
return <>
|
|
{items.map((item, index) => <Col key={index} className="lg-6">
|
|
<Card className="mb-4">
|
|
|
|
<CardImg top {...item.image} />
|
|
|
|
<CardBody>
|
|
<div className="small text-muted">{dateFormat(item.created)}</div>
|
|
<h2 className="card-title h4">{item.title}</h2>
|
|
<p className="card-text">{item.shortText}</p>
|
|
<Link to={`${path}/${currentPage}/${item.slug}`} className="btn btn-primary">Read more →</Link>
|
|
</CardBody>
|
|
|
|
</Card>
|
|
</Col>)}
|
|
|
|
<Pagination {...{
|
|
totalPages: totalPages,
|
|
currentPage: currentPage,
|
|
onClick: (nextPage) => {
|
|
dispatch(blogCatalogActionCreators.requestBlogCatalog({
|
|
currentPage: nextPage + ""
|
|
}))
|
|
|
|
navigate(`${path}/${nextPage}`)
|
|
}
|
|
}} />
|
|
</>
|
|
}
|
|
|
|
const BlogCatalog = () => {
|
|
const params = useParams()
|
|
const dispatch = useDispatch()
|
|
|
|
const { content, blogCatalog, blogCategories, blogFeatured } = useSelector((state: ApplicationState) => state)
|
|
const page = content?.blogCatalog
|
|
const path = findRoutes(content?.routes, 'BlogCatalog')[0]?.targets[0]
|
|
|
|
useEffect(() => {
|
|
// dispatch(blogCatalogActionCreators.requestBlogCatalog({
|
|
// currentPage: params?.page ? params.page : "1"
|
|
// }))
|
|
// dispatch(blogFeaturedActionCreators.requestBlogFeatured())
|
|
dispatch(blogCategoriesActionCreators.requestBlogCategories())
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
// blogCatalog?.isLoading
|
|
// ? dispatch(loaderActionCreators.show())
|
|
// : setTimeout(() => {
|
|
// dispatch(loaderActionCreators.hide())
|
|
// }, 1000)
|
|
}, [blogCatalog?.isLoading])
|
|
|
|
const blogItem = blogFeatured?.items[0]
|
|
|
|
const featuredBlog: FeaturedBlog = {
|
|
path: path,
|
|
currentPage: blogCatalog?.currentPage,
|
|
item: blogItem,
|
|
readTime: page?.featuredBlogSection?.readTime
|
|
}
|
|
|
|
if(featuredBlog.readTime && blogItem?.created && blogItem?.readTime)
|
|
featuredBlog.readTime = featuredBlog.readTime?.replace('{date}', dateFormat(blogItem.created))
|
|
.replace('{readTime}', `${blogItem.readTime}`)
|
|
|
|
return <>
|
|
<TitleSection {...page?.titleSection} />
|
|
<Container fluid>
|
|
<Row>
|
|
<Col>
|
|
<FeaturedBlogSection {...featuredBlog} />
|
|
<Row>
|
|
<BlogItemsSection path={path} {...blogCatalog} />
|
|
</Row>
|
|
</Col>
|
|
<Col lg="4">
|
|
<Search />
|
|
<Categories {...blogCategories} />
|
|
<Empty/>
|
|
</Col>
|
|
</Row>
|
|
</Container>
|
|
</>
|
|
}
|
|
|
|
export {
|
|
BlogCatalog
|
|
}
|