diff --git a/package.json b/package.json index 4488cb3..0e289d2 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,18 @@ { - "name": "swppfront", + "name": "todo", "version": "0.1.0", "private": true, + "proxy": "http://localhost:8000", "dependencies": { + "axios": "^0.20.0", "react": "^16.9.0", "react-dom": "^16.9.0", + "react-redux": "^7.2.1", "react-router": "^5.0.1", "react-router-dom": "^5.0.1", - "react-scripts": "3.1.1" + "react-scripts": "3.1.1", + "redux": "^4.0.5", + "redux-thunk": "^2.3.0" }, "scripts": { "start": "react-scripts start", diff --git a/src/components/Todo/Todo.js b/src/components/Todo/Todo.js index 9c80927..95d8fcd 100644 --- a/src/components/Todo/Todo.js +++ b/src/components/Todo/Todo.js @@ -8,10 +8,12 @@ const Todo = (props) => {
+ onClick={props.clickDetail}> {props.title}
{props.done &&
} + +
); }; diff --git a/src/containers/TodoList/NewTodo/NewTodo.js b/src/containers/TodoList/NewTodo/NewTodo.js index 59e7930..7c30bf2 100644 --- a/src/containers/TodoList/NewTodo/NewTodo.js +++ b/src/containers/TodoList/NewTodo/NewTodo.js @@ -4,6 +4,19 @@ import { Redirect } from 'react-router-dom'; import './NewTodo.css'; +import { connect } from 'react-redux'; +import * as actionTypes from '../../../store/actions/actionTypes'; +import * as actionCreators from '../../../store/actions/index'; + +const mapDispatchToProps = dispatch => { + return { + onStoreTodo: (title, content) => + dispatch(actionCreators.postTodo({title: title, content: content})) +// dispatch({ type: actionTypes.ADD_TODO, title: title, content: content }) +// }; + } +}; + class NewTodo extends Component { state = { title: '', @@ -12,12 +25,16 @@ class NewTodo extends Component { } postTodoHandler = () => { + this.props.onStoreTodo(this.state.title, this.state.content); + //this.setState({ submitted: true }); + const data = { title: this.state.title, content: this.state.content } alert('submitted' + data.title); // this.props.history.push('/todos'); this.props.history.goBack(); this.setState({ submitted: true }); + } render() { @@ -36,7 +53,7 @@ class NewTodo extends Component { > @@ -45,4 +62,4 @@ class NewTodo extends Component { } } -export default NewTodo; \ No newline at end of file +export default connect(null, mapDispatchToProps)(NewTodo); diff --git a/src/containers/TodoList/RealDetail/RealDetail.js b/src/containers/TodoList/RealDetail/RealDetail.js index 89500a3..bb48eb2 100644 --- a/src/containers/TodoList/RealDetail/RealDetail.js +++ b/src/containers/TodoList/RealDetail/RealDetail.js @@ -1,23 +1,35 @@ import React, { Component } from 'react'; import './RealDetail.css'; +import * as actionTypes from '../../../store/actions/actionTypes'; +import { connect } from 'react-redux'; + +import * as actionCreators from '../../../store/actions/index'; class RealDetail extends Component { + componentDidMount() { + this.props.onGetTodo(parseInt(this.props.match.params.id)); + } render() { + let title = ''; let content = ''; + if (this.props.selectedTodo){ + title = this.props.selectedTodo.title; + content = this.props.selectedTodo.content; + } return (
Name:
-
+
{title}
Content:
-
+
{content}
@@ -25,4 +37,16 @@ class RealDetail extends Component { } }; -export default RealDetail; \ No newline at end of file +const mapStateToProps = state => { + return { + selectedTodo: state.td.selectedTodo, + } +} +const mapDispatchToProps = dispatch => { + return { + onGetTodo: id => + dispatch(actionCreators.getTodo(id)), + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(RealDetail); \ No newline at end of file diff --git a/src/containers/TodoList/TodoList.js b/src/containers/TodoList/TodoList.js index 6cb72c1..3ee4b14 100644 --- a/src/containers/TodoList/TodoList.js +++ b/src/containers/TodoList/TodoList.js @@ -7,7 +7,22 @@ import { NavLink } from 'react-router-dom'; import './TodoList.css'; +import { connect } from 'react-redux'; + +import * as actionTypes from '../../store/actions/actionTypes'; + +import { withRouter } from 'react-router'; +import axios from 'axios'; +import * as actionCreators from '../../store/actions/index'; + +const mapStateToProps = state => { + return { + storedTodos: state.td.todos + }; +}; + class TodoList extends Component { + state = { todos: [ { id: 1, title: 'SWPP', content: 'take swpp class', done: true }, @@ -15,24 +30,26 @@ class TodoList extends Component { { id: 3, title: 'Dinner', content: 'eat dinner', done: false } ], selectedTodo: null, + }; + + componentDidMount() { + this.props.onGetAll(); } clickTodoHandler = (td) => { - if (this.state.selectedTodo === td) { - this.setState({ ...this.state, selectedTodo: null }); - } else { - this.setState({ ...this.state, selectedTodo: td }); - } - } + this.props.history.push('/todos/'+td.id+'/'); + }; render() { - const todos = this.state.todos.map(td => { + const todos = this.props.storedTodos.map((td) => { return ( this.clickTodoHandler(td)} + clickDetail={() => this.clickTodoHandler(td)} + clickDone={() => this.props.onToggleTodo(td.id)} + clickDelete={() => this.props.onDeleteTodo(td.id)} /> ); }); @@ -59,4 +76,14 @@ class TodoList extends Component { } } -export default TodoList; \ No newline at end of file +const mapDispatchToProps = dispatch => { + return { + onToggleTodo: id => + dispatch(actionCreators.toggleTodo(id)), + onDeleteTodo: id => + dispatch(actionCreators.deleteTodo(id)), + onGetAll: () => dispatch(actionCreators.getTodos()), + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(withRouter(TodoList)); diff --git a/src/index.js b/src/index.js index 87d1be5..0f5c540 100644 --- a/src/index.js +++ b/src/index.js @@ -4,7 +4,21 @@ import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; -ReactDOM.render(, document.getElementById('root')); +import { Provider } from 'react-redux'; +import { createStore, applyMiddleware, combineReducers } from 'redux'; + +import todoReducer from './store/reducers/todo'; +import thunk from 'redux-thunk'; + +const rootReducer = combineReducers({ + td: todoReducer, +}); + +const store = createStore(rootReducer, applyMiddleware(thunk)); +//const store = createStore(rootReducer,window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()); + + +ReactDOM.render(, document.getElementById('root')); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. diff --git a/src/store/actionCreators/action b/src/store/actionCreators/action new file mode 100644 index 0000000..e69de29 diff --git a/src/store/actions/actionTypes.js b/src/store/actions/actionTypes.js new file mode 100644 index 0000000..8b4377d --- /dev/null +++ b/src/store/actions/actionTypes.js @@ -0,0 +1,5 @@ +export const GET_ALL = 'GET_ALL'; +export const GET_TODO = 'GET_TODO'; +export const TOGGLE_DONE = 'TOGGLE_DONE'; +export const DELETE_TODO = 'DELETE_TODO'; +export const ADD_TODO = 'ADD_TODO'; diff --git a/src/store/actions/index.js b/src/store/actions/index.js new file mode 100644 index 0000000..f75610a --- /dev/null +++ b/src/store/actions/index.js @@ -0,0 +1 @@ +export { getTodos, postTodo, deleteTodo, toggleTodo, getTodo } from './todo'; \ No newline at end of file diff --git a/src/store/actions/todo.js b/src/store/actions/todo.js new file mode 100644 index 0000000..275e3db --- /dev/null +++ b/src/store/actions/todo.js @@ -0,0 +1,75 @@ +import * as actionTypes from './actionTypes'; +import axios from 'axios'; + +export const getTodos_ = todos => { + return { type: actionTypes.GET_ALL, todos: todos}; +} + +export const getTodos = () => { + return dispatch => { + return axios.get('/api/todo/') + .then(res => dispatch(getTodos_(res.data))); + } +} + +export const postTodo_ = td => { + return { + type: actionTypes.ADD_TODO, + id: td.id, + title: td.title, + content: td.content + } +} +export const postTodo = td => { + return dispatch => { + return axios.post('/api/todo/', td) + .then(res => { + dispatch(postTodo_(res.data)); + }) + } +} + +export const deleteTodo_ = id => { + return { + type: actionTypes.DELETE_TODO, + targetID: id + } +} +export const deleteTodo = id => { + return (dispatch) => { + return axios.delete('/api/todo/'+id) + .then(res => { + dispatch(deleteTodo_(id)); + }) + } +} + +export const toggleTodo_ = id => { + return { + type: actionTypes.TOGGLE_DONE, + targetID: id + } +} +export const toggleTodo = id => { + return (dispatch) => { + return axios.put('/api/todo/'+id) + .then(res => { + dispatch(toggleTodo_(id)); + }) + } +} + +export const getTodo_ = todo => { + return { + type: actionTypes.GET_TODO, + target: todo + } +} +export const getTodo = id => { + return (dispatch) => { + return axios.get('/api/todo/'+id) + .then(res => { + dispatch(getTodo_(res.data)); + }) + } +} \ No newline at end of file diff --git a/src/store/reducers/todo.js b/src/store/reducers/todo.js new file mode 100644 index 0000000..c7899c1 --- /dev/null +++ b/src/store/reducers/todo.js @@ -0,0 +1,41 @@ +import * as actionTypes from '../actions/actionTypes'; +const initialState = { + todos: [ + { id: 1, title: 'SWPP', content: 'take swpp class', done: true }, + { id: 2, title: 'Movie', content: 'watch movie', done: false }, + { id: 3, title: 'Dinner', content: 'eat dinner', done: false } + ], + selectedTodo: null +}; +const reducer = (state = initialState, action) => { + switch (action.type){ + case actionTypes.ADD_TODO: + const newTodo = { + id: action.id, + title: action.title, content: action.content, done:action.done + } + return {...state, todos: [...state.todos, newTodo]}; + case actionTypes.DELETE_TODO: + const deleted = state.todos.filter(todo => { + return todo.id !== action.targetID; + }); + return { ...state, todos: deleted}; + case actionTypes.TOGGLE_DONE: + const modified = state.todos.map(todo => { + if (todo.id === action.targetID){ + return { ...todo, done: !todo.done}; + } else{ + return {...todo }; + } + }); + return {...state, todos: modified}; + case actionTypes.GET_TODO: + return { ...state, selectedTodo: action.target }; + case actionTypes.GET_ALL: + return {...state, todos: action.todos}; + default: + break; + } + return state; +} +export default reducer;