(bugfix): models fixing
This commit is contained in:
parent
e1ae3f20e5
commit
ddb13e4184
@ -42,8 +42,7 @@ const App = () => {
|
||||
const { pathname } = useLocation()
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const content = useSelector((state: ApplicationState) => state.content)
|
||||
const loader = useSelector((state: ApplicationState) => state.loader)
|
||||
const { content, loader } = useSelector((state: ApplicationState) => state)
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(settingsActionCreators.requestContent())
|
||||
|
||||
@ -1,20 +1,28 @@
|
||||
import React, { FC } from 'react'
|
||||
import { Card, CardBody } from 'reactstrap'
|
||||
import { CommentModel } from '../../models'
|
||||
import { CommentsSectionModel } from '../../models/pageSections'
|
||||
|
||||
const Comments: FC<CommentsSectionModel> = ({
|
||||
comments = []
|
||||
|
||||
|
||||
interface Comments {
|
||||
staticContent?: CommentsSectionModel,
|
||||
items?: CommentModel []
|
||||
}
|
||||
|
||||
const CommentsSection: FC<Comments> = ({
|
||||
staticContent,
|
||||
items = []
|
||||
}) => {
|
||||
|
||||
|
||||
return <section className="mb-5">
|
||||
<Card className="card bg-light">
|
||||
<CardBody className="card-body">
|
||||
<form className="mb-4">
|
||||
<textarea className="form-control" rows={3} placeholder="Join the discussion and leave a comment!"></textarea>
|
||||
<textarea className="form-control" rows={3} placeholder={staticContent?.leaveComment}></textarea>
|
||||
</form>
|
||||
|
||||
{comments.map((comment, index) => <div key={index} className={`d-flex ${index < comments.length - 1 ? 'mb-4' : ''}`}>
|
||||
{items.map((comment, index) => <div key={index} className={`d-flex ${index < items.length - 1 ? 'mb-4' : ''}`}>
|
||||
<div className="flex-shrink-0">
|
||||
<img className="rounded-circle" {...comment.author.image} />
|
||||
</div>
|
||||
@ -42,5 +50,5 @@ const Comments: FC<CommentsSectionModel> = ({
|
||||
}
|
||||
|
||||
export {
|
||||
Comments
|
||||
CommentsSection
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, { FC } from 'react'
|
||||
import { Card, CardBody, CardHeader, Col, Row } from 'reactstrap'
|
||||
import { CategoryModel } from '../../models'
|
||||
|
||||
@ -15,20 +15,16 @@ const Search = () => {
|
||||
}
|
||||
|
||||
interface ICategories {
|
||||
categories?: CategoryModel []
|
||||
items?: CategoryModel []
|
||||
}
|
||||
|
||||
const Categories = (props: ICategories) => {
|
||||
const { categories } = props
|
||||
const Categories: FC<ICategories> = ({
|
||||
items = []
|
||||
}) => {
|
||||
const middleIndex = Math.ceil(items.length / 2)
|
||||
|
||||
if(!categories) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
const middleIndex = Math.ceil(categories.length / 2)
|
||||
|
||||
const firstHalf = categories.splice(0, middleIndex)
|
||||
const secondHalf = categories.splice(-middleIndex)
|
||||
const firstHalf = items.splice(0, middleIndex)
|
||||
const secondHalf = items.splice(-middleIndex)
|
||||
|
||||
return <Card className="mb-4">
|
||||
<CardHeader>Categories</CardHeader>
|
||||
|
||||
@ -5,8 +5,11 @@ export interface RequestModel {
|
||||
[key: string]: string | undefined
|
||||
}
|
||||
|
||||
export interface ResponseModel {
|
||||
|
||||
export interface ResponseModel { }
|
||||
|
||||
|
||||
export interface PageModel {
|
||||
|
||||
}
|
||||
|
||||
export interface PageSectionModel {
|
||||
@ -16,22 +19,18 @@ export interface PageSectionModel {
|
||||
|
||||
export interface PersonModel {
|
||||
id: string,
|
||||
image: ImageModel
|
||||
image?: ImageModel
|
||||
}
|
||||
|
||||
export interface PostItemModel {
|
||||
id: string,
|
||||
slug: string,
|
||||
image: ImageModel,
|
||||
badge: string,
|
||||
badges: string [],
|
||||
title: string,
|
||||
shortText: string,
|
||||
text: string,
|
||||
shortText?: string,
|
||||
text?: string,
|
||||
author: AuthorModel,
|
||||
created: string,
|
||||
tags: string []
|
||||
}
|
||||
|
||||
export interface PageModel {
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { PageSectionModel, PersonModel, PostItemModel } from "./abstractions"
|
||||
import { PersonModel, PostItemModel } from "./abstractions"
|
||||
|
||||
export interface AuthorModel extends PersonModel {
|
||||
nickName: string
|
||||
@ -14,12 +14,23 @@ export interface CategoryModel {
|
||||
text: string
|
||||
}
|
||||
|
||||
export interface CommentModel {
|
||||
author: AuthorModel,
|
||||
comment: string,
|
||||
responses?: CommentModel []
|
||||
}
|
||||
|
||||
export interface FeatureModel {
|
||||
icon: string,
|
||||
title: string,
|
||||
text: string
|
||||
}
|
||||
|
||||
export interface FormItemModel {
|
||||
title?: string,
|
||||
placeHolder?: string
|
||||
}
|
||||
|
||||
export interface ImageModel {
|
||||
src: string,
|
||||
alt: string
|
||||
@ -51,7 +62,7 @@ export interface ShopItemModel extends PostItemModel {
|
||||
quantity?: number
|
||||
}
|
||||
|
||||
export interface TestimonialsModel {
|
||||
export interface TestimonialModel {
|
||||
text: string,
|
||||
reviewer: ReviewerModel
|
||||
}
|
||||
@ -61,14 +72,3 @@ export interface PaginationModel<T> {
|
||||
currentPage: number,
|
||||
items: T []
|
||||
}
|
||||
|
||||
export interface FormItemModel {
|
||||
title?: string,
|
||||
placeHolder?: string
|
||||
}
|
||||
|
||||
export interface CommentModel {
|
||||
author: AuthorModel,
|
||||
comment: string,
|
||||
responses?: CommentModel []
|
||||
}
|
||||
@ -1,21 +1,37 @@
|
||||
import { BlogItemModel, CommentModel, FeatureModel, FormItemModel, ImageModel, MenuItemModel, TestimonialsModel } from "./"
|
||||
import { FeatureModel, FormItemModel, ImageModel, MenuItemModel, TestimonialModel } from "./"
|
||||
import { PageSectionModel } from "./abstractions"
|
||||
|
||||
export interface BlogTitleSectionModel extends PageSectionModel {
|
||||
postedOnBy: string
|
||||
}
|
||||
export interface CallToActionSectionModel extends PageSectionModel {
|
||||
privacyDisclaimer: string
|
||||
email: FormItemModel
|
||||
privacyDisclaimer?: string
|
||||
email?: FormItemModel
|
||||
}
|
||||
|
||||
export interface FeaturedBlogsSectionModel extends PageSectionModel {
|
||||
items: BlogItemModel []
|
||||
export interface CommentsSectionModel extends PageSectionModel {
|
||||
leaveComment: string
|
||||
}
|
||||
|
||||
export interface FeaturesSectionModel extends PageSectionModel {
|
||||
items: FeatureModel []
|
||||
}
|
||||
|
||||
export interface FeaturedBlogSectionModel extends PageSectionModel {
|
||||
readTime: string
|
||||
}
|
||||
|
||||
export interface FeaturedBlogsSectionModel extends PageSectionModel {}
|
||||
|
||||
export interface ProductSectionModel extends PageSectionModel {
|
||||
availableQuantity: string,
|
||||
addToCart: string
|
||||
}
|
||||
|
||||
export interface RelatedProductsSectionModel extends PageSectionModel {}
|
||||
|
||||
export interface TestimonialsSectionModel extends PageSectionModel {
|
||||
items: TestimonialsModel []
|
||||
items: TestimonialModel []
|
||||
}
|
||||
|
||||
export interface TitleSectionModel extends PageSectionModel {
|
||||
@ -24,6 +40,18 @@ export interface TitleSectionModel extends PageSectionModel {
|
||||
secondaryLink?: MenuItemModel
|
||||
}
|
||||
|
||||
export interface CommentsSectionModel extends PageSectionModel {
|
||||
comments?: CommentModel []
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,15 @@
|
||||
import { PageModel } from "./abstractions"
|
||||
import { CallToActionSectionModel, FeaturedBlogsSectionModel, FeaturesSectionModel, TestimonialsSectionModel, TitleSectionModel } from "./pageSections"
|
||||
import { BlogTitleSectionModel, CallToActionSectionModel, CommentsSectionModel, FeaturedBlogSectionModel, FeaturedBlogsSectionModel, FeaturesSectionModel, ProductSectionModel, RelatedProductsSectionModel, TestimonialsSectionModel, TitleSectionModel } from "./pageSections"
|
||||
|
||||
export interface BlogCatalogPageModel extends PageModel {
|
||||
titleSection: TitleSectionModel,
|
||||
featuredBlogSection: FeaturedBlogSectionModel
|
||||
}
|
||||
|
||||
export interface BlogItemPageModel extends PageModel {
|
||||
titleSection: BlogTitleSectionModel,
|
||||
commentsSection: CommentsSectionModel
|
||||
}
|
||||
|
||||
export interface HomePageModel extends PageModel {
|
||||
titleSection: TitleSectionModel,
|
||||
@ -13,10 +23,7 @@ export interface ShopCatalogPageModel extends PageModel {
|
||||
titleSection: TitleSectionModel
|
||||
}
|
||||
|
||||
export interface BlogCatalogPageModel extends PageModel {
|
||||
titleSection: TitleSectionModel
|
||||
}
|
||||
|
||||
export interface BlogPageModel extends PageModel {
|
||||
|
||||
export interface ShopItemPageModel extends PageModel {
|
||||
productSection: ProductSectionModel
|
||||
relatedProductsSection: RelatedProductsSectionModel
|
||||
}
|
||||
|
||||
@ -1,13 +1,7 @@
|
||||
import { RequestModel } from "./abstractions"
|
||||
|
||||
|
||||
export interface GetShopCatalogRequestModel extends RequestModel {
|
||||
category?: string,
|
||||
searchText?: string,
|
||||
currentPage?: string,
|
||||
itemsPerPage?: string
|
||||
}
|
||||
|
||||
// Blog requests
|
||||
export interface GetBlogCatalogRequestModel extends RequestModel {
|
||||
category?: string,
|
||||
searchText?: string,
|
||||
@ -15,10 +9,33 @@ export interface GetBlogCatalogRequestModel extends RequestModel {
|
||||
itemsPerPage?: string
|
||||
}
|
||||
|
||||
export interface GetBlogCategoriesRequestModel extends RequestModel { }
|
||||
|
||||
export interface GetBlogItemRequestModel extends RequestModel {
|
||||
slug: string
|
||||
}
|
||||
|
||||
export interface GetStaticContentRequestModel extends RequestModel {
|
||||
export interface GetBlogFeaturedRequestModel extends RequestModel { }
|
||||
|
||||
// Static content
|
||||
export interface GetContentRequestModel extends RequestModel {
|
||||
locale?: string
|
||||
}
|
||||
|
||||
// Shop requests
|
||||
export interface GetShopCatalogRequestModel extends RequestModel {
|
||||
category?: string,
|
||||
searchText?: string,
|
||||
currentPage?: string,
|
||||
itemsPerPage?: string
|
||||
}
|
||||
|
||||
export interface GetShopCategoriesRequestModel extends RequestModel { }
|
||||
|
||||
export interface GetShopFeaturedRequestModel extends RequestModel { }
|
||||
|
||||
export interface GetShopItemRequestModel extends RequestModel {
|
||||
slug: string
|
||||
}
|
||||
|
||||
export interface GetShopRelatedRequestModel extends RequestModel { }
|
||||
|
||||
@ -1,22 +1,34 @@
|
||||
import { BlogItemModel, CategoryModel, CommentModel, MenuItemModel, PaginationModel, RouteModel, ShopItemModel } from "./"
|
||||
import { ResponseModel } from "./abstractions"
|
||||
import { BlogCatalogPageModel, HomePageModel, ShopCatalogPageModel } from "./pages"
|
||||
import {
|
||||
HomePageModel,
|
||||
BlogCatalogPageModel,
|
||||
BlogItemPageModel,
|
||||
ShopCatalogPageModel,
|
||||
ShopItemPageModel
|
||||
} from "./pages"
|
||||
|
||||
export interface GetBlogCatalogResponseModel extends ResponseModel {
|
||||
featuredBlog: BlogItemModel,
|
||||
categories: CategoryModel [],
|
||||
blogItemsPagination: PaginationModel<BlogItemModel>
|
||||
// Shop response models
|
||||
export interface GetShopCatalogResponseModel extends PaginationModel<ShopItemModel>, ResponseModel {}
|
||||
|
||||
export interface GetShopCategoriesResponseModel extends ResponseModel {
|
||||
items: CategoryModel []
|
||||
}
|
||||
|
||||
export interface GetBlogItemResponseModel extends ResponseModel {
|
||||
export interface GetShopFeaturedResponseModel extends ResponseModel {
|
||||
items: ShopItemModel []
|
||||
}
|
||||
|
||||
export interface GetShopItemResponseModel extends ShopItemModel, ResponseModel {
|
||||
comments: CommentModel []
|
||||
}
|
||||
|
||||
export interface GetShopCatalogResponseModel extends ResponseModel {
|
||||
shopItemsPagination: PaginationModel<ShopItemModel>
|
||||
export interface GetShopRelatedResponseModel extends ResponseModel {
|
||||
items: ShopItemModel []
|
||||
}
|
||||
|
||||
export interface GetStaticContentResponseModel extends ResponseModel {
|
||||
// Static content response model
|
||||
export interface GetContentResponseModel extends ResponseModel {
|
||||
siteName: string,
|
||||
|
||||
routes: RouteModel [],
|
||||
@ -27,13 +39,32 @@ export interface GetStaticContentResponseModel extends ResponseModel {
|
||||
sideMenu: MenuItemModel [],
|
||||
|
||||
homePage: HomePageModel,
|
||||
|
||||
shopCatalog: ShopCatalogPageModel,
|
||||
blogCatalog: BlogCatalogPageModel
|
||||
shopItem: ShopItemPageModel,
|
||||
|
||||
blogCatalog: BlogCatalogPageModel,
|
||||
blogItem: BlogItemPageModel
|
||||
}
|
||||
|
||||
// Blog response models
|
||||
export interface GetBlogCatalogResponseModel extends PaginationModel<BlogItemModel>, ResponseModel { }
|
||||
|
||||
export interface GetBlogCategoriesResponseModel extends ResponseModel {
|
||||
items: CategoryModel []
|
||||
}
|
||||
export interface GetBlogFeaturedResponseModel extends ResponseModel {
|
||||
items: BlogItemModel []
|
||||
}
|
||||
|
||||
export interface GetBlogItemResponseModel extends BlogItemModel, ResponseModel {
|
||||
comments: CommentModel []
|
||||
}
|
||||
|
||||
// Weather forecasts
|
||||
export interface GetWeatherForecastResponseModel extends ResponseModel {
|
||||
date: string,
|
||||
temperatireC: number,
|
||||
temperatureF: number,
|
||||
summary?: string
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,25 @@
|
||||
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 { BlogItemModel, PaginationModel } from '../../../models'
|
||||
import { ApplicationState } from '../../../store'
|
||||
|
||||
import { Categories, Empty, Search } from '../../../components/SideWidgets'
|
||||
import { TitleSectionModel } from '../../../models/pageSections'
|
||||
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">
|
||||
@ -27,45 +32,80 @@ const TitleSection: FC<TitleSectionModel> = (props) => {
|
||||
</header>
|
||||
}
|
||||
|
||||
interface FeaturedBlogModel extends BlogItemModel {
|
||||
currentPage: number
|
||||
path: string,
|
||||
|
||||
interface FeaturedBlog {
|
||||
path?: string,
|
||||
currentPage?: number
|
||||
item?: BlogItemModel,
|
||||
readTime?: string
|
||||
}
|
||||
|
||||
const FeaturedBlog: FC<FeaturedBlogModel> = (props) => {
|
||||
const { id, slug, badge, image, title, shortText, author, created, readTime, likes, tags, currentPage, path } = props
|
||||
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 {...image} />
|
||||
<CardImg top {...item.image} />
|
||||
<CardBody className="p-4">
|
||||
<div className="badge bg-primary bg-gradient rounded-pill mb-2">{badge}</div>
|
||||
<Link className="text-decoration-none link-dark stretched-link" to={`${path}/${currentPage}/${slug}`}>
|
||||
<h5 className="card-title mb-3">{title}</h5>
|
||||
{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: shortText }}></p>
|
||||
<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" {...author.image} />
|
||||
<img className="rounded-circle me-3" {...item.author.image} />
|
||||
<div className="small">
|
||||
<div className="fw-bold">{author.nickName}</div>
|
||||
<div className="text-muted">{dateFormat(created)} · Time to read: {readTime} min</div>
|
||||
<div className="fw-bold">{item.author.nickName}</div>
|
||||
<div className="text-muted">{readTime}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
}
|
||||
|
||||
interface BlogItemsPaginationModel extends PaginationModel<BlogItemModel> {
|
||||
path: string
|
||||
interface BlogItems {
|
||||
path?: string
|
||||
totalPages?: number,
|
||||
currentPage?: number,
|
||||
items?: BlogItemModel []
|
||||
}
|
||||
|
||||
const BlogItemsPagination: FC<BlogItemsPaginationModel> = (props) => {
|
||||
const { items, currentPage, totalPages, path } = props
|
||||
const BlogItemsSection: FC<BlogItems> = ({
|
||||
path = "",
|
||||
totalPages = 1,
|
||||
currentPage = 1,
|
||||
items = []
|
||||
}) => {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const navigate = useNavigate()
|
||||
@ -104,40 +144,52 @@ const BlogCatalog = () => {
|
||||
const params = useParams()
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const content = useSelector((state: ApplicationState) => state.content)
|
||||
const { content, blogCatalog, blogCategories, blogFeatured } = useSelector((state: ApplicationState) => state)
|
||||
const page = content?.blogCatalog
|
||||
const path = findRoutes(content?.routes, 'BlogCatalog')[0]?.targets[0]
|
||||
|
||||
const blogCatalog = useSelector((state: ApplicationState) => state.blogCatalog)
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(blogCatalogActionCreators.requestBlogCatalog({
|
||||
currentPage: params?.page ? params.page : "1"
|
||||
}))
|
||||
// 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
|
||||
// ? 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>
|
||||
{blogCatalog?.featuredBlog ? <FeaturedBlog path={path} currentPage={blogCatalog.blogItemsPagination.currentPage} {...blogCatalog.featuredBlog} /> : ''}
|
||||
<FeaturedBlogSection {...featuredBlog} />
|
||||
<Row>
|
||||
{blogCatalog?.blogItemsPagination ? <BlogItemsPagination path={path} {...blogCatalog.blogItemsPagination} /> : '' }
|
||||
<BlogItemsSection path={path} {...blogCatalog} />
|
||||
</Row>
|
||||
</Col>
|
||||
<Col lg="4">
|
||||
<Search />
|
||||
{blogCatalog?.categories ? <Categories {...{
|
||||
categories: blogCatalog.categories
|
||||
}} /> : '' }
|
||||
<Categories {...blogCategories} />
|
||||
<Empty/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// React
|
||||
import React, { useEffect } from 'react'
|
||||
import React, { FC, useEffect } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
|
||||
// Redux
|
||||
@ -7,123 +7,96 @@ import { useDispatch, useSelector } from 'react-redux'
|
||||
import { actionCreators as loaderActionCreators } from '../../../store/reducers/Loader'
|
||||
import { actionCreators as blogItemActionCreators } from '../../../store/reducers/BlogItem'
|
||||
|
||||
|
||||
|
||||
|
||||
import { Col, Container, Row } from 'reactstrap'
|
||||
|
||||
import { Comments } from '../../../components/Comments'
|
||||
import { CommentsSection } from '../../../components/Comments'
|
||||
import { Categories, Empty, Search } from '../../../components/SideWidgets'
|
||||
|
||||
import { CommentModel } from '../../../models'
|
||||
|
||||
import { ApplicationState } from '../../../store'
|
||||
import { dateFormat } from '../../../functions'
|
||||
import { ImageModel } from '../../../models'
|
||||
|
||||
const comments : CommentModel [] = [
|
||||
{
|
||||
author: {
|
||||
id: "",
|
||||
nickName: "Commenter Name 1",
|
||||
image: {
|
||||
src: "https://dummyimage.com/50x50/ced4da/6c757d.jpg",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
comment: "If you're going to lead a space frontier, it has to be government; it'll never be private enterprise. Because the space frontier is dangerous, and it's expensive, and it has unquantified risks.",
|
||||
|
||||
responses: [
|
||||
{
|
||||
author: {
|
||||
id: "",
|
||||
nickName: "Commenter Name 4",
|
||||
image: {
|
||||
src: "https://dummyimage.com/50x50/ced4da/6c757d.jpg",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
comment: "And under those conditions, you cannot establish a capital-market evaluation of that enterprise. You can't get investors."
|
||||
},
|
||||
{
|
||||
author: {
|
||||
id: "",
|
||||
nickName: "Commenter Name 3",
|
||||
image: {
|
||||
src: "https://dummyimage.com/50x50/ced4da/6c757d.jpg",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
comment: "When you put money directly to a problem, it makes a good headline."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
author: {
|
||||
id: "",
|
||||
nickName: "Commenter Name 2",
|
||||
image: {
|
||||
src: "https://dummyimage.com/50x50/ced4da/6c757d.jpg",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
comment: "When I look at the universe and all the ways the universe wants to kill us, I find it hard to reconcile that with statements of beneficence."
|
||||
interface BlogItemTitle {
|
||||
title?: string,
|
||||
postedOnBy? :string,
|
||||
badges? : string[],
|
||||
image?: ImageModel
|
||||
}
|
||||
|
||||
const BlogTitleSection: FC<BlogItemTitle> = ({
|
||||
title = "",
|
||||
postedOnBy = "",
|
||||
badges = [],
|
||||
image = {
|
||||
src: "",
|
||||
alt: ""
|
||||
}
|
||||
}) => {
|
||||
|
||||
]
|
||||
return <>
|
||||
<header className="mb-4">
|
||||
<h1 className="fw-bolder mb-1">{title}</h1>
|
||||
<div className="text-muted fst-italic mb-2">{postedOnBy ? postedOnBy : ''}</div>
|
||||
|
||||
const badges : string [] = [
|
||||
"Web Design",
|
||||
"Freebies"
|
||||
]
|
||||
{badges ? badges.map((badge, index) => <a key={index} className="badge bg-secondary text-decoration-none link-light" href="#!">{badge}</a>) : <></>}
|
||||
</header>
|
||||
|
||||
<figure className="mb-4">
|
||||
<img className="img-fluid rounded" {...image} />
|
||||
</figure>
|
||||
</>
|
||||
}
|
||||
|
||||
const BlogItem = () => {
|
||||
const params = useParams()
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const blogItem = useSelector((state: ApplicationState) => state.blogItem)
|
||||
const { content, blogItem } = useSelector((state: ApplicationState) => state)
|
||||
const page = content?.blogItem
|
||||
|
||||
useEffect(() => {
|
||||
if(params?.slug)
|
||||
dispatch(blogItemActionCreators.requestBlogItem({
|
||||
slug: params.slug
|
||||
}))
|
||||
// if(params?.slug)
|
||||
// dispatch(blogItemActionCreators.requestBlogItem({
|
||||
// slug: params.slug
|
||||
// }))
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
blogItem?.isLoading
|
||||
? dispatch(loaderActionCreators.show())
|
||||
: setTimeout(() => {
|
||||
dispatch(loaderActionCreators.hide())
|
||||
}, 1000)
|
||||
// blogItem?.isLoading
|
||||
// ? dispatch(loaderActionCreators.show())
|
||||
// : setTimeout(() => {
|
||||
// dispatch(loaderActionCreators.hide())
|
||||
// }, 1000)
|
||||
}, [blogItem?.isLoading])
|
||||
|
||||
const blogItemTitle: BlogItemTitle = {
|
||||
title: blogItem?.title,
|
||||
postedOnBy: page?.titleSection?.postedOnBy,
|
||||
badges: blogItem?.badges,
|
||||
image: blogItem?.image
|
||||
}
|
||||
|
||||
if(blogItemTitle.postedOnBy && blogItem?.created)
|
||||
blogItemTitle.postedOnBy = blogItemTitle.postedOnBy?.replace('{date}', dateFormat(blogItem.created))
|
||||
|
||||
if(blogItemTitle.postedOnBy && blogItem?.author)
|
||||
blogItemTitle.postedOnBy = blogItemTitle.postedOnBy?.replace('{nickName}', dateFormat(blogItem.author.nickName))
|
||||
|
||||
return <Container fluid mt="5">
|
||||
<Row>
|
||||
<Col lg="8">
|
||||
|
||||
<article>
|
||||
<header className="mb-4">
|
||||
<h1 className="fw-bolder mb-1">Welcome to Blog Post!</h1>
|
||||
<div className="text-muted fst-italic mb-2">Posted on January 1, 2022 by Start Bootstrap</div>
|
||||
|
||||
{badges.map((badge, index) => <a key={index} className="badge bg-secondary text-decoration-none link-light" href="#!">{badge}</a>)}
|
||||
</header>
|
||||
|
||||
<figure className="mb-4">
|
||||
<img className="img-fluid rounded" src="https://dummyimage.com/900x400/ced4da/6c757d.jpg" alt="..." />
|
||||
</figure>
|
||||
|
||||
<section className="mb-5">
|
||||
<p className="fs-5 mb-4">Science is an enterprise that should be cherished as an activity of the free human mind. Because it transforms who we are, how we live, and it gives us an understanding of our place in the universe.</p>
|
||||
<p className="fs-5 mb-4">The universe is large and old, and the ingredients for life as we know it are everywhere, so there's no reason to think that Earth would be unique in that regard. Whether of not the life became intelligent is a different question, and we'll see if we find that.</p>
|
||||
<p className="fs-5 mb-4">If you get asteroids about a kilometer in size, those are large enough and carry enough energy into our system to disrupt transportation, communication, the food chains, and that can be a really bad day on Earth.</p>
|
||||
<h2 className="fw-bolder mb-4 mt-5">I have odd cosmic thoughts every day</h2>
|
||||
<p className="fs-5 mb-4">For me, the most fascinating interface is Twitter. I have odd cosmic thoughts every day and I realized I could hold them to myself or share them with people who might be interested.</p>
|
||||
<p className="fs-5 mb-4">Venus has a runaway greenhouse effect. I kind of want to know what happened there because we're twirling knobs here on Earth without knowing the consequences of it. Mars once had running water. It's bone dry today. Something bad happened there as well.</p>
|
||||
</section>
|
||||
<BlogTitleSection {...blogItemTitle} />
|
||||
<section className="mb-5" dangerouslySetInnerHTML={{ __html: blogItem?.text ? blogItem.text : '' }}></section>
|
||||
</article>
|
||||
|
||||
<Comments comments={comments} />
|
||||
<CommentsSection {...{
|
||||
staticContent: page?.commentsSection,
|
||||
items: blogItem?.comments
|
||||
}} />
|
||||
</Col>
|
||||
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ const Counter = () => {
|
||||
const counterState = useSelector((state: IReduxState) => state.counter)
|
||||
|
||||
const increment = () => {
|
||||
dispatch(counterActionCreators.increment())
|
||||
// dispatch(counterActionCreators.increment())
|
||||
}
|
||||
|
||||
return <>
|
||||
|
||||
@ -23,9 +23,9 @@ const FetchData = () => {
|
||||
const { startDateIndex, forecasts, isLoading } = useSelector((state: IReduxState) => state.weatherForecasts)
|
||||
|
||||
const ensureDataFetched = () => {
|
||||
dispatch(weatherForecastsActionCreators.requestWeatherForecasts(
|
||||
parseInt(params.startDateIndex ? params.startDateIndex : '0', 10)
|
||||
))
|
||||
// dispatch(weatherForecastsActionCreators.requestWeatherForecasts(
|
||||
// parseInt(params.startDateIndex ? params.startDateIndex : '0', 10)
|
||||
// ))
|
||||
}
|
||||
|
||||
// This method is called when the route parameters change
|
||||
|
||||
@ -6,12 +6,14 @@ import { Link } from 'react-router-dom'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { ApplicationState } from '../../store'
|
||||
import { actionCreators as loaderActionCreators } from '../../store/reducers/Loader'
|
||||
import { actionCreators as blogFeaturedActionCreators } from '../../store/reducers/BlogFeatured'
|
||||
|
||||
// Reactstrap
|
||||
import { Card, CardBody, CardFooter, CardImg, Col, Container, Row } from 'reactstrap'
|
||||
|
||||
// Models (interfaces)
|
||||
import { CallToActionSectionModel, FeaturedBlogsSectionModel, FeaturesSectionModel, TestimonialsSectionModel, TitleSectionModel } from '../../models/pageSections'
|
||||
import { BlogItemModel, FeatureModel, TestimonialModel } from '../../models'
|
||||
|
||||
// Custom components
|
||||
import { FeatherIcon } from '../../components/FeatherIcons'
|
||||
@ -19,18 +21,20 @@ import { FeatherIcon } from '../../components/FeatherIcons'
|
||||
// Functions
|
||||
import { dateFormat } from '../../functions'
|
||||
|
||||
// CSS Modules
|
||||
import style from './scss/style.module.scss'
|
||||
|
||||
const TitleSection : FC<TitleSectionModel> = (props) => {
|
||||
const { title, text } = props
|
||||
|
||||
const TitleSection : FC<TitleSectionModel> = ({
|
||||
title = "",
|
||||
text = ""
|
||||
}) => {
|
||||
return <header className="py-5 bg-dark">
|
||||
<Container fluid className="px-5">
|
||||
<Row className="gx-5 align-items-center justify-content-center">
|
||||
<Col className="lg-8 xl-7 xxl-6">
|
||||
<div className="my-5 text-center text-xl-start">
|
||||
<h1 className="display-5 fw-bolder text-white mb-2">{title}</h1>
|
||||
<span className="lead fw-normal text-white-50 mb-4" dangerouslySetInnerHTML={{ __html: text ? text : "" }}>
|
||||
<span className="lead fw-normal text-white-50 mb-4" dangerouslySetInnerHTML={{ __html: text }}>
|
||||
|
||||
</span>
|
||||
<div className="d-grid gap-3 d-sm-flex justify-content-sm-center justify-content-xl-start">
|
||||
@ -43,14 +47,17 @@ const TitleSection : FC<TitleSectionModel> = (props) => {
|
||||
</Row>
|
||||
</Container>
|
||||
</header>
|
||||
|
||||
}
|
||||
|
||||
interface Fetures {
|
||||
title?: string,
|
||||
items?: FeatureModel []
|
||||
}
|
||||
|
||||
|
||||
const FeaturesSection: FC<FeaturesSectionModel> = (props) => {
|
||||
const { title, items } = props
|
||||
|
||||
const FeaturesSection: FC<Fetures> = ({
|
||||
title = "",
|
||||
items = []
|
||||
}) => {
|
||||
return <section className="py-5" id="features">
|
||||
<Container fluid className="px-5 my-5">
|
||||
<Row className="gx-5">
|
||||
@ -73,73 +80,89 @@ const FeaturesSection: FC<FeaturesSectionModel> = (props) => {
|
||||
</section>
|
||||
}
|
||||
|
||||
const TestimonialsSection: FC<TestimonialsSectionModel> = (props) => {
|
||||
const item = props?.items ? props?.items.shift() : undefined
|
||||
interface Testimonials {
|
||||
items?: TestimonialModel []
|
||||
}
|
||||
|
||||
if(!item) return <></>
|
||||
const TestimonialsSection: FC<Testimonials> = ({
|
||||
items = []
|
||||
}) => {
|
||||
const item = items[0]
|
||||
|
||||
return <section className="py-5 bg-light">
|
||||
<Container fluid className="px-5 my-5">
|
||||
<Row className="gx-5 justify-content-center">
|
||||
<Col className="lg-10 xl-7">
|
||||
<div className="text-center">
|
||||
<div className="fs-4 mb-4 fst-italic" dangerouslySetInnerHTML={{ __html: item.text }}></div>
|
||||
<div className="d-flex align-items-center justify-content-center">
|
||||
<img className="rounded-circle me-3" {...item.reviewer.image} />
|
||||
<div className="fw-bold">{item.reviewer.fullName}<span className="fw-bold text-primary mx-1">/</span>{item.reviewer.position}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</section>
|
||||
}
|
||||
|
||||
const FromOurBlogSection: FC<FeaturedBlogsSectionModel> = (props) => {
|
||||
const { title, text, items } = props
|
||||
|
||||
return <section className="py-5">
|
||||
<Container fluid className="px-5 my-5">
|
||||
<Row className="gx-5 justify-content-center">
|
||||
<Col className="lg-8 xl-6">
|
||||
<div className="text-center">
|
||||
<h2 className="fw-bolder">{title ? title : ''}</h2>
|
||||
<p className="lead fw-normal text-muted mb-5" dangerouslySetInnerHTML={{ __html: text ? text : '' }}></p>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="gx-5">
|
||||
{items ? items.map((item, index) => <Col key={index} className="lg-4 mb-5">
|
||||
<Card className="h-100 shadow border-0">
|
||||
<CardImg top {...item.image} />
|
||||
<CardBody className="p-4">
|
||||
<div className="badge bg-primary bg-gradient rounded-pill mb-2">{item.badge}</div>
|
||||
<Link className="text-decoration-none link-dark stretched-link" to="#!">
|
||||
<h5 className="card-title mb-3">{item.title}</h5>
|
||||
</Link>
|
||||
<p className="card-text mb-0" dangerouslySetInnerHTML={{ __html: text ? text : '' }}></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">{dateFormat(item.created)} · {item.readTime}</div>
|
||||
</div>
|
||||
{ item
|
||||
? <div className="text-center">
|
||||
<div className="fs-4 mb-4 fst-italic" dangerouslySetInnerHTML={{ __html: item.text }}></div>
|
||||
<div className="d-flex align-items-center justify-content-center">
|
||||
<img className="rounded-circle me-3" {...item.reviewer.image} />
|
||||
<div className="fw-bold">{item.reviewer.fullName}<span className="fw-bold text-primary mx-1">/</span>{item.reviewer.position}
|
||||
</div>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Col>) : ''}
|
||||
</div>
|
||||
: '' }
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
}
|
||||
|
||||
const CallToActionSection: FC<CallToActionSectionModel> = (props) => {
|
||||
const { title, text, privacyDisclaimer, email } = props
|
||||
interface FeaturedBlogs extends FeaturedBlogsSectionModel {
|
||||
items?: BlogItemModel []
|
||||
}
|
||||
const FeaturedBlogsSection: FC<FeaturedBlogs> = ({
|
||||
title = "",
|
||||
text = "",
|
||||
items = [] }) => <section className="py-5">
|
||||
<Container fluid className="px-5 my-5">
|
||||
<Row className="gx-5 justify-content-center">
|
||||
<Col className="lg-8 xl-6">
|
||||
<div className="text-center">
|
||||
<h2 className="fw-bolder">{title}</h2>
|
||||
<p className="lead fw-normal text-muted mb-5" dangerouslySetInnerHTML={{ __html: text }}></p>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="gx-5">
|
||||
{items.map((item, index) => <Col key={index} className="lg-4 mb-5">
|
||||
<Card className="h-100 shadow border-0">
|
||||
<CardImg top {...item.image} />
|
||||
<CardBody className="p-4">
|
||||
<div className="badge bg-primary bg-gradient rounded-pill mb-2">{item.badges}</div>
|
||||
<Link className="text-decoration-none link-dark stretched-link" to="#!">
|
||||
<h5 className="card-title mb-3">{item.title}</h5>
|
||||
</Link>
|
||||
<p className="card-text mb-0" dangerouslySetInnerHTML={{ __html: text ? text : '' }}></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">{dateFormat(item.created)} · {item.readTime}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Col>)}
|
||||
</Row>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
const CallToActionSection: FC<CallToActionSectionModel> = ({
|
||||
title = "",
|
||||
text = "",
|
||||
privacyDisclaimer = "",
|
||||
email = {
|
||||
placeHolder: "",
|
||||
title: ""
|
||||
}
|
||||
}) => {
|
||||
|
||||
return <section className="py-5">
|
||||
<Container fluid className="px-5 my-5">
|
||||
@ -164,26 +187,27 @@ const CallToActionSection: FC<CallToActionSectionModel> = (props) => {
|
||||
|
||||
const Home = () => {
|
||||
const dispatch = useDispatch()
|
||||
const content = useSelector((state: ApplicationState) => state.content)
|
||||
const { content, blogFeatured } = useSelector((state: ApplicationState) => state)
|
||||
const page = content?.homePage
|
||||
|
||||
useEffect(() => {
|
||||
content?.isLoading
|
||||
dispatch(blogFeaturedActionCreators.requestBlogFeatured())
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
content?.isLoading || content?.isLoading
|
||||
? dispatch(loaderActionCreators.show())
|
||||
: setTimeout(() => {
|
||||
dispatch(loaderActionCreators.hide())
|
||||
}, 1000)
|
||||
}, [content?.isLoading])
|
||||
|
||||
const page = content?.homePage
|
||||
|
||||
if(!page) return <></>
|
||||
}, [content?.isLoading, blogFeatured?.isLoading])
|
||||
|
||||
return <>
|
||||
<TitleSection {...page.titleSection} />
|
||||
<FeaturesSection {...page.featuresSection} />
|
||||
<TestimonialsSection {...page.testimonialsSection} />
|
||||
<FromOurBlogSection {...page.featuredBlogsSection} />
|
||||
<CallToActionSection {...page.callToActionSection} />
|
||||
<TitleSection {...page?.titleSection} />
|
||||
<FeaturesSection {...page?.featuresSection} />
|
||||
<TestimonialsSection {...page?.testimonialsSection} />
|
||||
<FeaturedBlogsSection items={blogFeatured?.items} {...page?.featuredBlogsSection} />
|
||||
<CallToActionSection {...page?.callToActionSection} />
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ import { actionCreators as shopCatalogActionCreators } from '../../../store/redu
|
||||
import { Card, CardBody, CardFooter, CardImg, Col, Container, Row } from 'reactstrap'
|
||||
|
||||
// Models (interfaces)
|
||||
import { PaginationModel, ShopItemModel } from '../../../models'
|
||||
import { ShopItemModel } from '../../../models'
|
||||
import { TitleSectionModel } from '../../../models/pageSections'
|
||||
|
||||
// Custom components
|
||||
@ -22,26 +22,32 @@ import { Pagination } from '../../../components/Pagination'
|
||||
// Functions
|
||||
import { findRoutes } from '../../../functions'
|
||||
|
||||
const TitleSection: FC<TitleSectionModel> = (props) => {
|
||||
const { title, text } = props
|
||||
const TitleSection: FC<TitleSectionModel> = ({
|
||||
title = "",
|
||||
text = ""
|
||||
}) => <header className="bg-dark py-5">
|
||||
<Container fluid className="px-4 px-lg-5 my-5">
|
||||
<div className="text-center text-white">
|
||||
<h1 className="display-4 fw-bolder">{title}</h1>
|
||||
<p className="lead fw-normal text-white-50 mb-0">{text}</p>
|
||||
</div>
|
||||
</Container>
|
||||
</header>
|
||||
|
||||
return <header className="bg-dark py-5">
|
||||
<Container fluid className="px-4 px-lg-5 my-5">
|
||||
|
||||
<div className="text-center text-white">
|
||||
<h1 className="display-4 fw-bolder">{title ? title : ''}</h1>
|
||||
<p className="lead fw-normal text-white-50 mb-0">{text ? text : ''}</p>
|
||||
</div>
|
||||
</Container>
|
||||
</header>
|
||||
interface ShopItems {
|
||||
path?: string
|
||||
totalPages?: number,
|
||||
currentPage?: number,
|
||||
items?: ShopItemModel []
|
||||
}
|
||||
|
||||
interface ShopItemsPaginationModel extends PaginationModel<ShopItemModel> {
|
||||
path: string
|
||||
}
|
||||
|
||||
const ShopItemsPagination: FC<ShopItemsPaginationModel> = (props) => {
|
||||
const { items, currentPage, totalPages, path } = props
|
||||
const ShopItemsSection: FC<ShopItems> = ({
|
||||
path = "",
|
||||
totalPages = 1,
|
||||
currentPage = 1,
|
||||
items = []
|
||||
}) => {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const navigate = useNavigate()
|
||||
@ -51,7 +57,7 @@ const ShopItemsPagination: FC<ShopItemsPaginationModel> = (props) => {
|
||||
<Row className="gx-4 gx-lg-5 row-cols-2 row-cols-md-3 row-cols-xl-4 justify-content-center">
|
||||
{items.map((item, index) => <Col key={index} className="mb-5">
|
||||
<Card className="h-100">
|
||||
<div className="badge bg-dark text-white position-absolute" style={{top: "0.5rem", right: "0.5rem"}}>{item.badge}</div>
|
||||
<div className="badge bg-dark text-white position-absolute" style={{top: "0.5rem", right: "0.5rem"}}>{item.badges}</div>
|
||||
|
||||
<Link to={`${path}/${currentPage}/${item.slug}`}>
|
||||
<CardImg top {...item.image} />
|
||||
@ -66,8 +72,8 @@ const ShopItemsPagination: FC<ShopItemsPaginationModel> = (props) => {
|
||||
}} />
|
||||
|
||||
{item.newPrice
|
||||
? <><span className="text-muted text-decoration-line-through">{item.price}</span> {item.newPrice}</>
|
||||
: item.price}
|
||||
? <><span className="text-muted text-decoration-line-through">{item.price}</span> <span>{item.newPrice}</span></>
|
||||
: <span>{item.price}</span>}
|
||||
|
||||
</div>
|
||||
</CardBody>
|
||||
@ -75,9 +81,7 @@ const ShopItemsPagination: FC<ShopItemsPaginationModel> = (props) => {
|
||||
<div className="text-center"><a className="btn btn-outline-dark mt-auto" href="#">Add to cart</a></div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
)}
|
||||
</Col>)}
|
||||
|
||||
|
||||
</Row>
|
||||
@ -102,28 +106,32 @@ const ShopCatalog = () => {
|
||||
const params = useParams()
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const content = useSelector((state: ApplicationState) => state.content)
|
||||
const { content, shopCatalog } = useSelector((state: ApplicationState) => state)
|
||||
const page = content?.shopCatalog
|
||||
const path = findRoutes(content?.routes, 'ShopCatalog')[0]?.targets[0]
|
||||
|
||||
const shopCatalog = useSelector((state: ApplicationState) => state.shopCatalog)
|
||||
useEffect(() => {
|
||||
dispatch(shopCatalogActionCreators.requestShopCatalog({
|
||||
currentPage: params?.page ? params.page : "1"
|
||||
}))
|
||||
// dispatch(shopCatalogActionCreators.requestShopCatalog({
|
||||
// currentPage: params?.page ? params.page : "1"
|
||||
// }))
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
shopCatalog?.isLoading
|
||||
? dispatch(loaderActionCreators.show())
|
||||
: setTimeout(() => {
|
||||
dispatch(loaderActionCreators.hide())
|
||||
}, 1000)
|
||||
// shopCatalog?.isLoading
|
||||
// ? dispatch(loaderActionCreators.show())
|
||||
// : setTimeout(() => {
|
||||
// dispatch(loaderActionCreators.hide())
|
||||
// }, 1000)
|
||||
}, [shopCatalog?.isLoading])
|
||||
|
||||
const shopItems: ShopItems = {
|
||||
path,
|
||||
...shopCatalog
|
||||
}
|
||||
|
||||
return <>
|
||||
<TitleSection {...page?.titleSection} />
|
||||
{shopCatalog?.shopItemsPagination ? <ShopItemsPagination path={path} {...shopCatalog.shopItemsPagination} /> : ''}
|
||||
<ShopItemsSection {...shopItems} />
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
@ -1,23 +1,54 @@
|
||||
import React, { FC } from 'react'
|
||||
import React, { FC, useEffect } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { actionCreators as loaderActionCreators } from '../../../store/reducers/Loader'
|
||||
import { actionCreators as shopItemActionCreators } from '../../../store/reducers/ShopItem'
|
||||
|
||||
import { Container } from 'reactstrap'
|
||||
import { FeatherIcon } from '../../../components/FeatherIcons'
|
||||
import { RelatedProducts } from '../RelatedProducts'
|
||||
import { ApplicationState } from '../../../store'
|
||||
|
||||
|
||||
const ShopItem = () => {
|
||||
const params = useParams()
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const { content, shopItem } = useSelector((state: ApplicationState) => state)
|
||||
const page = content?.shopItem
|
||||
|
||||
useEffect(() => {
|
||||
// if(params?.slug)
|
||||
// dispatch(shopItemActionCreators.requestShopItem({
|
||||
// slug: params.slug
|
||||
// }))
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
// shopItem?.isLoading
|
||||
// ? dispatch(loaderActionCreators.show())
|
||||
// : setTimeout(() => {
|
||||
// dispatch(loaderActionCreators.hide())
|
||||
// }, 1000)
|
||||
}, [shopItem?.isLoading])
|
||||
|
||||
return <>
|
||||
<section className="py-5">
|
||||
<Container fluid className="px-4 px-lg-5 my-5">
|
||||
<div className="row gx-4 gx-lg-5 align-items-center">
|
||||
<div className="col-md-6"><img className="card-img-top mb-5 mb-md-0" src="https://dummyimage.com/600x700/dee2e6/6c757d.jpg" alt="..." /></div>
|
||||
<div className="col-md-6"><img className="card-img-top mb-5 mb-md-0" {...shopItem?.image} /></div>
|
||||
<div className="col-md-6">
|
||||
<div className="small mb-1">SKU: BST-498</div>
|
||||
<h1 className="display-5 fw-bolder">Shop item template</h1>
|
||||
<div className="small mb-1">{`SKU: ${shopItem?.sku}`}</div>
|
||||
<h1 className="display-5 fw-bolder">{shopItem?.title}</h1>
|
||||
<div className="fs-5 mb-5">
|
||||
<span className="text-decoration-line-through">$45.00</span>
|
||||
<span>$40.00</span>
|
||||
{shopItem?.newPrice
|
||||
? <><span className="text-decoration-line-through">{shopItem?.price}</span> <span>{shopItem?.newPrice}</span></>
|
||||
: <span>{shopItem?.price}</span>}
|
||||
</div>
|
||||
<p className="lead">Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium at dolorem quidem modi. Nam sequi consequatur obcaecati excepturi alias magni, accusamus eius blanditiis delectus ipsam minima ea iste laborum vero?</p>
|
||||
|
||||
<section dangerouslySetInnerHTML={{ __html: shopItem?.text ? shopItem.text : '' }}></section>
|
||||
|
||||
<div className="d-flex">
|
||||
<input className="form-control text-center me-3" id="inputQuantity" type="num" value="1" style={{maxWidth: "3rem"}} />
|
||||
<button className="btn btn-outline-dark flex-shrink-0" type="button">
|
||||
|
||||
@ -3,7 +3,7 @@ import { RequestModel } from "./models/abstractions"
|
||||
|
||||
|
||||
|
||||
interface IFetchResult {
|
||||
interface FetchData {
|
||||
status: number,
|
||||
text: string
|
||||
}
|
||||
@ -40,7 +40,7 @@ const Get = async <T>(apiUrl: string, props?: RequestModel): Promise<T | null> =
|
||||
})
|
||||
|
||||
if (fetchData?.text)
|
||||
return JSON.parse((fetchData as IFetchResult).text) as T
|
||||
return JSON.parse((fetchData as FetchData).text) as T
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@ -1,45 +1,63 @@
|
||||
import * as WeatherForecasts from './reducers/WeatherForecasts'
|
||||
import * as Counter from './reducers/Counter'
|
||||
import * as BlogCatalog from './reducers/BlogCatalog'
|
||||
import * as BlogCategories from './reducers/BlogCategories'
|
||||
import * as BlogFeatured from './reducers/BlogFeatured'
|
||||
import * as BlogItem from './reducers/BlogItem'
|
||||
|
||||
import * as Counter from './reducers/Counter'
|
||||
import * as Loader from './reducers/Loader'
|
||||
|
||||
import * as Content from './reducers/Content'
|
||||
|
||||
import * as BlogCatalog from './reducers/BlogCatalog'
|
||||
import * as BlogItem from './reducers/BlogItem'
|
||||
|
||||
import * as ShopCatalog from './reducers/ShopCatalog'
|
||||
import * as ShopCategories from './reducers/ShopCategories'
|
||||
import * as ShopFeatured from './reducers/ShopFeatured'
|
||||
import * as ShopItem from './reducers/ShopItem'
|
||||
import * as ShopRelated from './reducers/ShopRelated'
|
||||
|
||||
import * as WeatherForecasts from './reducers/WeatherForecasts'
|
||||
|
||||
// The top-level state object
|
||||
export interface ApplicationState {
|
||||
counter: Counter.CounterState | undefined
|
||||
weatherForecasts: WeatherForecasts.WeatherForecastsState | undefined
|
||||
|
||||
loader: Loader.LoaderState | undefined
|
||||
blogCatalog: BlogCatalog.BlogCatalogState | undefined
|
||||
blogCategories: BlogCategories.BlogCategoriesState | undefined
|
||||
blogFeatured: BlogFeatured.BlogFeaturedState | undefined
|
||||
blogItem: BlogItem.BlogItemState | undefined
|
||||
|
||||
content: Content.ContentState | undefined
|
||||
|
||||
blogCatalog: BlogCatalog.BlogCatalogState | undefined
|
||||
blogItem: BlogItem.BlogItemState | undefined
|
||||
counter: Counter.CounterState | undefined
|
||||
loader: Loader.LoaderState | undefined
|
||||
|
||||
shopCatalog: ShopCatalog.ShopCatalogState | undefined
|
||||
shopCategories: ShopCategories.ShopCategoriesState | undefined
|
||||
shopFeatured: ShopFeatured.ShopFeaturedState | undefined
|
||||
shopItem: ShopItem.ShopItemState | undefined
|
||||
shopRelated: ShopRelated.ShopRelatedState | undefined
|
||||
|
||||
weatherForecasts: WeatherForecasts.WeatherForecastsState | undefined
|
||||
}
|
||||
|
||||
// Whenever an action is dispatched, Redux will update each top-level application state property using
|
||||
// the reducer with the matching name. It's important that the names match exactly, and that the reducer
|
||||
// acts on the corresponding ApplicationState property type.
|
||||
export const reducers = {
|
||||
counter: Counter.reducer,
|
||||
weatherForecasts: WeatherForecasts.reducer,
|
||||
|
||||
loader: Loader.reducer,
|
||||
|
||||
content: Content.reducer,
|
||||
|
||||
blogCatalog: BlogCatalog.reducer,
|
||||
blogCategories: BlogCategories.reducer,
|
||||
blogFeatured: BlogFeatured.reducer,
|
||||
blogItem: BlogItem.reducer,
|
||||
|
||||
shopCatalog: ShopCatalog.reducer
|
||||
content: Content.reducer,
|
||||
|
||||
counter: Counter.reducer,
|
||||
loader: Loader.reducer,
|
||||
|
||||
shopCatalog: ShopCatalog.reducer,
|
||||
shopCategories: ShopCategories.reducer,
|
||||
shopFeatured: ShopFeatured.reducer,
|
||||
shopItem: ShopItem.reducer,
|
||||
shopRelated: ShopRelated.reducer,
|
||||
|
||||
weatherForecasts: WeatherForecasts.reducer
|
||||
}
|
||||
|
||||
// This type can be used as a hint on action creators so that its 'dispatch' and 'getState' params are
|
||||
|
||||
@ -22,81 +22,46 @@ type KnownAction = RequestAction | ReceiveAction
|
||||
export const actionCreators = {
|
||||
requestBlogCatalog: (props?: GetBlogCatalogRequestModel): AppThunkAction<KnownAction> => (dispatch, getState) => {
|
||||
|
||||
const apiUrl = 'https://localhost:7151/api/BlogCatalog'
|
||||
|
||||
Get<Promise<GetBlogCatalogResponseModel>>(apiUrl, props)
|
||||
Get<Promise<GetBlogCatalogResponseModel>>('https://localhost:7151/api/BlogCatalog', props)
|
||||
.then(response => response)
|
||||
.then(data => {
|
||||
if(data)
|
||||
dispatch({ type: 'RECEIVE_BLOG_CATALOG', ...data })
|
||||
})
|
||||
|
||||
console.log(getState().blogCatalog)
|
||||
|
||||
dispatch({ type: 'REQUEST_BLOG_CATALOG' })
|
||||
}
|
||||
}
|
||||
|
||||
const unloadedState: BlogCatalogState = {
|
||||
featuredBlog: {
|
||||
id: "",
|
||||
slug: "demo-post",
|
||||
badge: "demo",
|
||||
image: {
|
||||
src: "https://dummyimage.com/850x350/dee2e6/6c757d.jpg",
|
||||
alt: "..."
|
||||
},
|
||||
title: "Lorem ipsum",
|
||||
shortText: "",
|
||||
text: "",
|
||||
author: {
|
||||
totalPages: 1,
|
||||
currentPage: 1,
|
||||
items: [
|
||||
{
|
||||
id: "",
|
||||
nickName: "Admin",
|
||||
slug: "demo-post",
|
||||
badges: [ "demo" ],
|
||||
image: {
|
||||
src: "https://dummyimage.com/40x40/ced4da/6c757d",
|
||||
src: "https://dummyimage.com/850x350/dee2e6/6c757d.jpg",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
created: new Date().toString(),
|
||||
tags: [],
|
||||
|
||||
likes: 0
|
||||
},
|
||||
categories: [
|
||||
{ id: "", text: "" }
|
||||
],
|
||||
|
||||
blogItemsPagination: {
|
||||
totalPages: 1,
|
||||
currentPage: 1,
|
||||
items: [
|
||||
{
|
||||
id: "",
|
||||
slug: "demo-post",
|
||||
badge: "demo",
|
||||
image: {
|
||||
src: "https://dummyimage.com/850x350/dee2e6/6c757d.jpg",
|
||||
alt: "..."
|
||||
},
|
||||
title: "Lorem ipsum",
|
||||
shortText: "",
|
||||
text: "",
|
||||
author: {
|
||||
id: "",
|
||||
nickName: "Admin",
|
||||
image: {
|
||||
src: "https://dummyimage.com/40x40/ced4da/6c757d",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
created: new Date().toString(),
|
||||
tags: [],
|
||||
|
||||
likes: 0
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
title: "Lorem ipsum",
|
||||
shortText: "",
|
||||
text: "",
|
||||
author: {
|
||||
id: "",
|
||||
nickName: "Admin",
|
||||
image: {
|
||||
src: "https://dummyimage.com/40x40/ced4da/6c757d",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
created: new Date().toString(),
|
||||
tags: [],
|
||||
|
||||
likes: 0
|
||||
},
|
||||
],
|
||||
isLoading: false
|
||||
}
|
||||
|
||||
|
||||
65
clientapp/src/store/reducers/BlogCategories.ts
Normal file
65
clientapp/src/store/reducers/BlogCategories.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { Action, Reducer } from 'redux'
|
||||
import { AppThunkAction } from '../'
|
||||
|
||||
import { GetBlogCategoriesRequestModel } from '../../models/requests'
|
||||
import { GetBlogCategoriesResponseModel } from '../../models/responses'
|
||||
import { Get } from '../../restClient'
|
||||
|
||||
export interface BlogCategoriesState extends GetBlogCategoriesResponseModel {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
interface RequestAction extends GetBlogCategoriesRequestModel {
|
||||
type: 'REQUEST_BLOG_CATEGORIES'
|
||||
}
|
||||
|
||||
interface ReceiveAction extends GetBlogCategoriesResponseModel {
|
||||
type: 'RECEIVE_BLOG_CATEGORIES'
|
||||
}
|
||||
|
||||
type KnownAction = RequestAction | ReceiveAction
|
||||
|
||||
export const actionCreators = {
|
||||
requestBlogCategories: (props?: GetBlogCategoriesRequestModel): AppThunkAction<KnownAction> => (dispatch, getState) => {
|
||||
|
||||
Get<Promise<GetBlogCategoriesResponseModel>>('https://localhost:7151/api/BlogCategories', props)
|
||||
.then(response => response)
|
||||
.then(data => {
|
||||
if(data)
|
||||
dispatch({ type: 'RECEIVE_BLOG_CATEGORIES', ...data })
|
||||
})
|
||||
|
||||
dispatch({ type: 'REQUEST_BLOG_CATEGORIES' })
|
||||
}
|
||||
}
|
||||
|
||||
const unloadedState: BlogCategoriesState = {
|
||||
items: [
|
||||
{ id: "", text: "Software" },
|
||||
{ id: "", text: "Hardware" }
|
||||
],
|
||||
isLoading: false
|
||||
}
|
||||
|
||||
export const reducer: Reducer<BlogCategoriesState> = (state: BlogCategoriesState | undefined, incomingAction: Action): BlogCategoriesState => {
|
||||
if (state === undefined) {
|
||||
return unloadedState
|
||||
}
|
||||
|
||||
const action = incomingAction as KnownAction
|
||||
switch (action.type) {
|
||||
case 'REQUEST_BLOG_CATEGORIES':
|
||||
return {
|
||||
...state,
|
||||
isLoading: true
|
||||
}
|
||||
|
||||
case 'RECEIVE_BLOG_CATEGORIES':
|
||||
return {
|
||||
...action,
|
||||
isLoading: false
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
135
clientapp/src/store/reducers/BlogFeatured.ts
Normal file
135
clientapp/src/store/reducers/BlogFeatured.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import { Action, Reducer } from 'redux'
|
||||
import { AppThunkAction } from '../'
|
||||
|
||||
import { GetBlogFeaturedRequestModel } from '../../models/requests'
|
||||
import { GetBlogFeaturedResponseModel } from '../../models/responses'
|
||||
import { Get } from '../../restClient'
|
||||
|
||||
export interface BlogFeaturedState extends GetBlogFeaturedResponseModel {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
interface RequestAction extends GetBlogFeaturedRequestModel {
|
||||
type: 'REQUEST_BLOG_FEATURED'
|
||||
}
|
||||
|
||||
interface ReceiveAction extends GetBlogFeaturedResponseModel {
|
||||
type: 'RECEIVE_BLOG_FEATURED'
|
||||
}
|
||||
|
||||
type KnownAction = RequestAction | ReceiveAction
|
||||
|
||||
export const actionCreators = {
|
||||
requestBlogFeatured: (props?: GetBlogFeaturedRequestModel): AppThunkAction<KnownAction> => (dispatch, getState) => {
|
||||
|
||||
Get<Promise<GetBlogFeaturedResponseModel>>('https://localhost:7151/api/BlogFeatured', props)
|
||||
.then(response => response)
|
||||
.then(data => {
|
||||
if(data)
|
||||
dispatch({ type: 'RECEIVE_BLOG_FEATURED', ...data })
|
||||
})
|
||||
|
||||
dispatch({ type: 'REQUEST_BLOG_FEATURED' })
|
||||
}
|
||||
}
|
||||
|
||||
const unloadedState: BlogFeaturedState = {
|
||||
items: [
|
||||
{
|
||||
id: "",
|
||||
slug: "demo-post",
|
||||
badges: [ "demo" ],
|
||||
image: {
|
||||
src: "https://dummyimage.com/850x350/dee2e6/6c757d.jpg",
|
||||
alt: "..."
|
||||
},
|
||||
title: "Lorem ipsum",
|
||||
shortText: "",
|
||||
author: {
|
||||
id: "",
|
||||
nickName: "Admin",
|
||||
image: {
|
||||
src: "https://dummyimage.com/40x40/ced4da/6c757d",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
created: new Date().toString(),
|
||||
tags: [],
|
||||
|
||||
readTime: 10,
|
||||
likes: 0
|
||||
},
|
||||
{
|
||||
id: "",
|
||||
slug: "demo-post",
|
||||
badges: [ "demo" ],
|
||||
image: {
|
||||
src: "https://dummyimage.com/850x350/dee2e6/6c757d.jpg",
|
||||
alt: "..."
|
||||
},
|
||||
title: "Lorem ipsum",
|
||||
shortText: "",
|
||||
author: {
|
||||
id: "",
|
||||
nickName: "Admin",
|
||||
image: {
|
||||
src: "https://dummyimage.com/40x40/ced4da/6c757d",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
created: new Date().toString(),
|
||||
tags: [],
|
||||
|
||||
readTime: 10,
|
||||
likes: 0
|
||||
},
|
||||
{
|
||||
id: "",
|
||||
slug: "demo-post",
|
||||
badges: [ "demo" ],
|
||||
image: {
|
||||
src: "https://dummyimage.com/850x350/dee2e6/6c757d.jpg",
|
||||
alt: "..."
|
||||
},
|
||||
title: "Lorem ipsum",
|
||||
shortText: "",
|
||||
author: {
|
||||
id: "",
|
||||
nickName: "Admin",
|
||||
image: {
|
||||
src: "https://dummyimage.com/40x40/ced4da/6c757d",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
created: new Date().toString(),
|
||||
tags: [],
|
||||
|
||||
readTime: 10,
|
||||
likes: 0
|
||||
}
|
||||
],
|
||||
isLoading: false
|
||||
}
|
||||
|
||||
export const reducer: Reducer<BlogFeaturedState> = (state: BlogFeaturedState | undefined, incomingAction: Action): BlogFeaturedState => {
|
||||
if (state === undefined) {
|
||||
return unloadedState
|
||||
}
|
||||
|
||||
const action = incomingAction as KnownAction
|
||||
switch (action.type) {
|
||||
case 'REQUEST_BLOG_FEATURED':
|
||||
return {
|
||||
...state,
|
||||
isLoading: true
|
||||
}
|
||||
|
||||
case 'RECEIVE_BLOG_FEATURED':
|
||||
return {
|
||||
...action,
|
||||
isLoading: false
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
@ -21,23 +21,47 @@ type KnownAction = RequestAction | ReceiveAction
|
||||
|
||||
export const actionCreators = {
|
||||
requestBlogItem: (props: GetBlogItemRequestModel): AppThunkAction<KnownAction> => (dispatch, getState) => {
|
||||
const apiUrl = 'https://localhost:7151/api/BlogItem'
|
||||
|
||||
Get<Promise<GetBlogItemResponseModel>>(apiUrl, props)
|
||||
Get<Promise<GetBlogItemResponseModel>>('https://localhost:7151/api/BlogItem', props)
|
||||
.then(response => response)
|
||||
.then(data => {
|
||||
if(data)
|
||||
dispatch({ type: 'RECEIVE_BLOG_ITEM', ...data })
|
||||
})
|
||||
|
||||
console.log(getState().blogItem)
|
||||
|
||||
dispatch({ type: 'REQUEST_BLOG_ITEM', slug: props.slug })
|
||||
}
|
||||
}
|
||||
|
||||
const unloadedState: BlogItemState = {
|
||||
id: "",
|
||||
slug: "demo-post",
|
||||
image: {
|
||||
src: "https://dummyimage.com/900x400/ced4da/6c757d.jpg",
|
||||
alt: "..."
|
||||
},
|
||||
badges: [
|
||||
"Web Design",
|
||||
"Freebies"
|
||||
],
|
||||
|
||||
title: "Welcome to Blog Post!",
|
||||
text: `<p className="fs-5 mb-4">Science is an enterprise that should be cherished as an activity of the free human mind. Because it transforms who we are, how we live, and it gives us an understanding of our place in the universe.</p>
|
||||
<p className="fs-5 mb-4">The universe is large and old, and the ingredients for life as we know it are everywhere, so there's no reason to think that Earth would be unique in that regard. Whether of not the life became intelligent is a different question, and we'll see if we find that.</p>
|
||||
<p className="fs-5 mb-4">If you get asteroids about a kilometer in size, those are large enough and carry enough energy into our system to disrupt transportation, communication, the food chains, and that can be a really bad day on Earth.</p>
|
||||
<h2 className="fw-bolder mb-4 mt-5">I have odd cosmic thoughts every day</h2>
|
||||
<p className="fs-5 mb-4">For me, the most fascinating interface is Twitter. I have odd cosmic thoughts every day and I realized I could hold them to myself or share them with people who might be interested.</p>
|
||||
<p className="fs-5 mb-4">Venus has a runaway greenhouse effect. I kind of want to know what happened there because we're twirling knobs here on Earth without knowing the consequences of it. Mars once had running water. It's bone dry today. Something bad happened there as well.</p>`,
|
||||
author: {
|
||||
id: "",
|
||||
nickName: "Admin",
|
||||
image: {
|
||||
src: "https://dummyimage.com/40x40/ced4da/6c757d",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
created: new Date().toString(),
|
||||
tags: [ "react", "redux", "webapi" ],
|
||||
comments: [
|
||||
{
|
||||
author: {
|
||||
|
||||
@ -1,30 +1,28 @@
|
||||
import { Action, Reducer } from 'redux'
|
||||
import { AppThunkAction } from '../'
|
||||
|
||||
import { GetStaticContentRequestModel } from '../../models/requests'
|
||||
import { GetStaticContentResponseModel } from '../../models/responses'
|
||||
import { GetContentRequestModel } from '../../models/requests'
|
||||
import { GetContentResponseModel } from '../../models/responses'
|
||||
import { Get } from '../../restClient'
|
||||
|
||||
export interface ContentState extends GetStaticContentResponseModel {
|
||||
export interface ContentState extends GetContentResponseModel {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
interface RequestAction extends GetStaticContentRequestModel {
|
||||
interface RequestAction extends GetContentRequestModel {
|
||||
type: 'REQUEST_CONTENT'
|
||||
}
|
||||
|
||||
interface ReceiveAction extends GetStaticContentResponseModel {
|
||||
interface ReceiveAction extends GetContentResponseModel {
|
||||
type: 'RECEIVE_CONTENT'
|
||||
}
|
||||
|
||||
type KnownAction = RequestAction | ReceiveAction;
|
||||
|
||||
export const actionCreators = {
|
||||
requestContent: (props?: GetStaticContentRequestModel): AppThunkAction<KnownAction> => (dispatch, getState) => {
|
||||
requestContent: (props?: GetContentRequestModel): AppThunkAction<KnownAction> => (dispatch, getState) => {
|
||||
|
||||
const apiUrl = 'https://localhost:7151/api/StaticContent'
|
||||
|
||||
Get<Promise<GetStaticContentResponseModel>>(apiUrl, props)
|
||||
Get<Promise<GetContentResponseModel>>('https://localhost:7151/api/Content', props)
|
||||
.then(response => response)
|
||||
.then((data) => {
|
||||
if(data) {
|
||||
@ -32,8 +30,6 @@ export const actionCreators = {
|
||||
}
|
||||
})
|
||||
|
||||
console.log(getState().content)
|
||||
|
||||
dispatch({ type: 'REQUEST_CONTENT' })
|
||||
}
|
||||
}
|
||||
@ -99,7 +95,7 @@ const unloadedState: ContentState = {
|
||||
]
|
||||
},
|
||||
testimonialsSection: {
|
||||
items : [
|
||||
items: [
|
||||
{
|
||||
text: "The <code>ClientApp</code> subdirectory is a standard React application based on the <code>create-react-app</code> template. If you open a command prompt in that directory, you can run <code>yarn</code> commands such as <code>yarn test</code> or <code>yarn install</code>.",
|
||||
reviewer: {
|
||||
@ -112,66 +108,7 @@ const unloadedState: ContentState = {
|
||||
]
|
||||
},
|
||||
featuredBlogsSection: {
|
||||
title: "From our blog",
|
||||
items: [
|
||||
{
|
||||
id: "",
|
||||
slug: "blog-post-title",
|
||||
image: { src: "https://dummyimage.com/600x350/ced4da/6c757d", alt: "..." },
|
||||
badge: "news",
|
||||
title: "Blog post title",
|
||||
shortText: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eaque fugit ratione dicta mollitia. Officiis ad...",
|
||||
text: "",
|
||||
author: {
|
||||
id: "",
|
||||
image: { src: "https://dummyimage.com/40x40/ced4da/6c757d", alt: "..." },
|
||||
nickName: "Admin"
|
||||
},
|
||||
created: (new Date).toString(),
|
||||
tags: [ "react", "redux", "webapi" ],
|
||||
|
||||
readTime: 10,
|
||||
likes: 200,
|
||||
},
|
||||
{
|
||||
id: "",
|
||||
slug: "blog-post-title",
|
||||
image: { src: "https://dummyimage.com/600x350/ced4da/6c757d", alt: "..." },
|
||||
badge: "news",
|
||||
title: "Blog post title",
|
||||
shortText: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eaque fugit ratione dicta mollitia. Officiis ad...",
|
||||
text: "",
|
||||
author: {
|
||||
id: "",
|
||||
image: { src: "https://dummyimage.com/40x40/ced4da/6c757d", alt: "..." },
|
||||
nickName: "Admin"
|
||||
},
|
||||
created: (new Date).toString(),
|
||||
tags: [ "react", "redux", "webapi" ],
|
||||
|
||||
readTime: 10,
|
||||
likes: 200,
|
||||
},
|
||||
{
|
||||
id: "",
|
||||
slug: "blog-post-title",
|
||||
image: { src: "https://dummyimage.com/600x350/ced4da/6c757d", alt: "..." },
|
||||
badge: "news",
|
||||
title: "Blog post title",
|
||||
shortText: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eaque fugit ratione dicta mollitia. Officiis ad...",
|
||||
text: "",
|
||||
author: {
|
||||
id: "",
|
||||
image: { src: "https://dummyimage.com/40x40/ced4da/6c757d", alt: "..." },
|
||||
nickName: "Admin"
|
||||
},
|
||||
created: (new Date).toString(),
|
||||
tags: [ "react", "redux", "webapi" ],
|
||||
|
||||
readTime: 10,
|
||||
likes: 200,
|
||||
}
|
||||
]
|
||||
title: "Featured blogs"
|
||||
},
|
||||
callToActionSection: {
|
||||
title: "New products, delivered to you.",
|
||||
@ -191,11 +128,34 @@ const unloadedState: ContentState = {
|
||||
}
|
||||
},
|
||||
|
||||
shopItem: {
|
||||
productSection: {
|
||||
availableQuantity: "Available Qty.",
|
||||
addToCart: "Add to cart"
|
||||
},
|
||||
relatedProductsSection: {
|
||||
title: "Related products"
|
||||
}
|
||||
},
|
||||
|
||||
blogCatalog: {
|
||||
titleSection: {
|
||||
title: "Welcome to Blog Home!",
|
||||
text: "A Bootstrap 5 starter layout for your next blog homepage"
|
||||
},
|
||||
featuredBlogSection: {
|
||||
readTime: "{date} Time to read: {readTime} min"
|
||||
},
|
||||
},
|
||||
|
||||
blogItem: {
|
||||
titleSection: {
|
||||
postedOnBy: "Posted on {date} by {nickName}"
|
||||
},
|
||||
commentsSection: {
|
||||
leaveComment: "Join the discussion and leave a comment!"
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
isLoading: false
|
||||
|
||||
@ -22,9 +22,7 @@ type KnownAction = RequestAction | ReceiveAction
|
||||
export const actionCreators = {
|
||||
requestShopCatalog: (props?: GetShopCatalogRequestModel): AppThunkAction<KnownAction> => (dispatch, getState) => {
|
||||
|
||||
const apiUrl = 'https://localhost:7151/api/ShopCatalog'
|
||||
|
||||
Get<Promise<GetShopCatalogResponseModel>>(apiUrl, props)
|
||||
Get<Promise<GetShopCatalogResponseModel>>('https://localhost:7151/api/ShopCatalog', props)
|
||||
.then(response => response)
|
||||
.then(data => {
|
||||
if(data)
|
||||
@ -36,37 +34,33 @@ export const actionCreators = {
|
||||
}
|
||||
|
||||
const unloadedState: ShopCatalogState = {
|
||||
shopItemsPagination: {
|
||||
totalPages: 1,
|
||||
currentPage: 1,
|
||||
totalPages: 1,
|
||||
currentPage: 1,
|
||||
items: [
|
||||
{
|
||||
id: '',
|
||||
slug: "shop-catalog-item",
|
||||
sku: "SKU-0",
|
||||
image: { src: "https://dummyimage.com/450x300/dee2e6/6c757d.jpg", alt: "..." },
|
||||
badges: [ "sale" ],
|
||||
title: "Shop item title",
|
||||
|
||||
items: [
|
||||
{
|
||||
shortText: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eaque fugit ratione dicta mollitia. Officiis ad...",
|
||||
text: "",
|
||||
author: {
|
||||
id: '',
|
||||
slug: "shop-catalog-item",
|
||||
sku: "SKU-0",
|
||||
image: { src: "https://dummyimage.com/450x300/dee2e6/6c757d.jpg", alt: "..." },
|
||||
badge: "sale",
|
||||
title: "Shop item title",
|
||||
image: { src: "https://dummyimage.com/40x40/ced4da/6c757d", alt: "..." },
|
||||
nickName: "Admin"
|
||||
},
|
||||
created: (new Date).toString(),
|
||||
|
||||
shortText: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eaque fugit ratione dicta mollitia. Officiis ad...",
|
||||
text: "",
|
||||
author: {
|
||||
id: '',
|
||||
image: { src: "https://dummyimage.com/40x40/ced4da/6c757d", alt: "..." },
|
||||
nickName: "Admin"
|
||||
},
|
||||
created: (new Date).toString(),
|
||||
|
||||
tags: [ "react", "redux", "webapi" ],
|
||||
|
||||
rating: 4.5,
|
||||
price: 20,
|
||||
newPrice: 10
|
||||
}
|
||||
]
|
||||
},
|
||||
tags: [ "react", "redux", "webapi" ],
|
||||
|
||||
rating: 4.5,
|
||||
price: 20,
|
||||
newPrice: 10
|
||||
}
|
||||
],
|
||||
isLoading: false
|
||||
}
|
||||
|
||||
|
||||
65
clientapp/src/store/reducers/ShopCategories.ts
Normal file
65
clientapp/src/store/reducers/ShopCategories.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { Action, Reducer } from 'redux'
|
||||
import { AppThunkAction } from '../'
|
||||
|
||||
import { GetShopCategoriesRequestModel } from '../../models/requests'
|
||||
import { GetShopCategoriesResponseModel } from '../../models/responses'
|
||||
import { Get } from '../../restClient'
|
||||
|
||||
export interface ShopCategoriesState extends GetShopCategoriesResponseModel {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
interface RequestAction extends GetShopCategoriesRequestModel {
|
||||
type: 'REQUEST_SHOP_CATEGORIES'
|
||||
}
|
||||
|
||||
interface ReceiveAction extends GetShopCategoriesResponseModel {
|
||||
type: 'RECEIVE_SHOP_CATEGORIES'
|
||||
}
|
||||
|
||||
type KnownAction = RequestAction | ReceiveAction
|
||||
|
||||
export const actionCreators = {
|
||||
requestShopCategories: (props?: GetShopCategoriesRequestModel): AppThunkAction<KnownAction> => (dispatch, getState) => {
|
||||
|
||||
Get<Promise<GetShopCategoriesResponseModel>>('https://localhost:7151/api/ShopCategories', props)
|
||||
.then(response => response)
|
||||
.then(data => {
|
||||
if(data)
|
||||
dispatch({ type: 'RECEIVE_SHOP_CATEGORIES', ...data })
|
||||
})
|
||||
|
||||
dispatch({ type: 'REQUEST_SHOP_CATEGORIES' })
|
||||
}
|
||||
}
|
||||
|
||||
const unloadedState: ShopCategoriesState = {
|
||||
items: [
|
||||
{ id: "", text: "Software" },
|
||||
{ id: "", text: "Hardware" }
|
||||
],
|
||||
isLoading: false
|
||||
}
|
||||
|
||||
export const reducer: Reducer<ShopCategoriesState> = (state: ShopCategoriesState | undefined, incomingAction: Action): ShopCategoriesState => {
|
||||
if (state === undefined) {
|
||||
return unloadedState
|
||||
}
|
||||
|
||||
const action = incomingAction as KnownAction
|
||||
switch (action.type) {
|
||||
case 'REQUEST_SHOP_CATEGORIES':
|
||||
return {
|
||||
...state,
|
||||
isLoading: true
|
||||
}
|
||||
|
||||
case 'RECEIVE_SHOP_CATEGORIES':
|
||||
return {
|
||||
...action,
|
||||
isLoading: false
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
86
clientapp/src/store/reducers/ShopFeatured.ts
Normal file
86
clientapp/src/store/reducers/ShopFeatured.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import { Action, Reducer } from 'redux'
|
||||
import { AppThunkAction } from '../'
|
||||
|
||||
import { GetShopFeaturedRequestModel, } from '../../models/requests'
|
||||
import { GetShopFeaturedResponseModel } from '../../models/responses'
|
||||
import { Get } from '../../restClient'
|
||||
|
||||
export interface ShopFeaturedState extends GetShopFeaturedResponseModel {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
interface RequestAction extends GetShopFeaturedRequestModel {
|
||||
type: 'REQUEST_SHOP_FEATURED'
|
||||
}
|
||||
|
||||
interface ReceiveAction extends GetShopFeaturedResponseModel {
|
||||
type: 'RECEIVE_SHOP_FEATURED'
|
||||
}
|
||||
|
||||
type KnownAction = RequestAction | ReceiveAction
|
||||
|
||||
export const actionCreators = {
|
||||
requestShopFeatured: (props?: GetShopFeaturedRequestModel): AppThunkAction<KnownAction> => (dispatch, getState) => {
|
||||
|
||||
Get<Promise<GetShopFeaturedResponseModel>>('https://localhost:7151/api/ShopFeatured', props)
|
||||
.then(response => response)
|
||||
.then(data => {
|
||||
if(data)
|
||||
dispatch({ type: 'RECEIVE_SHOP_FEATURED', ...data })
|
||||
})
|
||||
|
||||
dispatch({ type: 'REQUEST_SHOP_FEATURED' })
|
||||
}
|
||||
}
|
||||
|
||||
const unloadedState: ShopFeaturedState = {
|
||||
items: [
|
||||
{
|
||||
id: '',
|
||||
slug: "shop-catalog-item",
|
||||
sku: "SKU-0",
|
||||
image: { src: "https://dummyimage.com/450x300/dee2e6/6c757d.jpg", alt: "..." },
|
||||
badges: [ "sale" ],
|
||||
title: "Shop item title",
|
||||
|
||||
shortText: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eaque fugit ratione dicta mollitia. Officiis ad...",
|
||||
text: "",
|
||||
author: {
|
||||
id: '',
|
||||
image: { src: "https://dummyimage.com/40x40/ced4da/6c757d", alt: "..." },
|
||||
nickName: "Admin"
|
||||
},
|
||||
created: (new Date).toString(),
|
||||
|
||||
tags: [ "react", "redux", "webapi" ],
|
||||
|
||||
rating: 4.5,
|
||||
price: 20,
|
||||
newPrice: 10
|
||||
}
|
||||
],
|
||||
isLoading: false
|
||||
}
|
||||
|
||||
export const reducer: Reducer<ShopFeaturedState> = (state: ShopFeaturedState | undefined, incomingAction: Action): ShopFeaturedState => {
|
||||
if (state === undefined) {
|
||||
return unloadedState
|
||||
}
|
||||
|
||||
const action = incomingAction as KnownAction
|
||||
switch (action.type) {
|
||||
case 'REQUEST_SHOP_FEATURED':
|
||||
return {
|
||||
...state,
|
||||
isLoading: true
|
||||
}
|
||||
|
||||
case 'RECEIVE_SHOP_FEATURED':
|
||||
return {
|
||||
...action,
|
||||
isLoading: false
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
143
clientapp/src/store/reducers/ShopItem.ts
Normal file
143
clientapp/src/store/reducers/ShopItem.ts
Normal file
@ -0,0 +1,143 @@
|
||||
import { Action, Reducer } from 'redux'
|
||||
import { AppThunkAction } from '../'
|
||||
|
||||
import { GetShopItemRequestModel } from '../../models/requests'
|
||||
import { GetShopItemResponseModel } from '../../models/responses'
|
||||
import { Get } from '../../restClient'
|
||||
|
||||
|
||||
export interface ShopItemState extends GetShopItemResponseModel {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
interface RequestAction extends GetShopItemRequestModel {
|
||||
type: 'REQUEST_SHOP_ITEM'
|
||||
}
|
||||
|
||||
interface ReceiveAction extends GetShopItemResponseModel {
|
||||
type: 'RECEIVE_SHOP_ITEM'
|
||||
}
|
||||
|
||||
type KnownAction = RequestAction | ReceiveAction
|
||||
|
||||
export const actionCreators = {
|
||||
requestShopItem: (props: GetShopItemRequestModel): AppThunkAction<KnownAction> => (dispatch, getState) => {
|
||||
|
||||
Get<Promise<GetShopItemResponseModel>>('https://localhost:7151/api/ShopItem', props)
|
||||
.then(response => response)
|
||||
.then(data => {
|
||||
if(data)
|
||||
dispatch({ type: 'RECEIVE_SHOP_ITEM', ...data })
|
||||
})
|
||||
|
||||
dispatch({ type: 'REQUEST_SHOP_ITEM', slug: props.slug })
|
||||
}
|
||||
}
|
||||
|
||||
const unloadedState: ShopItemState = {
|
||||
id: "",
|
||||
slug: "demo-post",
|
||||
image: {
|
||||
src: "https://dummyimage.com/600x700/dee2e6/6c757d.jpg",
|
||||
alt: "..."
|
||||
},
|
||||
|
||||
sku: "BST-498",
|
||||
|
||||
badges: [
|
||||
"Sale"
|
||||
],
|
||||
|
||||
title: "Shop item template",
|
||||
text: `<p className="lead">Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium at dolorem quidem modi. Nam sequi consequatur obcaecati excepturi alias magni, accusamus eius blanditiis delectus ipsam minima ea iste laborum vero?</p>`,
|
||||
author: {
|
||||
id: "",
|
||||
nickName: "Admin",
|
||||
image: {
|
||||
src: "https://dummyimage.com/40x40/ced4da/6c757d",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
created: new Date().toString(),
|
||||
tags: [ "react", "redux", "webapi" ],
|
||||
|
||||
price: 20,
|
||||
newPrice: 10,
|
||||
|
||||
quantity: 10,
|
||||
|
||||
comments: [
|
||||
{
|
||||
author: {
|
||||
id: "",
|
||||
nickName: "Commenter Name 1",
|
||||
image: {
|
||||
src: "https://dummyimage.com/50x50/ced4da/6c757d.jpg",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
comment: "If you're going to lead a space frontier, it has to be government; it'll never be private enterprise. Because the space frontier is dangerous, and it's expensive, and it has unquantified risks.",
|
||||
|
||||
responses: [
|
||||
{
|
||||
author: {
|
||||
id: "",
|
||||
nickName: "Commenter Name 4",
|
||||
image: {
|
||||
src: "https://dummyimage.com/50x50/ced4da/6c757d.jpg",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
comment: "And under those conditions, you cannot establish a capital-market evaluation of that enterprise. You can't get investors."
|
||||
},
|
||||
{
|
||||
author: {
|
||||
id: "",
|
||||
nickName: "Commenter Name 3",
|
||||
image: {
|
||||
src: "https://dummyimage.com/50x50/ced4da/6c757d.jpg",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
comment: "When you put money directly to a problem, it makes a good headline."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
author: {
|
||||
id: "",
|
||||
nickName: "Commenter Name 2",
|
||||
image: {
|
||||
src: "https://dummyimage.com/50x50/ced4da/6c757d.jpg",
|
||||
alt: "..."
|
||||
}
|
||||
},
|
||||
comment: "When I look at the universe and all the ways the universe wants to kill us, I find it hard to reconcile that with statements of beneficence."
|
||||
}
|
||||
|
||||
],
|
||||
isLoading: false
|
||||
}
|
||||
|
||||
export const reducer: Reducer<ShopItemState> = (state: ShopItemState | undefined, incomingAction: Action): ShopItemState => {
|
||||
if (state === undefined) {
|
||||
return unloadedState
|
||||
}
|
||||
|
||||
const action = incomingAction as KnownAction
|
||||
switch (action.type) {
|
||||
case 'REQUEST_SHOP_ITEM':
|
||||
return {
|
||||
...state,
|
||||
isLoading: true
|
||||
}
|
||||
|
||||
case 'RECEIVE_SHOP_ITEM':
|
||||
return {
|
||||
...action,
|
||||
isLoading: false
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
88
clientapp/src/store/reducers/ShopRelated.ts
Normal file
88
clientapp/src/store/reducers/ShopRelated.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import { Action, Reducer } from 'redux'
|
||||
import { AppThunkAction } from '../'
|
||||
|
||||
import { GetShopCatalogRequestModel } from '../../models/requests'
|
||||
import { GetShopCatalogResponseModel } from '../../models/responses'
|
||||
import { Get } from '../../restClient'
|
||||
|
||||
export interface ShopRelatedState extends GetShopCatalogResponseModel {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
interface RequestAction extends GetShopCatalogRequestModel {
|
||||
type: 'REQUEST_SHOP_RELATED'
|
||||
}
|
||||
|
||||
interface ReceiveAction extends GetShopCatalogResponseModel {
|
||||
type: 'RECEIVE_SHOP_RELATED'
|
||||
}
|
||||
|
||||
type KnownAction = RequestAction | ReceiveAction
|
||||
|
||||
export const actionCreators = {
|
||||
requestShopRelated: (props?: GetShopCatalogRequestModel): AppThunkAction<KnownAction> => (dispatch, getState) => {
|
||||
|
||||
Get<Promise<GetShopCatalogResponseModel>>('https://localhost:7151/api/ShopRelated', props)
|
||||
.then(response => response)
|
||||
.then(data => {
|
||||
if(data)
|
||||
dispatch({ type: 'RECEIVE_SHOP_RELATED', ...data })
|
||||
})
|
||||
|
||||
dispatch({ type: 'REQUEST_SHOP_RELATED' })
|
||||
}
|
||||
}
|
||||
|
||||
const unloadedState: ShopRelatedState = {
|
||||
totalPages: 1,
|
||||
currentPage: 1,
|
||||
items: [
|
||||
{
|
||||
id: '',
|
||||
slug: "shop-catalog-item",
|
||||
sku: "SKU-0",
|
||||
image: { src: "https://dummyimage.com/450x300/dee2e6/6c757d.jpg", alt: "..." },
|
||||
badges: [ "sale" ],
|
||||
title: "Shop item title",
|
||||
|
||||
shortText: "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eaque fugit ratione dicta mollitia. Officiis ad...",
|
||||
text: "",
|
||||
author: {
|
||||
id: '',
|
||||
image: { src: "https://dummyimage.com/40x40/ced4da/6c757d", alt: "..." },
|
||||
nickName: "Admin"
|
||||
},
|
||||
created: (new Date).toString(),
|
||||
|
||||
tags: [ "react", "redux", "webapi" ],
|
||||
|
||||
rating: 4.5,
|
||||
price: 20,
|
||||
newPrice: 10
|
||||
}
|
||||
],
|
||||
isLoading: false
|
||||
}
|
||||
|
||||
export const reducer: Reducer<ShopRelatedState> = (state: ShopRelatedState | undefined, incomingAction: Action): ShopRelatedState => {
|
||||
if (state === undefined) {
|
||||
return unloadedState
|
||||
}
|
||||
|
||||
const action = incomingAction as KnownAction
|
||||
switch (action.type) {
|
||||
case 'REQUEST_SHOP_RELATED':
|
||||
return {
|
||||
...state,
|
||||
isLoading: true
|
||||
}
|
||||
|
||||
case 'RECEIVE_SHOP_RELATED':
|
||||
return {
|
||||
...action,
|
||||
isLoading: false
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
@ -7,7 +7,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Core.Models {
|
||||
public class PaginationModel<T> {
|
||||
public class PaginationModel<T> : ResponseModel {
|
||||
public int TotalPages { get; set; }
|
||||
public int CurrentPage { get; set; }
|
||||
public List<T> Items { get; set; }
|
||||
|
||||
@ -14,9 +14,9 @@ namespace WeatherForecast.Controllers;
|
||||
[Route("api/[controller]")]
|
||||
public class BlogCatalogController : ControllerBase {
|
||||
|
||||
private readonly ILogger<LoginController> _logger;
|
||||
private readonly ILogger<BlogCatalogController> _logger;
|
||||
|
||||
public BlogCatalogController(ILogger<LoginController> logger) {
|
||||
public BlogCatalogController(ILogger<BlogCatalogController> logger) {
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,44 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using WeatherForecast.Models;
|
||||
using WeatherForecast.Models.Responses;
|
||||
|
||||
namespace WeatherForecast.Controllers {
|
||||
|
||||
[AllowAnonymous]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class BlogCategoriesController : ControllerBase {
|
||||
|
||||
private readonly ILogger<BlogCategoriesController> _logger;
|
||||
|
||||
public BlogCategoriesController(ILogger<BlogCategoriesController> logger) {
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public IActionResult Get() {
|
||||
|
||||
var categories = new List<CategoryModel> {
|
||||
new CategoryModel {
|
||||
Id = Guid.NewGuid(),
|
||||
Text = "Software"
|
||||
},
|
||||
new CategoryModel {
|
||||
Id = Guid.NewGuid(),
|
||||
Text = "Hardware"
|
||||
},
|
||||
};
|
||||
|
||||
var response = new GetBlogCategoriesResponseModel {
|
||||
Items = categories
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
57
webapi/WeatherForecast/Controllers/BlogFeaturedController.cs
Normal file
57
webapi/WeatherForecast/Controllers/BlogFeaturedController.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using WeatherForecast.Models;
|
||||
using WeatherForecast.Models.Responses;
|
||||
|
||||
namespace WeatherForecast.Controllers {
|
||||
|
||||
[AllowAnonymous]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class BlogFeaturedController : ControllerBase {
|
||||
|
||||
private readonly ILogger<BlogFeaturedController> _logger;
|
||||
|
||||
public BlogFeaturedController(ILogger<BlogFeaturedController> logger) {
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public IActionResult Get() {
|
||||
#if MOCK_SERVER
|
||||
var blogItems = new List<BlogItemModel>();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
blogItems.Add(new BlogItemModel {
|
||||
Id = Guid.NewGuid(),
|
||||
Slug = "blog-post-title",
|
||||
Image = new ImageModel { Src = "https://dummyimage.com/600x350/ced4da/6c757d", Alt = "..." },
|
||||
Badge = "news",
|
||||
Title = "C# Blog post title",
|
||||
ShortText = "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eaque fugit ratione dicta mollitia. Officiis ad...",
|
||||
Text = "",
|
||||
Author = new AuthorModel {
|
||||
Id = Guid.NewGuid(),
|
||||
Image = new ImageModel { Src = "https://dummyimage.com/40x40/ced4da/6c757d", Alt = "..." },
|
||||
NickName = "Admin"
|
||||
},
|
||||
Created = DateTime.UtcNow,
|
||||
Tags = new List<string> { "react", "redux", "webapi" },
|
||||
|
||||
ReadTime = 10,
|
||||
Likes = 200,
|
||||
});
|
||||
}
|
||||
|
||||
var result = new GetBlogFeaturedResponseModel(blogItems);
|
||||
|
||||
return Ok(result);
|
||||
#else
|
||||
return Ok();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
26
webapi/WeatherForecast/Controllers/BlogItemController.cs
Normal file
26
webapi/WeatherForecast/Controllers/BlogItemController.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace WeatherForecast.Controllers {
|
||||
|
||||
[AllowAnonymous]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class BlogItemController : ControllerBase {
|
||||
|
||||
private readonly ILogger<BlogItemController> _logger;
|
||||
|
||||
public BlogItemController(ILogger<BlogItemController> logger) {
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public IActionResult Get() {
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,16 +14,16 @@ namespace WeatherForecast.Controllers;
|
||||
[ApiController]
|
||||
[AllowAnonymous]
|
||||
[Route("api/[controller]")]
|
||||
public class StaticContentController : ControllerBase {
|
||||
public class ContentController : ControllerBase {
|
||||
|
||||
private readonly ILogger<StaticContentController> _logger;
|
||||
private readonly ILogger<ContentController> _logger;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
public StaticContentController(
|
||||
ILogger<StaticContentController> logger
|
||||
public ContentController(
|
||||
ILogger<ContentController> logger
|
||||
) {
|
||||
_logger = logger;
|
||||
}
|
||||
@ -103,30 +103,7 @@ public class StaticContentController : ControllerBase {
|
||||
|
||||
|
||||
|
||||
var blogItems = new List<BlogItemModel>();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
var blogItemModel = new BlogItemModel {
|
||||
Id = Guid.NewGuid(),
|
||||
Slug = "blog-post-title",
|
||||
Image = new ImageModel { Src = "https://dummyimage.com/600x350/ced4da/6c757d", Alt = "..." },
|
||||
Badge = "news",
|
||||
Title = "Blog post title",
|
||||
ShortText = "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eaque fugit ratione dicta mollitia. Officiis ad...",
|
||||
Text = "",
|
||||
Author = new AuthorModel {
|
||||
Id = Guid.NewGuid(),
|
||||
Image = new ImageModel { Src = "https://dummyimage.com/40x40/ced4da/6c757d", Alt = "..." },
|
||||
NickName = "Admin"
|
||||
},
|
||||
Created = DateTime.UtcNow,
|
||||
Tags = new List<string> { "react", "redux", "webapi" },
|
||||
|
||||
ReadTime = 10,
|
||||
Likes = 200,
|
||||
};
|
||||
|
||||
blogItems.Add(blogItemModel);
|
||||
}
|
||||
|
||||
|
||||
|
||||
var homePage = new HomePageModel {
|
||||
@ -169,6 +146,7 @@ public class StaticContentController : ControllerBase {
|
||||
new TestimonialModel {
|
||||
Text = "The <code>ClientApp</code> subdirectory is a standard React application based on the <code>create-react-app</code> template. If you open a command prompt in that directory, you can run <code>yarn</code> commands such as <code>yarn test</code> or <code>yarn install</code>.",
|
||||
Reviewer = new ReviewerModel {
|
||||
Id = Guid.NewGuid(),
|
||||
Image = new ImageModel { Src = "https://dummyimage.com/40x40/ced4da/6c757d", Alt = "..." },
|
||||
FullName = "Admin",
|
||||
Position = "CEO, MAKS-IT"
|
||||
@ -177,8 +155,7 @@ public class StaticContentController : ControllerBase {
|
||||
}
|
||||
},
|
||||
FeaturedBlogsSection = new FeaturedBologsSectionModel {
|
||||
Title = "From our blog",
|
||||
Items = blogItems
|
||||
Title = "From our blog"
|
||||
},
|
||||
|
||||
CallToActionSection = new CallToActionSectionModel {
|
||||
@ -199,14 +176,36 @@ public class StaticContentController : ControllerBase {
|
||||
}
|
||||
};
|
||||
|
||||
var shopItem = new ShopItemPageModel {
|
||||
ProductSection = new ProductSectionModel {
|
||||
AvailableQuantity = "Available Qty.",
|
||||
AddToCart = "Add to cart"
|
||||
},
|
||||
RelatedProductsSection = new RelatedProductsSectionModel {
|
||||
Title = "Related products"
|
||||
}
|
||||
};
|
||||
|
||||
var blogCatalogPage = new BlogCatalogPageModel {
|
||||
TitleSection = new TitleSectionModel {
|
||||
Title = "Welcome to Blog Home!",
|
||||
Text = "A Bootstrap 5 starter layout for your next blog homepage"
|
||||
},
|
||||
FeaturedBlogSection = new FeaturedBlogSectionModel {
|
||||
ReadTime = "{date} Time to read: {readTime} min"
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(new GetStaticContentResponseModel {
|
||||
var blogItem = new BlogItemPageModel {
|
||||
TitleSection = new BlogTitleSectionModel {
|
||||
PostedOnBy = "Posted on {date} by {nickName}"
|
||||
},
|
||||
CommentsSection = new CommentsSectionModel {
|
||||
LeaveComment = "Join the discussion and leave a comment!"
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(new GetContentResponseModel {
|
||||
SiteName = "MAKS-IT",
|
||||
|
||||
Routes = routes,
|
||||
@ -215,9 +214,14 @@ public class StaticContentController : ControllerBase {
|
||||
|
||||
TopMenu = topMenu,
|
||||
SideMenu = sideMenu,
|
||||
|
||||
HomePage = homePage,
|
||||
|
||||
ShopCatalog = shopCatalogPage,
|
||||
BlogCatalog = blogCatalogPage
|
||||
ShopItem = shopItem,
|
||||
|
||||
BlogCatalog = blogCatalogPage,
|
||||
Blogitem = blogItem
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -12,9 +12,9 @@ namespace WeatherForecast.Controllers;
|
||||
[Route("api/[controller]")]
|
||||
public class ShopCatalogController : ControllerBase {
|
||||
|
||||
private readonly ILogger<LoginController> _logger;
|
||||
private readonly ILogger<ShopCatalogController> _logger;
|
||||
|
||||
public ShopCatalogController(ILogger<LoginController> logger) {
|
||||
public ShopCatalogController(ILogger<ShopCatalogController> logger) {
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using WeatherForecast.Models;
|
||||
|
||||
namespace WeatherForecast.Controllers {
|
||||
|
||||
[AllowAnonymous]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class ShopCategoriesController : ControllerBase {
|
||||
|
||||
private readonly ILogger<ShopCategoriesController> _logger;
|
||||
|
||||
public ShopCategoriesController(ILogger<ShopCategoriesController> logger) {
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public IActionResult Get() {
|
||||
|
||||
var categories = new List<CategoryModel> {
|
||||
new CategoryModel {
|
||||
Id = Guid.NewGuid(),
|
||||
Text = "Software"
|
||||
},
|
||||
new CategoryModel {
|
||||
Id = Guid.NewGuid(),
|
||||
Text = "Hardware"
|
||||
},
|
||||
};
|
||||
|
||||
return Ok(categories);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
webapi/WeatherForecast/Controllers/ShopFeaturedController.cs
Normal file
26
webapi/WeatherForecast/Controllers/ShopFeaturedController.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace WeatherForecast.Controllers {
|
||||
|
||||
[AllowAnonymous]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class ShopFeaturedController : ControllerBase {
|
||||
|
||||
private readonly ILogger<ShopFeaturedController> _logger;
|
||||
|
||||
public ShopFeaturedController(ILogger<ShopFeaturedController> logger) {
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public IActionResult Get() {
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
26
webapi/WeatherForecast/Controllers/ShopItemController.cs
Normal file
26
webapi/WeatherForecast/Controllers/ShopItemController.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace WeatherForecast.Controllers {
|
||||
|
||||
[AllowAnonymous]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class ShopItemController : ControllerBase {
|
||||
|
||||
private readonly ILogger<ShopItemController> _logger;
|
||||
|
||||
public ShopItemController(ILogger<ShopItemController> logger) {
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public IActionResult Get() {
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
26
webapi/WeatherForecast/Controllers/ShopRelatedController.cs
Normal file
26
webapi/WeatherForecast/Controllers/ShopRelatedController.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace WeatherForecast.Controllers {
|
||||
|
||||
[AllowAnonymous]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class ShopRelatedController : ControllerBase {
|
||||
|
||||
private readonly ILogger<ShopRelatedController> _logger;
|
||||
|
||||
public ShopRelatedController(ILogger<ShopRelatedController> logger) {
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public IActionResult Get() {
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
namespace WeatherForecast.Models.Abstractions {
|
||||
public abstract class PersonModel {
|
||||
public Guid Id { get; set; }
|
||||
public ImageModel Image { get; set; }
|
||||
public ImageModel? Image { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,9 +10,9 @@
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public string ShortText { get; set; }
|
||||
public string? ShortText { get; set; }
|
||||
|
||||
public string Text { get; set; }
|
||||
public string? Text { get; set; }
|
||||
|
||||
public AuthorModel Author { get; set; }
|
||||
|
||||
|
||||
@ -4,9 +4,9 @@ namespace WeatherForecast.Models {
|
||||
public class BlogItemModel : PostItemModel {
|
||||
|
||||
|
||||
public int ReadTime { get; set; }
|
||||
public int? ReadTime { get; set; }
|
||||
|
||||
public int Likes { get; set; }
|
||||
public int? Likes { get; set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
7
webapi/WeatherForecast/Models/CommentModel.cs
Normal file
7
webapi/WeatherForecast/Models/CommentModel.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace WeatherForecast.Models {
|
||||
public class CommentModel {
|
||||
public AuthorModel Author { get; set; }
|
||||
public string Comment { get; set; }
|
||||
public List<CommentModel>? Responses { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
using WeatherForecast.Models.Abstractions;
|
||||
|
||||
namespace WeatherForecast.Models.PageSections {
|
||||
public class BlogTitleSectionModel : PageSectionModel {
|
||||
public string PostedOnBy { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
using WeatherForecast.Models.Abstractions;
|
||||
|
||||
namespace WeatherForecast.Models.PageSections {
|
||||
public class CommentsSectionModel : PageSectionModel {
|
||||
public string LeaveComment { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
using WeatherForecast.Models.Abstractions;
|
||||
|
||||
namespace WeatherForecast.Models.PageSections {
|
||||
public class FeaturedBlogSectionModel : PageSectionModel {
|
||||
public string ReadTime { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,5 @@
|
||||
using WeatherForecast.Models.Abstractions;
|
||||
|
||||
namespace WeatherForecast.Models.PageSections {
|
||||
public class FeaturedBologsSectionModel : PageSectionModel {
|
||||
public List<BlogItemModel> Items { get; set; }
|
||||
}
|
||||
public class FeaturedBologsSectionModel : PageSectionModel { }
|
||||
}
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
using WeatherForecast.Models.Abstractions;
|
||||
|
||||
namespace WeatherForecast.Models.PageSections {
|
||||
public class ProductSectionModel : PageSectionModel {
|
||||
public string AvailableQuantity { get; set; }
|
||||
public string AddToCart { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
using WeatherForecast.Models.Abstractions;
|
||||
|
||||
namespace WeatherForecast.Models.PageSections {
|
||||
public class RelatedProductsSectionModel : PageSectionModel {
|
||||
|
||||
}
|
||||
}
|
||||
@ -4,5 +4,6 @@ using WeatherForecast.Models.PageSections;
|
||||
namespace WeatherForecast.Models.Pages {
|
||||
public class BlogCatalogPageModel : PageModel {
|
||||
public TitleSectionModel TitleSection { get; set; }
|
||||
public FeaturedBlogSectionModel FeaturedBlogSection { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
9
webapi/WeatherForecast/Models/Pages/BlogItemPageModel.cs
Normal file
9
webapi/WeatherForecast/Models/Pages/BlogItemPageModel.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using WeatherForecast.Models.Abstractions;
|
||||
using WeatherForecast.Models.PageSections;
|
||||
|
||||
namespace WeatherForecast.Models.Pages {
|
||||
public class BlogItemPageModel : PageModel {
|
||||
public BlogTitleSectionModel TitleSection { get; set; }
|
||||
public CommentsSectionModel CommentsSection { get; set; }
|
||||
}
|
||||
}
|
||||
9
webapi/WeatherForecast/Models/Pages/ShopItemPageModel.cs
Normal file
9
webapi/WeatherForecast/Models/Pages/ShopItemPageModel.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using WeatherForecast.Models.Abstractions;
|
||||
using WeatherForecast.Models.PageSections;
|
||||
|
||||
namespace WeatherForecast.Models.Pages {
|
||||
public class ShopItemPageModel : PageModel {
|
||||
public ProductSectionModel ProductSection { get; set; }
|
||||
public RelatedProductsSectionModel RelatedProductsSection { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
using Core.Abstractions.Models;
|
||||
|
||||
namespace WeatherForecast.Models.Responses {
|
||||
public class GetBlogCategoriesResponseModel : ResponseModel {
|
||||
public List<CategoryModel> Items { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
using Core.Abstractions.Models;
|
||||
|
||||
namespace WeatherForecast.Models.Responses {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class GetBlogFeaturedResponseModel : ResponseModel {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public List<BlogItemModel> Items { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
public GetBlogFeaturedResponseModel(List<BlogItemModel> items) {
|
||||
Items = items;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
using WeatherForecast.Models.Pages;
|
||||
|
||||
namespace WeatherForecast.Models.Responses {
|
||||
public class GetStaticContentResponseModel : ResponseModel {
|
||||
public class GetContentResponseModel : ResponseModel {
|
||||
public string SiteName { get; set; }
|
||||
|
||||
public List<RouteModel> Routes { get; set; }
|
||||
@ -13,7 +13,11 @@ namespace WeatherForecast.Models.Responses {
|
||||
public List<MenuItemModel> SideMenu { get; set; }
|
||||
|
||||
public HomePageModel HomePage { get; set; }
|
||||
|
||||
public ShopCatalogPageModel ShopCatalog { get; set; }
|
||||
public ShopItemPageModel ShopItem { get; set; }
|
||||
|
||||
public BlogCatalogPageModel BlogCatalog { get; set; }
|
||||
public BlogItemPageModel Blogitem { get; set; }
|
||||
}
|
||||
}
|
||||
@ -2,11 +2,11 @@
|
||||
|
||||
namespace WeatherForecast.Models {
|
||||
public class ShopItemModel : PostItemModel {
|
||||
public List<ImageModel> Images { get; set; }
|
||||
public List<ImageModel>? Images { get; set; }
|
||||
public string Sku { get; set; }
|
||||
public double Rating { get; set; }
|
||||
public double? Rating { get; set; }
|
||||
public double Price { get; set; }
|
||||
public double NewPrice { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public double? NewPrice { get; set; }
|
||||
public int? Quantity { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +31,9 @@
|
||||
"commandName": "Docker",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"publishAllPorts": true,
|
||||
"useSSL": true
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>WeatherForecast</RootNamespace>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<DefineConstants>TRACE;DEBUG;MOCK_SERVER</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user