Skip to content

Commit 4a858be

Browse files
committed
feat: create blog page and display blog posts
1 parent 4fcda56 commit 4a858be

File tree

16 files changed

+218
-12
lines changed

16 files changed

+218
-12
lines changed

src/app/component.js

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
Home,
1515
PlayWithModel,
1616
Prediction,
17+
Blog,
1718
} from '../screens';
1819

1920
import {
@@ -120,6 +121,7 @@ const App = (props) => {
120121
<Route exact path={ROUTES.HOME} component={Home} />
121122
<Route path={ROUTES.ABOUT} component={About} />
122123
<Route path={ROUTES.ADMIN} component={Admin} />
124+
<Route path={ROUTES.BLOG} component={Blog} />
123125
<Route path={ROUTES.RESOURCES} component={Resources} />
124126
<Route path={ROUTES.TRAPPING_DATA} component={TrappingData} />
125127
<Route path={ROUTES.PLAY_WITH_MODEL} component={PlayWithModel} />

src/constants/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ const CHART_MODES = {
7171
const ROUTES = {
7272
ABOUT: '/about',
7373
ADMIN: '/admin',
74+
BLOG: '/blog',
7475
HOME: '/',
7576
RESOURCES: '/resources',
7677
PLAY_WITH_MODEL: '/play-with-model',

src/screens/admin/components/blog-post-form/style.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
transform: translate(-50%, -50%);
8888
min-width: 100%;
8989
min-height: 100%;
90-
width: auto;
90+
width: inherit;
9191
height: auto;
9292
}
9393
}

src/screens/admin/components/blog-posts/component.js

+4-10
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@ import React, { useEffect, useState } from 'react';
22
import Modal from 'react-modal';
33

44
import EditBlogPost from '../edit-blog-post';
5+
import { getDateToDisplay, sortBlogPosts } from '../../../../utils';
6+
57
import './style.scss';
68

79
const BlogPost = ({ post, onClickEdit, onDelete }) => {
8-
const date = new Date(post.date_created);
9-
const dateToDisplay = `${date.getMonth()}/${date.getDate()}/${date.getFullYear()}`;
10-
1110
const onClickDelete = () => {
1211
onDelete(post.id);
1312
};
@@ -19,7 +18,7 @@ const BlogPost = ({ post, onClickEdit, onDelete }) => {
1918
{post.title}
2019
</div>
2120
<div className="blog-post-date">
22-
{dateToDisplay}
21+
{getDateToDisplay(post.date_created)}
2322
</div>
2423
</div>
2524
<div className="blog-post-action-buttons">
@@ -80,12 +79,7 @@ const BlogPosts = (props) => {
8079
}, [getAllBlogPostsByAuthor]);
8180

8281
// Sort posts from the newest to the oldest
83-
const sortedBlogPosts = blogPosts.sort((a, b) => {
84-
const dateA = new Date(a.date_created);
85-
const dateB = new Date(b.date_created);
86-
87-
return dateB - dateA;
88-
});
82+
const sortedBlogPosts = sortBlogPosts(blogPosts);
8983

9084
const handleFormSubmit = async (formData) => {
9185
const closeModal = () => { setShowEditForm(false); };

src/screens/blog/component.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React, { useEffect } from 'react';
2+
3+
import BlogPost from './components';
4+
import { sortBlogPosts } from '../../utils';
5+
6+
import './style.scss';
7+
8+
const Blog = (props) => {
9+
const {
10+
blogPosts,
11+
getAllBlogPosts,
12+
} = props;
13+
14+
useEffect(() => {
15+
getAllBlogPosts();
16+
}, [getAllBlogPosts]);
17+
18+
const sortedBlogPosts = sortBlogPosts(blogPosts);
19+
20+
return (
21+
<div className="blog-page-container">
22+
<div id="overview-text">
23+
<h1 id="title">Blog</h1>
24+
</div>
25+
{sortedBlogPosts.length > 0
26+
? sortedBlogPosts.map((post) => <BlogPost post={post} />)
27+
: <div className="blog-page-no-posts">There are no blog posts yet</div>}
28+
</div>
29+
);
30+
};
31+
32+
export default Blog;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import React from 'react';
2+
3+
import { formatPostDates } from '../../../../utils';
4+
5+
import './style.scss';
6+
7+
const BlogPost = ({ post }) => {
8+
const {
9+
title,
10+
body,
11+
date_created: createdAt,
12+
date_edited: editedAt,
13+
image,
14+
author,
15+
} = post;
16+
17+
return (
18+
<div className="blog-page-blog-post-container page-container">
19+
<div className="blog-page-blog-post-title">{title}</div>
20+
<div className="blog-page-blog-post-author">{`by ${author}`}</div>
21+
{image && <img className="blog-page-blog-post-picture" src={`${global.API_URL}${image}`} alt="whatever" />}
22+
<div className="blog-page-blog-post-body">{body}</div>
23+
<div className="blog-page-blog-post-date">{formatPostDates(createdAt, editedAt)}</div>
24+
</div>
25+
);
26+
};
27+
28+
export default BlogPost;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import BlogPost from './component';
2+
3+
export default BlogPost;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
.blog-page {
2+
&-blog-post {
3+
&-container {
4+
margin: 30px auto;
5+
width: 100%;
6+
box-sizing: border-box;
7+
}
8+
9+
&-title {
10+
font-size: 24px;
11+
font-weight: 600;
12+
}
13+
14+
&-author {
15+
margin-bottom: 20px;
16+
margin-top: 6px;
17+
}
18+
19+
&-picture {
20+
max-width: 100%;
21+
height: auto;
22+
max-height: 500px;
23+
margin: 30px auto 50px;
24+
}
25+
26+
&-date {
27+
font-style: italic;
28+
font-size: 14px;
29+
margin-top: 30px;
30+
width: 100%;
31+
text-align: right;
32+
}
33+
34+
&-date,
35+
&-author {
36+
font-style: italic;
37+
color: #73767e;
38+
}
39+
40+
&-body {
41+
white-space: pre-line;
42+
}
43+
}
44+
}

src/screens/blog/components/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import BlogPost from './blog-post';
2+
3+
export default BlogPost;

src/screens/blog/index.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { connect } from 'react-redux';
2+
import { getAllBlogPosts } from '../../state/actions/blog';
3+
import Blog from './component';
4+
5+
const mapStateToProps = (state) => {
6+
const {
7+
blog: {
8+
blogPosts,
9+
},
10+
} = state;
11+
12+
return { blogPosts };
13+
};
14+
15+
const mapDispatchToProps = (dispatch) => {
16+
return {
17+
getAllBlogPosts: () => dispatch(getAllBlogPosts()),
18+
};
19+
};
20+
21+
export default connect(mapStateToProps, mapDispatchToProps)(Blog);

src/screens/blog/style.scss

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.blog-page-container {
2+
max-width: 1000px;
3+
margin: auto;
4+
display: flex;
5+
flex-direction: column;
6+
align-items: center;
7+
}
8+
9+
.blog-page-no-posts {
10+
font-size: 26px;
11+
width: 100%;
12+
text-align: center;
13+
color: #73767e;
14+
}

src/screens/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import About from './about';
22
import Admin from './admin';
3+
import Blog from './blog';
34
import Resources from './resources';
45
import TrappingData from './trapping-data';
56
import Home from './home';
@@ -9,6 +10,7 @@ import Prediction from './prediction';
910
export {
1011
About,
1112
Admin,
13+
Blog,
1214
Resources,
1315
TrappingData,
1416
Home,

src/state/actions/blog.js

+17-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export const ActionTypes = {
44
API_ERROR: 'API_ERROR',
55
CLEAR_API_ERROR: 'CLEAR_API_ERROR',
66
SET_BLOG_POSTS_BY_USER_DATA: 'SET_BLOG_POSTS_BY_USER_DATA',
7+
GET_ALL_BLOG_POSTS: 'GET_ALL_BLOG_POSTS',
78
EDIT_BLOG_POST: 'EDIT_BLOG_POST',
89
DELETE_BLOG_POST: 'DELETE_BLOG_POST',
910
CREATE_BLOG_POST: 'CREATE_BLOG_POST',
@@ -28,6 +29,21 @@ export const createBlogPost = (fields, onSuccess = () => {}) => {
2829
};
2930
};
3031

32+
export const getAllBlogPosts = () => {
33+
return async (dispatch) => {
34+
try {
35+
const blogPosts = await BlogService.getAllBlogPosts();
36+
dispatch({ type: ActionTypes.GET_ALL_BLOG_POSTS, payload: blogPosts });
37+
} catch (error) {
38+
dispatch({
39+
type: ActionTypes.API_ERROR,
40+
payload: 'GET ALL BLOG POSTS',
41+
error,
42+
});
43+
}
44+
};
45+
};
46+
3147
export const getAllBlogPostsByAuthor = (onSuccess = () => {}, onError = () => {}) => {
3248
return async (dispatch) => {
3349
try {
@@ -38,7 +54,7 @@ export const getAllBlogPostsByAuthor = (onSuccess = () => {}, onError = () => {}
3854
dispatch({
3955
type: ActionTypes.API_ERROR,
4056
payload: {
41-
action: 'GET ALL BLOG POSTS',
57+
action: 'GET ALL BLOG POSTS BY USER',
4258
error,
4359
},
4460
});

src/state/reducers/blog.js

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ActionTypes } from '../actions';
22

33
const initialState = {
4+
blogPosts: [],
45
blogPostsByUser: [],
56
error: null,
67
};
@@ -15,6 +16,9 @@ const BlogReducer = (state = initialState, action) => {
1516
case ActionTypes.SET_BLOG_POSTS_BY_USER_DATA:
1617
return { ...state, blogPostsByUser: action.payload };
1718

19+
case ActionTypes.GET_ALL_BLOG_POSTS:
20+
return { ...state, blogPosts: action.payload };
21+
1822
case ActionTypes.EDIT_BLOG_POST: {
1923
const updatedBlogPosts = state.blogPostsByUser.map(
2024
(post) => (post._id === action.payload._id

src/utils/blog.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Sort posts from the newest to the oldest
2+
const sortBlogPosts = (blogPosts) => blogPosts.sort((a, b) => {
3+
const dateA = new Date(a.date_created);
4+
const dateB = new Date(b.date_created);
5+
6+
return dateB - dateA;
7+
});
8+
9+
// Return string with US date format
10+
const getDateToDisplay = (dateToParse) => {
11+
const date = new Date(dateToParse);
12+
return `${date.getMonth()}/${date.getDate()}/${date.getFullYear()}`;
13+
};
14+
15+
// Return string with blog post creation and editing dates
16+
const formatPostDates = (created, updated) => {
17+
const options = {
18+
year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit',
19+
};
20+
const createdDate = new Date(created).toLocaleString('en-US', options);
21+
const updatedDate = new Date(updated).toLocaleString('en-US', options);
22+
23+
let dateString = `Posted on ${createdDate}`;
24+
if (updated && created !== updated) {
25+
dateString += `, Last updated on ${updatedDate}`;
26+
}
27+
28+
return dateString;
29+
};
30+
31+
export {
32+
sortBlogPosts, getDateToDisplay, formatPostDates,
33+
};

src/utils/index.js

+9
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,29 @@ import {
2222
toQueryParams,
2323
} from './network';
2424

25+
import {
26+
sortBlogPosts,
27+
getDateToDisplay,
28+
formatPostDates,
29+
} from './blog';
30+
2531
export {
2632
downloadCsv,
2733
getAuthTokenFromStorage,
2834
getChartModeFromStorage,
2935
getDataModeFromStorage,
36+
getDateToDisplay,
3037
getMapboxRDNameFormat,
3138
getStateAbbreviationFromStateName,
3239
getStateNameFromAbbreviation,
3340
getUserIdFromStorage,
41+
formatPostDates,
3442
removeAuthTokenFromStorage,
3543
removeUserIdFromStorage,
3644
setAuthTokenInStorage,
3745
setChartModeInStorage,
3846
setDataModeInStorage,
3947
setUserIdInStorage,
48+
sortBlogPosts,
4049
toQueryParams,
4150
};

0 commit comments

Comments
 (0)