diff --git a/index.html b/index.html index c3452f25e3..b588b33868 100644 --- a/index.html +++ b/index.html @@ -9,5 +9,5 @@
- + diff --git a/src/actions/index.js b/src/actions/index.js index e69de29bb2..c6ee26971b 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -0,0 +1,46 @@ +import axios from "axios"; + +export const FETCH_POSTS = "fetch_posts"; +export const CREATE_POST = "create_posts"; +export const FETCH_POST = "fetch_post"; +export const DELETE_POST = "delete_post"; + +const ROOT_URL = "https://reduxblog.herokuapp.com/api"; +const API_KEY = "?key=mykey4445"; +const url = `${ROOT_URL}/posts${API_KEY}`; + +export function fetchPosts() { + const request = axios.get(url); + + return { + type: FETCH_POSTS, + payload: request + }; +} + +export function createPost(values, callback) { + const request = axios.post(url, values).then(() => callback()); + return { + type: CREATE_POST, + payload: request + }; +} + +export function fetchPost(id) { + const request = axios.get(`${ROOT_URL}/posts/${id}${API_KEY}`); + + return { + type: FETCH_POST, + payload: request + }; +} + +export function deletePost(id, callback) { + const request = axios + .delete(`${ROOT_URL}/posts/${id}${API_KEY}`) + .then(() => callback()); + return { + type: DELETE_POST, + payload: id + }; +} diff --git a/src/components/app.js b/src/components/app.js deleted file mode 100644 index 58614b02cf..0000000000 --- a/src/components/app.js +++ /dev/null @@ -1,9 +0,0 @@ -import React, { Component } from 'react'; - -export default class App extends Component { - render() { - return ( -
React simple starter
- ); - } -} diff --git a/src/components/posts_index.js b/src/components/posts_index.js new file mode 100644 index 0000000000..2850bb5525 --- /dev/null +++ b/src/components/posts_index.js @@ -0,0 +1,43 @@ +import _ from "lodash"; +import React, { Component } from "react"; + +import { connect } from "react-redux"; +import { Link } from "react-router-dom"; +import { fetchPosts } from "../actions"; + +class PostIndex extends Component { + componentDidMount() { + this.props.fetchPosts(); + } + + renderPosts() { + return _.map(this.props.posts, post => { + return ( +
  • + {post.title} +
  • + ); + }); + } + + render() { + return ( +
    +
    + + add a Post + +
    + +

    Posts:

    + +
    + ); + } +} + +function mapStateToProps(state) { + return { posts: state.posts }; +} + +export default connect(mapStateToProps, { fetchPosts })(PostIndex); diff --git a/src/components/posts_new.js b/src/components/posts_new.js new file mode 100644 index 0000000000..4f27d97e80 --- /dev/null +++ b/src/components/posts_new.js @@ -0,0 +1,91 @@ +import React, { Component } from "react"; +import { Link } from "react-router-dom"; +import { Field, reduxForm } from "redux-form"; +import {connect} from 'react-redux'; +import {createPost} from '../actions'; + +class PostsNew extends Component { + renderField(field) { + const { meta: { touched, error} } =field; + const className=`form-group ${touched && error ? 'has-danger': ''}`; + return ( +
    + + +
    + {touched ? error: ''} +
    + +
    + ); + } + +onSubmit(values){ + +this.props.createPost(values, () => { + this.props.history.push('/'); + +}); + +} + + render() { + const { handleSubmit } = this.props + return ( +
    + + +
    + + + + + + Cancel + + +
    + ); + } +} + +function validate(values) { + const errors = {}; + + //Validate the inputs from 'values' + if (!values.title) { + errors.title = "Enter a title!"; + } + if (!values.categories) { + errors.categories = "Enter a categories!"; + } + if (!values.content) { + errors.content = "Enter some content!"; + } + + // If errors is empty the form is fine to submit + //If errors has *any* properties, redux form assumes form is valid + return errors; +} + +export default reduxForm({ + validate, + form: "PostsNewForm" +})( + connect(null,{ createPost })(PostsNew) + ); + diff --git a/src/components/posts_show.js b/src/components/posts_show.js new file mode 100644 index 0000000000..8df76bdcf3 --- /dev/null +++ b/src/components/posts_show.js @@ -0,0 +1,52 @@ +import React, { Component } from "react"; +import { connect } from "react-redux"; +import { fetchPost, deletePost } from "../actions"; +import { Link } from "react-router-dom"; + +class PostsShow extends Component { + componentDidMount() { + //To fetch info just once + // if (!this.props.post) { + const { id } = this.props.match.params; + this.props.fetchPost(id); + } + // } + + onDeleteClick(){ + const { id } = this.props.match.params; + this.props.deletePost(id, () => { + this.props.history.push('/'); + }); + } + + render() { + const { post } = this.props; + + if (!post) { + return
    Loading...
    ; + } + + return ( +
    + + Back to index + + +

    {post.title}

    +
    Categories:{post.categories}
    +

    {post.content}

    +
    + ); + } +} + +function mapStateToProps({ posts }, ownProps) { + return { post: posts[ownProps.match.params.id] }; +} + +export default connect(mapStateToProps, { fetchPost, deletePost })(PostsShow); diff --git a/src/index.js b/src/index.js index 69d577acd1..4db991f9ba 100644 --- a/src/index.js +++ b/src/index.js @@ -1,15 +1,29 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { Provider } from 'react-redux'; -import { createStore, applyMiddleware } from 'redux'; +import React from "react"; +import ReactDOM from "react-dom"; +import { Provider } from "react-redux"; +import { createStore, applyMiddleware } from "redux"; +import { BrowserRouter, Route, Switch } from "react-router-dom"; +import promise from "redux-promise"; +import '/style/style.css'; -import App from './components/app'; -import reducers from './reducers'; +import reducers from "./reducers"; +import PostsIndex from "./components/posts_index"; +import PostsNew from "./components/posts_new"; +import PostsShow from "./components/posts_show"; -const createStoreWithMiddleware = applyMiddleware()(createStore); +const createStoreWithMiddleware = applyMiddleware(promise)(createStore); ReactDOM.render( - - - , document.querySelector('.container')); + +
    + + + + + +
    +
    + , + document.querySelector(".container") +); diff --git a/src/reducers/index.js b/src/reducers/index.js index d12506f382..d2de719c95 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -1,7 +1,10 @@ import { combineReducers } from 'redux'; +import { reducer as formReducer} from 'redux-form'; +import PostReducer from './reducer_posts' const rootReducer = combineReducers({ - state: (state = {}) => state + posts: PostReducer, + form: formReducer }); export default rootReducer; diff --git a/src/reducers/reducer_posts.js b/src/reducers/reducer_posts.js new file mode 100644 index 0000000000..7962e9a2a4 --- /dev/null +++ b/src/reducers/reducer_posts.js @@ -0,0 +1,23 @@ +import _ from "lodash"; +import { FETCH_POSTS, FETCH_POST, DELETE_POST } from "../actions"; + +export default function(state = {}, action) { + switch (action.type) { + case DELETE_POST: + return _.omit(state, action.payload); + + case FETCH_POST: + // const post = action.payload.data; + // const newState = { ...state }; + // newState[post.id] = post; + // return newState; + + return { ...state, [action.payload.data.id]: action.payload.data }; + + case FETCH_POSTS: + return _.mapKeys(action.payload.data, "id"); + + default: + return state; + } +} diff --git a/style/style.css b/style/style.css index e69de29bb2..efb9b1240d 100644 --- a/style/style.css +++ b/style/style.css @@ -0,0 +1,4 @@ +form a{ + margin-left:10px; + +} \ No newline at end of file