Skip to content

Commit ce45439

Browse files
authored
Merge pull request #102 from waterloop/blog_page
Feature: Blog Posts
2 parents 60c3c7a + 3372fca commit ce45439

File tree

21 files changed

+530
-4
lines changed

21 files changed

+530
-4
lines changed

src/@types/blogs.d.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
declare module "blogs" {
2+
export interface PostResponse {
3+
success: string;
4+
posts: Posts[]
5+
6+
}
7+
export interface Posts {
8+
author: string;
9+
category: string;
10+
closed: string;
11+
date: string;
12+
id: number;
13+
image: string;
14+
content: string;
15+
summary: string;
16+
title: string;
17+
visibility: string;
18+
}
19+
}

src/App.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'typeface-ibm-plex-sans';
99

1010
import { Footer } from './sections/Footer';
1111
import JobPostingPage from './components/RecruitmentForm/JobPostingPage';
12+
import BlogPage from './components/Blogs/BlogPage';
1213
import { NavBar, SideBar } from './components';
1314

1415
import Contact from './pages/Contact';
@@ -18,6 +19,7 @@ import Home from './pages/Home';
1819
import RecruitmentRouter from 'pages/Recruitment/RecruitmentRouter';
1920
import Sponsors from './pages/Sponsors';
2021
import Team from './pages/Team';
22+
import Blog from './pages/Blog/Blog';
2123

2224
interface State {
2325
width: number;
@@ -132,6 +134,15 @@ class App extends React.Component<{}, State> {
132134
path: '/posting/:id',
133135
MainComponent: <JobPostingPage />,
134136
}),
137+
this.generatePageMap({
138+
path: '/blog',
139+
exact: true,
140+
MainComponent: <Blog />,
141+
}),
142+
this.generatePageMap({
143+
path: '/blog/:id',
144+
MainComponent: <BlogPage />,
145+
}),
135146
this.generatePageMap({
136147
path: '/404',
137148
exact: true,

src/api/blogs.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Server } from "server";
2+
import { AxiosResponse } from "axios";
3+
import { PostResponse } from "blogs";
4+
5+
const getBlogPosts = (server: Server) => (): Promise<AxiosResponse<PostResponse>> => server.get(`/api/blogs`);
6+
const getLatestBlogs = (server: Server) => (): Promise<AxiosResponse<PostResponse>> => server.get(`/api/blogs/latest`);
7+
8+
export default (server: Server) => ({
9+
getBlogPosts: getBlogPosts(server),
10+
getLatestBlogs: getLatestBlogs(server)
11+
});

src/api/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import postings from './postings';
33
import server from './server';
44
import sponsors from './sponsors';
55
import teams from './teams';
6+
import blogs from './blogs';
67

78
export default {
89
postings: postings(server),
910
teams: teams(server),
1011
sponsors: sponsors(server),
11-
newsletter: newsletter(server)
12+
newsletter: newsletter(server),
13+
blogs: blogs(server)
1214
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from 'react';
2+
import BlogPost from './BlogList';
3+
import usePosts from "hooks/blogs"
4+
import { Posts } from 'blogs';
5+
6+
interface Props {
7+
posts: {}
8+
}
9+
10+
const BlogLanding: React.FC<Props> = props => {
11+
const posts = usePosts('latest');
12+
13+
const recent = posts && posts.posts ? posts.posts : []
14+
15+
return (<div className="Block-Blog">
16+
<div className="Block-BlogPosts">
17+
<h2>Our Blogs<a onClick={(): Window | null => window.open('blog', '_self')}><u>Read All &gt;</u></a></h2>
18+
<div className="PostsBlock-Blog">
19+
{
20+
recent.map((d:Posts) => (
21+
<BlogPost post={d} key={d.id}/>
22+
))
23+
}
24+
</div>
25+
</div>
26+
</div>
27+
)
28+
}
29+
30+
export default BlogLanding
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Posts } from "blogs"
2+
import React from "react"
3+
4+
interface props {
5+
post: Posts
6+
}
7+
8+
const BlogPost: React.FC<props> = (props) => {
9+
const post = props.post
10+
11+
return (
12+
<>
13+
{post.visibility !== "Hidden" && <div className="PostBlock-Blog">
14+
<div className="ContentBlock-Blog" onClick={(): Window | null => window.open(`blog/${post.id}`, '_self')}>
15+
<img src={post.image} alt={post.title} />
16+
<div className="PostContent-Blog">
17+
<div className="PostTitle-Blog">{post.title}</div>
18+
<div className="PostText-Blog">{post.summary}</div>
19+
</div>
20+
</div>
21+
</div>}
22+
</>
23+
)}
24+
25+
export default BlogPost
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as Blog } from './BlogLanding'

src/components/Blogs/BlogPage.tsx

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { useParams } from 'react-router';
3+
import styled from 'styled-components';
4+
import usePosts from "hooks/blogs"
5+
import { Posts } from 'blogs'
6+
7+
const Container = styled.div`
8+
display: flex;
9+
flex-direction: column;
10+
justify-content: center;
11+
align-items: center;
12+
margin: 70px;
13+
`;
14+
15+
const Title = styled.p`
16+
font-family: "IBM Plex Sans";
17+
font-style: italic;
18+
font-size: 36pt;
19+
margin: 12pt 0;
20+
color: #232636;
21+
margin: 0 0 30px 0;
22+
`;
23+
24+
const Text = styled.p`
25+
color: black
26+
font-family: "IBM Plex Sans";
27+
font-size: 18px;
28+
margin: 0 0 20px 0;
29+
padding: 0;
30+
`;
31+
32+
const Content = styled.p`
33+
margin: 0 15vw 0 15vw;
34+
`;
35+
36+
const SubInfo = styled.p`
37+
display: flex;
38+
flex-direction: column;
39+
justify-content: start;
40+
`;
41+
42+
const BlogImage = styled.img`
43+
height: 20%;
44+
margin: 0 0 20px 0;
45+
`;
46+
47+
const BodyText = styled.pre`
48+
font-family: Consolas, monospace;
49+
margin: 0 0 20px 0;
50+
padding: 0;
51+
width: 70vw;
52+
white-space: pre-wrap;
53+
`;
54+
55+
interface RouteParams {
56+
id: string;
57+
}
58+
59+
const BlogPage: React.FC = () => {
60+
const [blogFound, setBlogFound] = useState<Posts>(
61+
{ author: "", category: "", closed: "", date: "", id: -1,
62+
image: "", content: "", summary: "", title: "", visibility: ""})
63+
64+
const stringId: RouteParams = useParams();
65+
const blogId: number = parseInt(stringId.id, 10);
66+
const {posts} = usePosts();
67+
68+
useEffect(() => {
69+
// filter to get the blog with specific id
70+
console.log(posts)
71+
posts.every(blog => {
72+
console.log(blog.id)
73+
if (blog.id === blogId){
74+
setBlogFound(blog);
75+
return false;
76+
}
77+
return true;
78+
})
79+
}, [posts, blogId])
80+
81+
return (
82+
<Container>
83+
{blogFound.id === -1 || blogFound.visibility === "Hidden" ? <Text>Sorry, the blog you're looking for doesnt exist</Text>
84+
:
85+
<>
86+
<Title>{blogFound.title}</Title>
87+
<BlogImage src={blogFound.image} alt={blogFound.title} />
88+
<SubInfo>
89+
<Text><b>Author:</b> {blogFound.author}</Text>
90+
<Text><b>Published Date:</b> {blogFound.date}</Text>
91+
</SubInfo>
92+
<Content>
93+
<BodyText>
94+
<Text>{blogFound.content}</Text>
95+
</BodyText>
96+
</Content>
97+
</>
98+
}
99+
</Container>
100+
);
101+
};
102+
103+
export default BlogPage;

src/components/Blogs/Blogs.tsx

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { Posts } from "blogs"
2+
import { Button } from "components/Button"
3+
import usePosts from "hooks/blogs"
4+
import React from "react"
5+
import styled from "styled-components"
6+
7+
const BlogPage = styled.div`
8+
`
9+
10+
const PostsWrapper = styled.div`
11+
margin-bottom: 40px;
12+
`
13+
14+
const PostDiv = styled.div`
15+
padding:40px;
16+
width: 100%;
17+
margin: 0 auto;
18+
display: inline-block;
19+
@media (max-width: 900px) {
20+
padding: 0px;
21+
display: flex;
22+
flex-direction: column;
23+
}
24+
`
25+
26+
27+
const ImageDiv = styled.div`
28+
width: 50%;
29+
height: 300px;
30+
float: left;
31+
> .img {
32+
width: 100%;
33+
height: 100%;
34+
border-radius: 10px;
35+
box-shadow: 0px 0px 8px 0px #ada8a8;
36+
}
37+
@media (max-width: 900px) {
38+
width: 100%;
39+
}
40+
`
41+
42+
const PostText = styled.div`
43+
padding: 20px;
44+
float: right;
45+
width: 40%;
46+
> .PostTitle-Blog {
47+
font-size: 18px;
48+
font-weight: bold;
49+
font-style: italic;
50+
margin-bottom: 5px;
51+
}
52+
> .PostAuthor-Blog{
53+
font-size: 12px;
54+
font-weight: bold;
55+
margin: 20px 0;
56+
}
57+
> .PostDescription-Blog {
58+
margin: 10px 0;
59+
font-size: 14px;
60+
}
61+
> .PostReadButton-Blog {
62+
margin: 25px 0;
63+
> .ButtonDiv {
64+
padding: 10px 40px;
65+
> .ButtonText {
66+
font-size: 18px;
67+
}
68+
}
69+
}
70+
@media (max-width: 900px) {
71+
width: 100%;
72+
}
73+
`
74+
75+
const Blogs: React.FC = () => {
76+
const posts = usePosts()
77+
78+
if (posts.posts.find(function(post) {
79+
return post.visibility === 'Public'
80+
}) === undefined) {
81+
82+
return (
83+
<BlogPage>
84+
<div>Stay tuned for upcoming blogs!</div>
85+
</BlogPage>
86+
)
87+
}
88+
89+
90+
posts.posts.sort((a:Posts,b:Posts) => {
91+
return new Date(b.date).valueOf() - new Date(a.date).valueOf();
92+
});
93+
94+
return (
95+
<BlogPage>
96+
<PostsWrapper>
97+
{
98+
posts.posts.map((post) => (
99+
post.visibility !== "Hidden" &&
100+
<PostDiv>
101+
<ImageDiv>
102+
<img className="img" src={post.image} alt={post.title} />
103+
</ImageDiv>
104+
<PostText>
105+
<div className="PostTitle-Blog">{post.title}</div>
106+
<div className="PostAuthor-Blog">Written by: {post.author}</div>
107+
<div className="PostDescription-Blog">{post.summary}</div>
108+
<div className="PostReadButton-Blog"><Button
109+
backgroundColor="yellow"
110+
textColor="black"
111+
text="READ"
112+
onClick={(): Window | null => window.open(`blog/${post.id}`, '_self')}
113+
/></div>
114+
</PostText>
115+
</PostDiv>
116+
))}
117+
</PostsWrapper>
118+
</BlogPage>
119+
)
120+
}
121+
122+
export default Blogs

src/components/Blogs/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {default as Blogs} from './Blogs'

0 commit comments

Comments
 (0)