Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@babel/preset-env": "^7.8.4",
"cookie-parser": "~1.4.4",
"core-js": "^3.6.4",
"cors": "^2.8.5",
"debug": "~2.6.9",
"express": "~4.16.1",
"http-errors": "~1.6.3",
Expand Down
2 changes: 2 additions & 0 deletions backend/src/app.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import cookieParser from "cookie-parser";
import "core-js/stable";
import cors from 'cors'
import express from "express";
import createError from "http-errors";
import logger from "morgan";
Expand All @@ -10,6 +11,7 @@ import todoRoutes from './routes/todoRoutes';
var app = express();

app.use(logger("dev"));
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
Expand Down
2 changes: 1 addition & 1 deletion backend/src/bin/www
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var http = require('http');
* Get port from environment and store in Express.
*/

var port = normalizePort(process.env.PORT || '3000');
var port = normalizePort(process.env.PORT || '4000');
app.set('port', port);

/**
Expand Down
11 changes: 9 additions & 2 deletions backend/src/controllers/todoController.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,22 @@ export const createTodo = async (req, res) => {
value,
completed: false
})
res.send({ todoId: todo.id });
res.json({ todoId: todo.id });
};

/**
* Retrieve all todos
*/
export const retrieveTodos = async (req, res) => {
const todos = await Todo.findAll();
res.send(todos);
// Strip todos so that we send minimal information to client
const strippedTodos = todos.map(todo => ({
id: todo.id,
value: todo.value,
completed: todo.completed
}));
console.log(strippedTodos);
res.json(strippedTodos);
};

/**
Expand Down
18 changes: 18 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^7.2.1",
"core-js": "^3.6.4",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-redux": "^7.1.3",
"react-scripts": "3.3.1",
"redux": "^4.0.5"
"redux": "^4.0.5",
Comment thread
rocketkai marked this conversation as resolved.
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0",
"regenerator-runtime": "^0.13.3"
},
"scripts": {
"start": "react-scripts start",
Expand Down
24 changes: 24 additions & 0 deletions frontend/src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ export const addTodo = text => ({
text
})

export const requestTodos = () => ({
type: 'REQUEST_TODOS'
})

export const receiveTodos = todos => ({
type: 'RECEIVE_TODOS',
todos,
})

export const setVisibilityFilter = filter => ({
type: 'SET_VISIBILITY_FILTER',
filter
Expand All @@ -19,4 +28,19 @@ export const VisibilityFilters = {
SHOW_ALL: 'SHOW_ALL',
SHOW_COMPLETED: 'SHOW_COMPLETED',
SHOW_ACTIVE: 'SHOW_ACTIVE'
}

export const fetchTodos = () => {
return dispatch => {
dispatch(requestTodos())
return fetch('http://localhost:4000/todos/retrieve')
.then(response => {
console.log(response)
return response.json()
})
.then(todos => {
console.log(todos)
dispatch(receiveTodos(todos))
})
}
}
File renamed without changes.
6 changes: 3 additions & 3 deletions frontend/src/components/Todo.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import React from 'react'
import PropTypes from 'prop-types'

const Todo = ({ onClick, completed, text }) => (
const Todo = ({ onClick, completed, value }) => (
<li
onClick={onClick}
style={{
textDecoration: completed ? 'line-through' : 'none'
}}
>
{text}
{value}
</li>
)

Todo.propTypes = {
onClick: PropTypes.func.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
value: PropTypes.string.isRequired
}

export default Todo
25 changes: 17 additions & 8 deletions frontend/src/components/TodoList.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import React from 'react'
import PropTypes from 'prop-types'

import Todo from './Todo'

const TodoList = ({ todos, toggleTodo }) => (
<ul>
{todos.map(todo => (
<Todo key={todo.id} {...todo} onClick={() => toggleTodo(todo.id)} />
))}
</ul>
)
class TodoList extends React.Component {
componentDidMount() {
this.props.fetchTodos()
}

render() {
return (
<ul>
{this.props.todos.map(todo => (
<Todo key={todo.id} {...todo} onClick={() => this.props.toggleTodo(todo.id)} />
))}
</ul>
)
}
}

TodoList.propTypes = {
todos: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
value: PropTypes.string.isRequired
}).isRequired
).isRequired,
toggleTodo: PropTypes.func.isRequired
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/configureStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createStore, applyMiddleware } from 'redux'
import { createLogger } from 'redux-logger'
import thunkMiddleware from 'redux-thunk'

import rootReducer from './reducers'

const loggerMiddleware = createLogger()

export default function configureStore(preloadedState) {
return createStore(
rootReducer,
preloadedState,
applyMiddleware(thunkMiddleware, loggerMiddleware)
)
}
5 changes: 3 additions & 2 deletions frontend/src/containers/VisibleTodoList.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { connect } from 'react-redux'
import { toggleTodo, VisibilityFilters } from '../actions'
import { fetchTodos, toggleTodo, VisibilityFilters } from '../actions'
import TodoList from '../components/TodoList'

const getVisibleTodos = (todos, filter) => {
Expand All @@ -16,10 +16,11 @@ const getVisibleTodos = (todos, filter) => {
}

const mapStateToProps = state => ({
todos: getVisibleTodos(state.todos, state.visibilityFilter)
todos: getVisibleTodos(state.todos.todos, state.visibilityFilter)
})

const mapDispatchToProps = dispatch => ({
fetchTodos: () => dispatch(fetchTodos()),
toggleTodo: id => dispatch(toggleTodo(id))
})

Expand Down
9 changes: 5 additions & 4 deletions frontend/src/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import 'core-js/stable';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import { Provider } from 'react-redux';
import 'regenerator-runtime';

import App from './components/App';
import rootReducer from './reducers'
import configureStore from './configureStore';

const store = createStore(rootReducer)
const store = configureStore()

render(
<Provider store={store}>
Expand Down
44 changes: 33 additions & 11 deletions frontend/src/reducers/todos.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,40 @@
const todos = (state = [], action) => {
const initialState = {
isFetching: false,
todos: []
}

const todos = (state = initialState, action) => {
switch (action.type) {
case 'ADD_TODO':
return [
return {
...state,
todos: [
...state.todos,
{
id: action.id,
value: action.value,
completed: false
}
]
}
case 'REQUEST_TODOS':
return {
...state,
{
id: action.id,
text: action.text,
completed: false
}
]
isFetching: true,
}
case 'RECEIVE_TODOS':
return {
...state,
isFetching: false,
todos: action.todos
}
case 'TOGGLE_TODO':
return state.map(todo =>
todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
)
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
)
}
default:
return state
}
Expand Down