diff --git a/clientapp/src/components/Comments/index.tsx b/clientapp/src/components/Comments/index.tsx new file mode 100644 index 0000000..e0ed40c --- /dev/null +++ b/clientapp/src/components/Comments/index.tsx @@ -0,0 +1,46 @@ +import React, { FC } from 'react' +import { Card, CardBody } from 'reactstrap' +import { CommentsSectionModel } from '../../models/pageSections' + +const Comments: FC = ({ + comments = [] +}) => { + + + return
+ + +
+ +
+ + {comments.map((comment, index) =>
+
+ +
+
+
{comment.author.nickName}
+ {comment.comment} + + {comment.responses? comment.responses.map((response, index) =>
+
+ +
+
+
{response.author.nickName}
+ {response.comment} +
+
) : ''} +
+
)} + + +
+
+
+ +} + +export { + Comments +} \ No newline at end of file diff --git a/clientapp/src/pages/Blog/SideWidgets/index.tsx b/clientapp/src/components/SideWidgets/index.tsx similarity index 97% rename from clientapp/src/pages/Blog/SideWidgets/index.tsx rename to clientapp/src/components/SideWidgets/index.tsx index 8af7df7..9750dfa 100644 --- a/clientapp/src/pages/Blog/SideWidgets/index.tsx +++ b/clientapp/src/components/SideWidgets/index.tsx @@ -1,6 +1,6 @@ import React from 'react' import { Card, CardBody, CardHeader, Col, Row } from 'reactstrap' -import { CategoryModel } from '../../../models' +import { CategoryModel } from '../../models' const Search = () => { return diff --git a/clientapp/src/models/abstractions.ts b/clientapp/src/models/abstractions.ts index e576289..6046a1f 100644 --- a/clientapp/src/models/abstractions.ts +++ b/clientapp/src/models/abstractions.ts @@ -2,7 +2,7 @@ import { AuthorModel, ImageModel } from "./" export interface RequestModel { - + [key: string]: string | undefined } export interface ResponseModel { diff --git a/clientapp/src/models/index.ts b/clientapp/src/models/index.ts index efb9bf8..d8f3da1 100644 --- a/clientapp/src/models/index.ts +++ b/clientapp/src/models/index.ts @@ -67,3 +67,8 @@ export interface FormItemModel { placeHolder?: string } +export interface CommentModel { + author: AuthorModel, + comment: string, + responses?: CommentModel [] +} \ No newline at end of file diff --git a/clientapp/src/models/pageSections.ts b/clientapp/src/models/pageSections.ts index b12aef8..090af66 100644 --- a/clientapp/src/models/pageSections.ts +++ b/clientapp/src/models/pageSections.ts @@ -1,4 +1,4 @@ -import { BlogItemModel, FeatureModel, FormItemModel, ImageModel, MenuItemModel, TestimonialsModel } from "./" +import { BlogItemModel, CommentModel, FeatureModel, FormItemModel, ImageModel, MenuItemModel, TestimonialsModel } from "./" import { PageSectionModel } from "./abstractions" export interface CallToActionSectionModel extends PageSectionModel { @@ -23,3 +23,7 @@ export interface TitleSectionModel extends PageSectionModel { primaryLink?: MenuItemModel, secondaryLink?: MenuItemModel } + +export interface CommentsSectionModel extends PageSectionModel { + comments?: CommentModel [] +} \ No newline at end of file diff --git a/clientapp/src/models/pages.ts b/clientapp/src/models/pages.ts index 6e3224f..436b519 100644 --- a/clientapp/src/models/pages.ts +++ b/clientapp/src/models/pages.ts @@ -16,3 +16,7 @@ export interface ShopCatalogPageModel extends PageModel { export interface BlogCatalogPageModel extends PageModel { titleSection: TitleSectionModel } + +export interface BlogPageModel extends PageModel { + +} diff --git a/clientapp/src/models/requests.ts b/clientapp/src/models/requests.ts index be97b32..36d3b48 100644 --- a/clientapp/src/models/requests.ts +++ b/clientapp/src/models/requests.ts @@ -1,22 +1,24 @@ +import { RequestModel } from "./abstractions" -export interface GetShopCatalogRequestModel { - [key: string]: string | undefined +export interface GetShopCatalogRequestModel extends RequestModel { category?: string, searchText?: string, currentPage?: string, itemsPerPage?: string } -export interface GetBlogCatalogRequestModel { - [key: string]: string | undefined +export interface GetBlogCatalogRequestModel extends RequestModel { category?: string, searchText?: string, currentPage?: string, itemsPerPage?: string } -export interface GetStaticContentRequestModel { - [key: string]: string | undefined +export interface GetBlogItemRequestModel extends RequestModel { + slug: string +} + +export interface GetStaticContentRequestModel extends RequestModel { locale?: string } diff --git a/clientapp/src/models/responses.ts b/clientapp/src/models/responses.ts index cf8556c..f4d99ce 100644 --- a/clientapp/src/models/responses.ts +++ b/clientapp/src/models/responses.ts @@ -1,4 +1,4 @@ -import { BlogItemModel, CategoryModel, MenuItemModel, PaginationModel, RouteModel, ShopItemModel } from "./" +import { BlogItemModel, CategoryModel, CommentModel, MenuItemModel, PaginationModel, RouteModel, ShopItemModel } from "./" import { ResponseModel } from "./abstractions" import { BlogCatalogPageModel, HomePageModel, ShopCatalogPageModel } from "./pages" @@ -8,6 +8,10 @@ export interface GetBlogCatalogResponseModel extends ResponseModel { blogItemsPagination: PaginationModel } +export interface GetBlogItemResponseModel extends ResponseModel { + comments: CommentModel [] +} + export interface GetShopCatalogResponseModel extends ResponseModel { shopItemsPagination: PaginationModel } diff --git a/clientapp/src/pages/Blog/Catalog/index.tsx b/clientapp/src/pages/Blog/Catalog/index.tsx index c5824e3..155dd47 100644 --- a/clientapp/src/pages/Blog/Catalog/index.tsx +++ b/clientapp/src/pages/Blog/Catalog/index.tsx @@ -11,7 +11,7 @@ import { dateFormat, findRoutes } from '../../../functions' import { BlogItemModel, PaginationModel } from '../../../models' import { ApplicationState } from '../../../store' -import { Categories, Empty, Search } from '../SideWidgets' +import { Categories, Empty, Search } from '../../../components/SideWidgets' import { TitleSectionModel } from '../../../models/pageSections' import { Pagination } from '../../../components/Pagination' @@ -27,14 +27,20 @@ const TitleSection: FC = (props) => { } -const FeaturedBlog: FC = (props) => { - const { id, slug, badge, image, title, shortText, author, created, readTime, likes, tags } = props +interface FeaturedBlogModel extends BlogItemModel { + currentPage: number + path: string, + +} + +const FeaturedBlog: FC = (props) => { + const { id, slug, badge, image, title, shortText, author, created, readTime, likes, tags, currentPage, path } = props return
{badge}
- +
{title}

@@ -122,7 +128,7 @@ const BlogCatalog = () => { - {blogCatalog?.featuredBlog ? : ''} + {blogCatalog?.featuredBlog ? : ''} {blogCatalog?.blogItemsPagination ? : '' } diff --git a/clientapp/src/pages/Blog/Item/index.tsx b/clientapp/src/pages/Blog/Item/index.tsx index 19e0093..1523883 100644 --- a/clientapp/src/pages/Blog/Item/index.tsx +++ b/clientapp/src/pages/Blog/Item/index.tsx @@ -1,13 +1,101 @@ -import React from 'react' -import { Card, CardBody, CardHeader, Col, Container, Row } from 'reactstrap' -import { Categories, Empty, Search } from '../SideWidgets' +// React +import React, { useEffect } from 'react' +import { useParams } from 'react-router-dom' + +// Redux +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 { Categories, Empty, Search } from '../../../components/SideWidgets' + +import { CommentModel } from '../../../models' +import { ApplicationState } from '../../../store' + +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." + } + +] + +const badges : string [] = [ + "Web Design", + "Freebies" +] + const BlogItem = () => { + const params = useParams() + const dispatch = useDispatch() + + const blogItem = useSelector((state: ApplicationState) => state.blogItem) + useEffect(() => { + if(params?.slug) + dispatch(blogItemActionCreators.requestBlogItem({ + slug: params.slug + })) + }, []) + + useEffect(() => { + blogItem?.isLoading + ? dispatch(loaderActionCreators.show()) + : setTimeout(() => { + dispatch(loaderActionCreators.hide()) + }, 1000) + }, [blogItem?.isLoading]) - const postItem = { - comments: [] - } return @@ -17,11 +105,13 @@ const BlogItem = () => {

Welcome to Blog Post!

Posted on January 1, 2022 by Start Bootstrap
- Web Design - Freebies + + {badges.map((badge, index) => {badge})}
-
...
+
+ ... +

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.

@@ -33,53 +123,7 @@ const BlogItem = () => {
- - - - - - -
-
-
-
- -
-
- -
...
-
-
Commenter Name
- 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. - -
-
...
-
-
Commenter Name
- And under those conditions, you cannot establish a capital-market evaluation of that enterprise. You can't get investors. -
-
- -
-
...
-
-
Commenter Name
- When you put money directly to a problem, it makes a good headline. -
-
-
-
- -
-
...
-
-
Commenter Name
- 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. -
-
-
-
-
+ diff --git a/clientapp/src/restClient.ts b/clientapp/src/restClient.ts index 171ddf6..0ff13d7 100644 --- a/clientapp/src/restClient.ts +++ b/clientapp/src/restClient.ts @@ -1,8 +1,7 @@ +import { RequestModel } from "./models/abstractions" + -export interface IRequest { - [key: string]: string | undefined -} interface IFetchResult { status: number, @@ -13,7 +12,7 @@ const Post = () => { } -const Get = async (apiUrl: string, props?: IRequest): Promise => { +const Get = async (apiUrl: string, props?: RequestModel): Promise => { const url = new URL(apiUrl) if(props) { diff --git a/clientapp/src/store/index.ts b/clientapp/src/store/index.ts index 73001f8..b20d4b8 100644 --- a/clientapp/src/store/index.ts +++ b/clientapp/src/store/index.ts @@ -4,9 +4,11 @@ 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 ShopCatalog from './reducers/ShopCatalog' +import * as BlogCatalog from './reducers/BlogCatalog' +import * as BlogItem from './reducers/BlogItem' + +import * as ShopCatalog from './reducers/ShopCatalog' // The top-level state object export interface ApplicationState { @@ -16,7 +18,10 @@ export interface ApplicationState { loader: Loader.LoaderState | undefined content: Content.ContentState | undefined + blogCatalog: BlogCatalog.BlogCatalogState | undefined + blogItem: BlogItem.BlogItemState | undefined + shopCatalog: ShopCatalog.ShopCatalogState | undefined } @@ -30,7 +35,10 @@ export const reducers = { loader: Loader.reducer, content: Content.reducer, + blogCatalog: BlogCatalog.reducer, + blogItem: BlogItem.reducer, + shopCatalog: ShopCatalog.reducer } diff --git a/clientapp/src/store/reducers/BlogCatalog.ts b/clientapp/src/store/reducers/BlogCatalog.ts index f276d7a..e0218e3 100644 --- a/clientapp/src/store/reducers/BlogCatalog.ts +++ b/clientapp/src/store/reducers/BlogCatalog.ts @@ -17,7 +17,7 @@ interface ReceiveAction extends GetBlogCatalogResponseModel { type: 'RECEIVE_BLOG_CATALOG' } -type KnownAction = RequestAction | ReceiveAction; +type KnownAction = RequestAction | ReceiveAction export const actionCreators = { requestBlogCatalog: (props?: GetBlogCatalogRequestModel): AppThunkAction => (dispatch, getState) => { diff --git a/clientapp/src/store/reducers/BlogItem.ts b/clientapp/src/store/reducers/BlogItem.ts new file mode 100644 index 0000000..700f25f --- /dev/null +++ b/clientapp/src/store/reducers/BlogItem.ts @@ -0,0 +1,116 @@ +import { Action, Reducer } from 'redux' +import { AppThunkAction } from '../' + +import { GetBlogItemRequestModel } from '../../models/requests' +import { GetBlogItemResponseModel } from '../../models/responses' +import { Get } from '../../restClient' + +export interface BlogItemState extends GetBlogItemResponseModel { + isLoading: boolean +} + +interface RequestAction extends GetBlogItemRequestModel { + type: 'REQUEST_BLOG_ITEM' +} + +interface ReceiveAction extends GetBlogItemResponseModel { + type: 'RECEIVE_BLOG_ITEM' +} + +type KnownAction = RequestAction | ReceiveAction + +export const actionCreators = { + requestBlogItem: (props: GetBlogItemRequestModel): AppThunkAction => (dispatch, getState) => { + const apiUrl = 'https://localhost:7151/api/BlogItem' + + Get>(apiUrl, 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 = { + + 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 = (state: BlogItemState | undefined, incomingAction: Action): BlogItemState => { + if (state === undefined) { + return unloadedState + } + + const action = incomingAction as KnownAction + switch (action.type) { + case 'REQUEST_BLOG_ITEM': + return { + ...state, + isLoading: true + } + + case 'RECEIVE_BLOG_ITEM': + return { + ...action, + isLoading: false + } + } + + return state +}