diff --git a/src/actions/actions.js b/src/actions/actions.js
index 821e726..7ab6eeb 100644
--- a/src/actions/actions.js
+++ b/src/actions/actions.js
@@ -1,5 +1,6 @@
import fetch from 'isomorphic-fetch';
import { config } from '../config.js';
+import {browserHistory} from 'react-router';
/*
* action creators
*/
@@ -25,9 +26,10 @@ export function setPosts(posts) {
};
}
-export function postsFetchData(url) {
+export function postsFetchData() {
return (dispatch) => {
dispatch(postsIsLoading(true));
+ const url = config.url.posts;
fetch(url)
.then((response) => {
@@ -67,7 +69,8 @@ export function getFilteredPosts() {
const title = post.title.toLowerCase();
const body = post.body.toLowerCase();
return (title.indexOf(searchedPhrase) >= 0 || body.indexOf(searchedPhrase) >= 0);
- }
+ } else
+ return false;
});
}
@@ -84,10 +87,12 @@ export function changeSearchedPhrase(searchedPhrase) {
export function deletePostAction(postId) {
return (dispatch, getState) => {
- const url = `${config.url}/${postId}`;
+ const url = `${config.url.posts}/${postId}`;
fetch(url, {method: 'DELETE'})
.then( () => {
+ postId = getState().postToDelete;
+
const posts = getState().posts;
posts.forEach((item, index) => {
@@ -98,13 +103,14 @@ export function deletePostAction(postId) {
dispatch(postsFilter(posts));
dispatch(setPosts(posts));
+ browserHistory.push('/');
});
}
}
-export function choosePostToDelete(postToDelete) {
+export function choosePostToDelete(postToDelete) {
return {
type: 'CHOOSE_POST_TO_DELETE',
postToDelete
};
-}
\ No newline at end of file
+}
diff --git a/src/actions/auth.js b/src/actions/auth.js
new file mode 100644
index 0000000..27855d4
--- /dev/null
+++ b/src/actions/auth.js
@@ -0,0 +1,121 @@
+import {config} from "../config";
+
+export function setIsLogged(bool) {
+
+ return {
+ type: 'SET_IS_LOGGED',
+ isLogged: bool
+ };
+}
+
+export function setToken(token) {
+ return {
+ type: 'SET_TOKEN',
+ token
+ };
+}
+
+export function setUserData(userData) {
+ return {
+ type: 'SET_USER_DATA',
+ userData
+ };
+}
+
+export function signIn(login, password) {
+ return (dispatch) => {
+ dispatch(fetchSignIn(login,password));
+ }
+}
+
+export function fetchSignIn(login, password) {
+ return (dispatch, getState) => {
+ const data = {
+ login,
+ password
+ }
+
+ const myParams = Object.keys(data).map((key) => {
+ return `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`;
+ }).join('&');
+
+ const fetchData = {
+ method: 'POST',
+ body: myParams,
+ //credentials: 'include',
+ headers: {
+ //"Content-Type": "application/json"
+ 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
+ 'Access-Control-Allow-Origin': 'http://localhost:3003'
+ }
+ }
+
+ const url = config.url.login;
+
+ fetch(url, fetchData)
+ .then((response) => {
+
+ if (!response.ok) {
+ throw Error(response.statusText);
+ }
+ return response;
+ })
+ .then( (response) => response.json() )
+ .then( (json) => {
+ dispatch(setToken(json.token));
+ dispatch(setIsLogged(true));
+ dispatch(fetchUserData(json.token));
+ localStorage.setItem('reactAppToken', json.token);
+ });
+
+ }
+}
+
+export function checkIfLogged() {
+ return (dispatch) => {
+ const token = localStorage.getItem('reactAppToken');
+
+ if (token !== 'null') {
+ dispatch(setIsLogged(true));
+ dispatch(setToken(token));
+ dispatch(fetchUserData(token))
+ } else {
+ dispatch(setIsLogged(false));
+ }
+ }
+}
+
+export function fetchUserData(token) {
+ return (dispatch) => {
+ const url = config.url.auth;
+
+ const fetchData = {
+ method: 'GET',
+ headers: {
+ authorization: token,
+ },
+ }
+
+ fetch(url, fetchData)
+ .then((response) => {
+ if (!response.ok) {
+
+ throw Error(response.statusText);
+ }
+ return response;
+ })
+ .then((response) => response.json())
+ .then((json) => {
+ dispatch(setUserData(json))
+ });
+ };
+}
+
+export function logOut() {
+ return (dispatch) => {
+ localStorage.removeItem('reactAppToken');
+ dispatch(setToken(''));
+ dispatch(setIsLogged(false));
+ dispatch(setUserData({}));
+ }
+}
diff --git a/src/actions/postPage.js b/src/actions/postPage.js
index 4c34f18..d11369c 100644
--- a/src/actions/postPage.js
+++ b/src/actions/postPage.js
@@ -1,5 +1,6 @@
import fetch from 'isomorphic-fetch';
import { config } from '../config.js';
+import {browserHistory} from 'react-router';
export function setPostTitle(title) {
return {
@@ -24,7 +25,7 @@ export function setPostUser(user) {
export function getPostData(postId) {
return (dispatch) => {
- const url = `${config.url}/${postId}`;
+ const url = `${config.url.posts}/${postId}`;
fetch(url)
.then( (response) => response.json() )
.then( (json) =>
@@ -46,7 +47,7 @@ export function setPostComments(comments) {
export function getPostComments(postId) {
return (dispatch) => {
- const url = `${config.url}/${postId}/comments`;
+ const url = `${config.url.posts}/${postId}/comments`;
fetch(url)
.then( (response) => response.json() )
.then( (json) =>
@@ -90,7 +91,7 @@ export function addPost() {
}
}
- fetch(config.url, fetchData)
+ fetch(config.url.posts, fetchData)
.then( (response) => response.json() )
.then( (json) => {
const posts = getState().posts;
@@ -101,7 +102,7 @@ export function addPost() {
dispatch({ type: 'INCREMENT_LAST_POST_ID' })
clearForm(dispatch);
-
+ browserHistory.push('/');
//this.setState({info: `Post #${json.id} was saved.`})
});
@@ -132,22 +133,57 @@ export function updatePost(postId) {
}
}
- const url = `${config.url}/${postId}`;
+ const url = `${config.url.posts}/${postId}`;
fetch(url, fetchData)
.then( (response) => {
let posts = getState().posts;
- posts = posts.filter( (post) => {
- if (post.id == postId) {
+ posts.filter( (post) => {
+ if (post.id === postId) {
post.title = fetchData.body.title;
post.body = fetchData.body.body;
post.userId = fetchData.body.userId
return true;
}
+ else return false;
});
+ browserHistory.push('/');
//const info = (response.ok) ? 'Changes in post was saved.' : 'Error!'
//this.setState({info})
});
}
}
+
+export function setUsers(users) {
+ return {
+ type: 'SET_USERS',
+ users
+ }
+}
+
+export function fetchUsers() {
+ return (dispatch, getState) => {
+ const url = config.url.users;
+ const token = getState().token;
+
+ const fetchData = {
+ method: 'GET',
+ headers: {
+ authorization: token,
+ }
+ }
+
+ fetch(url, fetchData)
+ .then((response) => {
+ if (!response.ok) {
+ throw Error(response.statusText);
+ }
+ return response;
+ })
+ .then((response) => response.json())
+ .then((json) => {
+ dispatch(setUsers(json));
+ });
+ };
+}
diff --git a/src/actions/validation.js b/src/actions/validation.js
new file mode 100644
index 0000000..158ef02
--- /dev/null
+++ b/src/actions/validation.js
@@ -0,0 +1,38 @@
+import { config } from "../config";
+import {
+
+} from './actions.js';
+
+export function setLoginFormValidation(formLoginIsValid, error) {
+ return {
+ type: 'SET_LOGIN_FORM_VALID',
+ formLoginIsValid
+ }
+}
+
+export function validateLoginForm(login, password) {
+ return (dispatch) => {
+ const errors = [];
+ let result = true;
+
+ const loginIsValid = validateInputText(login);
+ if (!loginIsValid) {
+ result = false;
+ errors.push(config.messages.empty_login);
+ }
+
+ const passwordIsValid = validateInputText(password);
+ if (!passwordIsValid) {
+ dispatch(setLoginFormValidation(passwordIsValid));
+ result = false;
+ errors.push(config.messages.empty_password);
+ }
+ //dispatch(setInfo(errors));
+ dispatch(setLoginFormValidation(true));
+ return result;
+ }
+}
+
+export function validateInputText(value) {
+ return !(value.length === 0);
+}
diff --git a/src/components/App/logo.svg b/src/components/App/logo.svg
deleted file mode 100755
index 6b60c10..0000000
--- a/src/components/App/logo.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/src/components/AuthComponent/AuthComponent.js b/src/components/AuthComponent/AuthComponent.js
new file mode 100644
index 0000000..1c67535
--- /dev/null
+++ b/src/components/AuthComponent/AuthComponent.js
@@ -0,0 +1,42 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { browserHistory } from 'react-router';
+import PropTypes from 'prop-types';
+import { checkIfLogged } from '../../actions/auth';
+
+export function requireAuthentication(Component) {
+
+ class AuthComponent extends React.Component {
+ static propTypes = {
+ isLogged: PropTypes.bool.isRequired,
+ checkIfLogged: PropTypes.func.isRequired
+ }
+
+ componentWillMount() {
+ this.props.checkIfLogged();
+ if (!this.props.isLogged) {
+ browserHistory.push('/login');
+ }
+ }
+
+ render() {
+ return (
+ this.props.isLogged &&
+ )
+ }
+ }
+
+ const mapStateToProps =
+ (state) => ({
+ token: state.token,
+ isLogged: state.isLogged
+ });
+
+ const mapDispatchToProps = (dispatch) => {
+ return {
+ checkIfLogged: () => dispatch(checkIfLogged())
+ };
+ }
+
+ return connect(mapStateToProps, mapDispatchToProps)(AuthComponent);
+}
diff --git a/src/components/App/App.scss b/src/components/AuthComponent/AuthComponent.scss
similarity index 100%
rename from src/components/App/App.scss
rename to src/components/AuthComponent/AuthComponent.scss
diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js
index f776590..c197127 100755
--- a/src/components/Header/Header.js
+++ b/src/components/Header/Header.js
@@ -4,19 +4,44 @@ import Logo from '../Logo/Logo';
import PropTypes from 'prop-types';
import logo from './logo.svg';
import classNames from 'classnames';
+import { connect } from "react-redux";
+import UserPanel from '../UserPanel/UserPanel';
+import {
+ signIn
+} from '../../actions/auth';
class Header extends React.Component {
static propTypes = {
- title: PropTypes.string.isRequired
+ title: PropTypes.string.isRequired,
+ isLogged: PropTypes.bool.isRequired,
}
+
render() {
return (
-
{this.props.title}
+ {this.props.title}
+
);
}
}
-export default Header;
+//export default Header;
+
+const mapStateToProps = (state) => {
+ return {
+ isLogged: state.isLogged,
+ };
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ signIn: (login) => dispatch(signIn(login))
+ };
+}
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(Header);
\ No newline at end of file
diff --git a/src/components/App/App.js b/src/components/Layout/Layout.js
similarity index 57%
rename from src/components/App/App.js
rename to src/components/Layout/Layout.js
index f315898..cde84ff 100644
--- a/src/components/App/App.js
+++ b/src/components/Layout/Layout.js
@@ -1,17 +1,21 @@
import React from 'react';
+import PropTypes from 'prop-types';
import Header from '../Header/Header';
import Footer from '../Footer/Footer';
-import getRoutes from '../../routes';
-class App extends React.Component {
+class Layout extends React.Component {
+ static propTypes = {
+ children: PropTypes.node
+ }
+
render() {
return (
- { getRoutes() }
+ { this.props.children }
);
}
}
-export default App;
+export default Layout;
diff --git a/src/components/Layout/Layout.scss b/src/components/Layout/Layout.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/components/LoginPage/LoginPage.js b/src/components/LoginPage/LoginPage.js
new file mode 100644
index 0000000..c49bf50
--- /dev/null
+++ b/src/components/LoginPage/LoginPage.js
@@ -0,0 +1,73 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import './LoginPage.css';
+import { connect } from "react-redux";
+import {
+ signIn
+} from '../../actions/auth';
+import { browserHistory } from 'react-router';
+import Layout from "../Layout/Layout";
+
+
+class LoginPage extends React.Component {
+ constructor(props){
+ super(props);
+ this.state={
+ login: '',
+ password: ''
+ }
+ }
+
+ static propTypes = {
+ isLogged: PropTypes.bool.isRequired,
+ login: PropTypes.string,
+ signIn: PropTypes.func.isRequired
+ }
+
+ onHandleButton() {
+ this.props.signIn(this.state.login, this.state.password);
+ browserHistory.push('/');
+ }
+
+ render() {
+ return (
+
+
+
Log in
+ this.setState({ login: event.target.value }) }
+ />
+
+ this.setState({ password: event.target.value }) }
+ />
+
+
+
+
+ );
+ }
+}
+
+const mapStateToProps = (state) => {
+ return {
+ isLogged: state.isLogged,
+ login: state.login
+ };
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ signIn: (login, password) => dispatch(signIn(login, password))
+ };
+}
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(LoginPage);
diff --git a/src/components/LoginPage/LoginPage.scss b/src/components/LoginPage/LoginPage.scss
new file mode 100644
index 0000000..6b64cd4
--- /dev/null
+++ b/src/components/LoginPage/LoginPage.scss
@@ -0,0 +1,7 @@
+.LoginPage {
+ max-width: 350px;
+ margin: auto;
+ input, button {
+ margin: 20px;
+ }
+}
\ No newline at end of file
diff --git a/src/components/Page/Page.js b/src/components/Page/Page.js
index 84d33f7..f9261a8 100755
--- a/src/components/Page/Page.js
+++ b/src/components/Page/Page.js
@@ -3,8 +3,8 @@ import { Link } from 'react-router';
import './Page.css';
import Post from '../Post/Post';
import Modal from '../Modal/Modal';
+import Layout from '../Layout/Layout';
import Search from '../Search/Search';
-import { config } from '../../config.js';
import classNames from 'classnames';
import { debounce } from 'throttle-debounce';
import PropTypes from 'prop-types';
@@ -32,7 +32,9 @@ class Page extends React.Component {
changePhrase: PropTypes.func.isRequired,
getSearchedPosts: PropTypes.func.isRequired,
setPostToDelete: PropTypes.func.isRequired,
- deletePost: PropTypes.func.isRequired
+ deletePost: PropTypes.func.isRequired,
+
+ isLogged: PropTypes.bool.isRequired
}
constructor(props) {
@@ -44,14 +46,14 @@ class Page extends React.Component {
componentDidMount() {
if (this.props.posts.length === 0) {
- this.props.fetchData(config.url);
+ this.props.fetchData();
}
}
openModal(postId) {
this.props.setPostToDelete(postId);
- this.setState({
+ this.setState({
isModalOpen: true
})
}
@@ -66,14 +68,6 @@ class Page extends React.Component {
}
__renderPosts() {
- return(
- this.props.filteredPosts.map(
- post => this.openModal(post.id)}/>
- )
- );
- }
-
- __renderPostsContainer() {
if (this.props.hasErrored) {
return Sorry! There was an error loading the items
;
}
@@ -85,7 +79,9 @@ class Page extends React.Component {
return(
- { this.__renderPosts() }
+ { this.props.filteredPosts.map(
+ post => this.openModal(post.id)}/>
+ )}
);
@@ -93,11 +89,13 @@ class Page extends React.Component {
__renderAddPostButton() {
return(
+ this.props.isLogged &&
- Add new post
+ Add new post
+
);
}
@@ -105,38 +103,34 @@ class Page extends React.Component {
const { searchedPhrase, getSearchedPosts, changePhrase } = this.props;
return (
-
-
-
- {this.__renderAddPostButton()}
-
post to delete: {this.props.postToDelete} , number of posts: {this.props.posts.length}
-
- searhced phrase:{ searchedPhrase }
-
debounce(500, changePhrase(e)) }
- onFilterTextButton={ (e) => getSearchedPosts(e) }
- />
-
- this.closeModal() }
- onConfirm={ () => this.onConfirmDelete(this.props.postToDelete) }
- buttonCloseLabel="No"
- buttonConfirmLabel="Yes"
- >
- Are you sure to delete post #{this.props.postToDelete}?
-
-
- {this.__renderPostsContainer()}
-
+
+
+ {this.__renderAddPostButton()}
+
+
debounce(500, changePhrase(e)) }
+ onFilterTextButton={ (e) => getSearchedPosts(e) }
+ />
+
+ this.closeModal() }
+ onConfirm={ () => this.onConfirmDelete(this.props.postToDelete) }
+ buttonCloseLabel="No"
+ buttonConfirmLabel="Yes"
+ >
+ Are you sure to delete post #{this.props.postToDelete}?
+
+
+ {this.__renderPosts()}
+
+
);
}
}
-//export default Page;
-
const mapStateToProps = (state) => {
return {
posts: state.posts,
@@ -147,6 +141,8 @@ const mapStateToProps = (state) => {
hasErrored: state.postsHasErrored,
isLoading: state.postsIsLoading,
postToDelete: state.postToDelete,
+
+ isLogged: state.isLogged
};
}
diff --git a/src/components/Post/Post.js b/src/components/Post/Post.js
index 77f9f91..3c71f0d 100755
--- a/src/components/Post/Post.js
+++ b/src/components/Post/Post.js
@@ -3,13 +3,17 @@ import { Link } from 'react-router';
import './Post.css';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { connect } from "react-redux";
class Post extends React.Component {
static propTypes = {
id: PropTypes.number,
title: PropTypes.string,
body: PropTypes.string,
- handleDelete: PropTypes.func.isRequired
+ handleDelete: PropTypes.func.isRequired,
+ isLogged: PropTypes.bool.isRequired,
+ myId: PropTypes.number,
+ userId: PropTypes.number
}
constructor(props) {
@@ -19,25 +23,48 @@ class Post extends React.Component {
render() {
return (
-
-
+
+
{this.props.title}
{this.props.body}
-
-
- Edit
-
-
-
+ {
+ this.props.isLogged &&
+
+
+ Edit
+
+
+
+ }
);
}
}
-export default Post;
+//export default Post;
+
+const mapStateToProps = (state) => {
+ return {
+ isLogged: state.isLogged,
+ myId: state.userData.id
+ };
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+
+ };
+}
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(Post);
diff --git a/src/components/Post/Post.scss b/src/components/Post/Post.scss
index f47aed4..6a99872 100755
--- a/src/components/Post/Post.scss
+++ b/src/components/Post/Post.scss
@@ -20,4 +20,7 @@
.Post_buttons{
}
+ &.Post--my-post{
+ background: lightgrey;
+ }
}
\ No newline at end of file
diff --git a/src/components/PostPage/PostPage.js b/src/components/PostPage/PostPage.js
index 579d39e..d9eeb77 100644
--- a/src/components/PostPage/PostPage.js
+++ b/src/components/PostPage/PostPage.js
@@ -2,11 +2,9 @@ import React from 'react';
import { Link } from 'react-router'
import './PostPage.css';
import PropTypes from 'prop-types';
-import { config } from '../../config.js';
import Comment from '../Comment/Comment';
import User from '../User/User';
import './PostPage.css';
-import fetch from 'isomorphic-fetch';
import { IndexLink } from 'react-router'
import {connect} from "react-redux";
import {
@@ -16,8 +14,10 @@ import {
getPostData,
getPostComments,
addPost,
- updatePost
+ updatePost,
+ fetchUsers
} from '../../actions/postPage';
+import Layout from "../Layout/Layout";
class PostPage extends React.Component {
static propTypes = {
@@ -27,14 +27,16 @@ class PostPage extends React.Component {
inputTitleValue: PropTypes.string,
textareaBodyValue: PropTypes.string,
- // userValue: PropTypes.number,
+ userValue: PropTypes.number,
setBody: PropTypes.func.isRequired,
setTitle: PropTypes.func.isRequired,
setUser: PropTypes.func.isRequired,
getPostData: PropTypes.func.isRequired,
getPostComments: PropTypes.func.isRequired,
addPost: PropTypes.func.isRequired,
- updatePost: PropTypes.func.isRequired
+ updatePost: PropTypes.func.isRequired,
+ getUsers: PropTypes.func.isRequired,
+ users: PropTypes.array.isRequired
}
constructor(props) {
@@ -47,6 +49,7 @@ class PostPage extends React.Component {
componentDidMount() {
const postId = this.props.params.postId;
+ this.props.getUsers();
if (postId) {
this.props.getPostData(postId);
@@ -57,26 +60,26 @@ class PostPage extends React.Component {
}
clearForm() {
- this.setTitle('');
- this.setBody('');
- this.setUser('');
+ this.props.setTitle('');
+ this.props.setBody('');
+ this.props.setUser(null);
}
handleSubmit() {
- const postId = this.props.params.postId;
+ const postId = Number(this.props.params.postId);
postId ? this.props.updatePost(postId) : this.props.addPost();
}
handleTitleChange(event) {
- this.setTitle(event.target.value);
+ this.props.setTitle(event.target.value);
}
handleBodyChange(event) {
- this.setBody(event.target.value);
+ this.props.setBody(event.target.value);
}
handleUserChange(event) {
- this.setUser(event.target.value);
+ this.props.setUser(Number(event.target.value));
}
__validateForm() {
@@ -95,8 +98,8 @@ class PostPage extends React.Component {
return(
User
-
this.handleUserChange(e)} >
- {config.users.map(
+ this.handleUserChange(e)} >
+ {this.props.users.map(
user =>
)}
@@ -176,13 +179,15 @@ class PostPage extends React.Component {
render() {
return (
-
- {this.__renderBreadcrumbs()}
- {this.__renderTitle()}
- {this.__renderForm()}
- {this.__renderInfo()}
- {this.__renderComments()}
-
+
+
+ {this.__renderBreadcrumbs()}
+ {this.__renderTitle()}
+ {this.__renderForm()}
+ {this.__renderInfo()}
+ {this.__renderComments()}
+
+
);
}
}
@@ -191,7 +196,8 @@ const mapStateToProps = (state) => {
inputTitleValue: state.inputTitleValue,
textareaBodyValue: state.textareaBodyValue,
userValue: state.userValue,
- comments: state.comments
+ comments: state.comments,
+ users: state.users
};
}
@@ -203,7 +209,8 @@ const mapDispatchToProps = (dispatch) => {
getPostData: (postId) => dispatch(getPostData(postId)),
getPostComments: (postId) => dispatch(getPostComments(postId)),
addPost: () => dispatch(addPost()),
- updatePost: (postId) => dispatch(updatePost(postId))
+ updatePost: (postId) => dispatch(updatePost(postId)),
+ getUsers: () => dispatch(fetchUsers())
};
}
diff --git a/src/components/PostPage/PostPage.scss b/src/components/PostPage/PostPage.scss
index 6bac736..d881a84 100644
--- a/src/components/PostPage/PostPage.scss
+++ b/src/components/PostPage/PostPage.scss
@@ -31,6 +31,7 @@
&.users_container{
ul{
+ margin: 0;
padding: $padding;
border: $baseBorder;
list-style-type: none;
diff --git a/src/components/User/User.js b/src/components/User/User.js
index f44c645..1358d20 100644
--- a/src/components/User/User.js
+++ b/src/components/User/User.js
@@ -1,20 +1,48 @@
import React from 'react';
import './User.css';
import PropTypes from 'prop-types';
+import { connect } from "react-redux";
+import classNames from 'classnames';
class User extends React.Component {
static propTypes = {
name: PropTypes.string,
- id: PropTypes.number.isRequired
+ id: PropTypes.number.isRequired,
+ myId: PropTypes.node,
+ firstName: PropTypes.string,
+ lastName: PropTypes.string
}
render() {
return (
- -
-
-
+
-
+
+
);
}
}
-export default User;
+
+const mapStateToProps = (state) => {
+ return {
+ myId: state.userData.id
+ };
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+
+ };
+}
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(User);
diff --git a/src/components/User/User.scss b/src/components/User/User.scss
index d8b54c7..eb338a3 100644
--- a/src/components/User/User.scss
+++ b/src/components/User/User.scss
@@ -6,5 +6,6 @@
}
label{
padding-left: $padding;
+ margin: 5px 0;
}
}
\ No newline at end of file
diff --git a/src/components/UserPanel/UserPanel.js b/src/components/UserPanel/UserPanel.js
new file mode 100644
index 0000000..cafd5ec
--- /dev/null
+++ b/src/components/UserPanel/UserPanel.js
@@ -0,0 +1,72 @@
+import React from 'react';
+import { Link } from 'react-router';
+import './UserPanel.css';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { connect } from "react-redux";
+import {
+ logOut
+} from '../../actions/auth';
+
+class UserPanel extends React.Component {
+ static propTypes = {
+ isLogged: PropTypes.bool.isRequired,
+ userData: PropTypes.object,
+ logOut: PropTypes.func
+ }
+
+ constructor(props) {
+ super(props)
+ this.state = { isModalOpen: false }
+ }
+
+ __renderUserData() {
+ return(
+
+
+ { this.props.userData.firstName } { this.props.userData.lastName }
+ ({ this.props.userData.email })
+
+
+ this.props.logOut()}
+ className={classNames('btn btn-default')}>
+ Log out
+
+
+ )
+ }
+
+ render() {
+ return (
+
+ {
+ this.props.isLogged
+ ? this.__renderUserData()
+ :
+ Sign in
+
+ }
+
+ );
+ }
+}
+
+const mapStateToProps = (state) => {
+ return {
+ isLogged: state.isLogged,
+ userData: state.userData
+ };
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ logOut: () => dispatch(logOut())
+ };
+}
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(UserPanel);
diff --git a/src/components/UserPanel/UserPanel.scss b/src/components/UserPanel/UserPanel.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/config.js b/src/config.js
index a0c6097..1996245 100755
--- a/src/config.js
+++ b/src/config.js
@@ -1,6 +1,12 @@
-export const config = {
- url: 'http://jsonplaceholder.typicode.com/posts',
- commentsUrl: 'http://jsonplaceholder.typicode.com',
+const baseApiUrl = 'http://localhost:3003';
+
+export const config = {
+ url: {
+ posts: `${baseApiUrl}/posts`,
+ users: `${baseApiUrl}/authors`,
+ login: `${baseApiUrl}/auth/login`,
+ auth: `${baseApiUrl}/auth/me`
+ },
users: [
{
id: 0,
diff --git a/src/index.js b/src/index.js
index 68d620f..f1304f9 100755
--- a/src/index.js
+++ b/src/index.js
@@ -2,11 +2,10 @@ import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap/dist/css/bootstrap-theme.css';
import React from 'react';
import ReactDOM from 'react-dom';
-import App from './components/App/App';
import './style/index.css';
import {Provider} from "react-redux";
import configureStore from './store/configureStore';
-
+import getRoutes from './routes';
const initialState = {
posts: [],
@@ -19,9 +18,8 @@ const store = configureStore(initialState);
ReactDOM.render((
-
-
-
+
+ { getRoutes() }
), document.getElementById('root'));
diff --git a/src/reducers/auth.js b/src/reducers/auth.js
new file mode 100644
index 0000000..69b89b8
--- /dev/null
+++ b/src/reducers/auth.js
@@ -0,0 +1,49 @@
+export function isLogged(state = false, action) {
+ switch (action.type) {
+ case 'SET_IS_LOGGED':
+ return action.isLogged;
+
+ default:
+ return state;
+ }
+}
+
+export function token(state = '', action) {
+ switch (action.type) {
+ case 'SET_TOKEN':
+ return action.token;
+
+ default:
+ return state;
+ }
+}
+
+export function userData(state = {}, action) {
+ switch (action.type) {
+ case 'SET_USER_DATA':
+ return action.userData;
+
+ default:
+ return state;
+ }
+}
+
+export function formLoginIsValid(state = false, action) {
+ switch (action.type) {
+ case 'SET_LOGIN_FORM_VALID':
+ return action.formLoginIsValid;
+
+ default:
+ return state;
+ }
+}
+
+export function info(state = [], action) {
+ switch (action.type) {
+ case 'SET_INFO':
+ return action.info;
+
+ default:
+ return state;
+ }
+}
\ No newline at end of file
diff --git a/src/reducers/index.js b/src/reducers/index.js
index 6c35819..55b42dc 100644
--- a/src/reducers/index.js
+++ b/src/reducers/index.js
@@ -1,6 +1,7 @@
import { combineReducers } from 'redux';
import { posts, postsHasErrored, postsIsLoading, searchedPhrase, filteredPosts, postToDelete } from './posts';
-import { inputTitleValue, textareaBodyValue, userValue, comments, lastPostId } from './postPage';
+import { inputTitleValue, textareaBodyValue, userValue, comments, lastPostId, users } from './postPage';
+import { isLogged, token, userData, formLoginIsValid, info } from './auth';
export default combineReducers({
posts,
@@ -13,5 +14,13 @@ export default combineReducers({
textareaBodyValue,
userValue,
comments,
- lastPostId
+ lastPostId,
+
+ isLogged,
+ token,
+ userData,
+ users,
+
+ formLoginIsValid,
+ info
});
diff --git a/src/reducers/postPage.js b/src/reducers/postPage.js
index 14325fb..0770a59 100644
--- a/src/reducers/postPage.js
+++ b/src/reducers/postPage.js
@@ -55,3 +55,13 @@ export function lastPostId(state = -1, action) {
}
}
+export function users(state = [], action) {
+ switch (action.type) {
+ case 'SET_USERS':
+ return action.users;
+
+ default:
+ return state;
+ }
+}
+
diff --git a/src/routes.js b/src/routes.js
index 97955aa..851c684 100644
--- a/src/routes.js
+++ b/src/routes.js
@@ -2,13 +2,16 @@ import React from 'react';
import { Router, Route, browserHistory } from 'react-router';
import Page from "./components/Page/Page";
import PostPage from "./components/PostPage/PostPage";
+import LoginPage from "./components/LoginPage/LoginPage";
+import {requireAuthentication} from "./components/AuthComponent/AuthComponent";
export default () => {
return (
-
-
+
+
+
);
-};
+};
\ No newline at end of file
diff --git a/src/store/configureStore.js b/src/store/configureStore.js
index b048480..a2e448f 100644
--- a/src/store/configureStore.js
+++ b/src/store/configureStore.js
@@ -1,11 +1,15 @@
-import { createStore, applyMiddleware } from 'redux';
+import {createStore, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers/index';
-export default function configureStore(initialState) {
- return createStore(
- rootReducer,
- initialState,
- applyMiddleware(thunk)
- );
+export default function configureStore(initialState){
+ return createStore(
+ rootReducer,
+ initialState,
+ compose(
+ applyMiddleware(thunk),
+ window.devToolsExtension ? window.devToolsExtension() : f => f
+ )
+ );
}
+