Skip to content

Solution: Nacho Montoya & Armando Vieira #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8232bfb
first structure
Nachomontoya May 19, 2021
7da705b
structure updated
Nachomontoya May 19, 2021
ebed14a
making todocontainer
armandovieirag May 19, 2021
8bb3350
fixing
armandovieirag May 19, 2021
abbcb68
ready
armandovieirag May 20, 2021
b80c35c
component Main
Nachomontoya May 20, 2021
71acf68
on All
Nachomontoya May 20, 2021
481053d
comments
Nachomontoya May 20, 2021
cfd9eba
Todo uploaded
Nachomontoya May 20, 2021
99f04cb
Fixed modules CBtn-Canbtn
armandovieirag May 20, 2021
2742f5b
AppHeader component
Nachomontoya May 20, 2021
e349680
footer component
Nachomontoya May 20, 2021
7425f17
Finished Todo-input
armandovieirag May 20, 2021
04e8a15
app updated
Nachomontoya May 20, 2021
cbe9f0f
merged with develop
Nachomontoya May 20, 2021
55ff931
api
Nachomontoya May 20, 2021
a98eff8
showing todos from array
Nachomontoya May 20, 2021
796866b
Create To do input
armandovieirag May 20, 2021
5a6698e
show todos updated
Nachomontoya May 21, 2021
dfd0be7
merged with develop
Nachomontoya May 21, 2021
bd6bb5c
no todos to show message updated
Nachomontoya May 21, 2021
edf7ed5
delete Completed
armandovieirag May 21, 2021
967ce2b
merged with develop
Nachomontoya May 21, 2021
f7edbc1
styles
Nachomontoya May 21, 2021
cff0559
edit todos
Nachomontoya May 21, 2021
d3577af
added localstorage
armandovieirag May 22, 2021
176c48b
completed btn and items left
armandovieirag May 23, 2021
b151ef5
fixing style
armandovieirag May 23, 2021
10c2a5e
store and update the complete and active arrays and updated local sto…
Nachomontoya May 24, 2021
301b2ab
updated arrays
Nachomontoya May 24, 2021
a46259f
navigation
Nachomontoya May 24, 2021
d6c0407
updated
Nachomontoya May 25, 2021
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
7 changes: 6 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ module.exports = {
},
],
rules: {
"prettier/prettier": "error",
"prettier/prettier": [
"error",
{
endOfLine: "auto",
},
],
"react/jsx-filename-extension": "off",
"import/prefer-default-export": "off",
"prefer-destructuring": "off",
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

.env
# Created by https://www.toptal.com/developers/gitignore/api/react,node
# Edit at https://www.toptal.com/developers/gitignore?templates=react,node

Expand Down
20,008 changes: 19,982 additions & 26 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"sass": "^1.32.11",
"uuid": "^8.3.2",
"web-vitals": "^1.1.1"
},
"devDependencies": {
Expand Down
263 changes: 251 additions & 12 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,254 @@
import React from "react";

function App() {
return (
<main className="container mt-5">
<section className="row">
<div className="col col-12">
<h1>Hola mundo</h1>
</div>
</section>
</main>
);
import React, { Component } from "react";
import { v4 as uuidv4 } from "uuid";
import { BrowserRouter, Route } from "react-router-dom";

import All from "./pages/All";
import Completed from "./pages/Completed";
import Actives from "./pages/Actives";
import "./App.scss";

const LOCAL_STORAGE_KEY = "all-todos";
const LOCAL_STORAGE_KEY_COMPLETED = "completed-todos";
const LOCAL_STORAGE_KEY_ACTIVE = "active-todos";
Comment on lines +10 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Muy bien pensado para que sea fácil de gestionar. También podríais tener todo en un objeto para tener solo una key en localStorage pero si os era más sencillo de gestionar así genial.


function loadLocalStorageData(page) {
const prevItems = localStorage.getItem(page);

if (!prevItems) {
return false;
}

try {
return JSON.parse(prevItems);
} catch (error) {
return null;
}
}
class App extends Component {
constructor(props) {
super(props);

this.state = {
isEmpty: true,
allTodos: [],
completeTodos: [],
activeTodos: [],
todoName: "",
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleAddTodo = this.handleAddTodo.bind(this);
this.handleRemove = this.handleRemove.bind(this);
this.handleChecked = this.handleChecked.bind(this);
this.handleEditTodo = this.handleEditTodo.bind(this);
this.handleAddToComplete = this.handleAddToComplete.bind(this);
this.handleAddToActive = this.handleAddToActive.bind(this);
}

componentDidMount() {
const prevItems = loadLocalStorageData(LOCAL_STORAGE_KEY);
const prevCompletedItems = loadLocalStorageData(
LOCAL_STORAGE_KEY_COMPLETED,
);
const prevActiveItems = loadLocalStorageData(LOCAL_STORAGE_KEY_ACTIVE);
if (!prevItems) {
this.setState({
isEmpty: true,
});
}

if (prevItems.length > 0) {
this.setState({
allTodos: prevItems,
completeTodos: prevCompletedItems,
activeTodos: prevActiveItems,
isEmpty: false,
});
}
}

componentDidUpdate() {
const { allTodos, completeTodos, activeTodos } = this.state;
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(allTodos));
localStorage.setItem(
LOCAL_STORAGE_KEY_COMPLETED,
JSON.stringify(completeTodos),
);
localStorage.setItem(LOCAL_STORAGE_KEY_ACTIVE, JSON.stringify(activeTodos));
}

handleAddTodo({ todoName, allTodos, activeTodos }) {
const newTodo = {
id: uuidv4(),
name: todoName,
complete: false,
};
this.setState({
allTodos: [...allTodos, newTodo],
activeTodos: [...activeTodos, newTodo],
todoName: "",
isEmpty: false,
});
}

handleSubmit(e) {
e.preventDefault();
this.handleAddTodo(this.state);
}

handleChange(e) {
this.setState({ todoName: e.target.value });
}

handleEditTodo(value, id) {
const { allTodos } = this.state;
const todoIndexInArray = allTodos.findIndex((todo) => todo.id === id);
allTodos[todoIndexInArray].name = value;
this.setState({
allTodos,
});
}

handleRemove(id) {
const { allTodos, activeTodos } = this.state;
const restOfTodosInAll = allTodos.filter((todo) => todo.id !== id);
const restOfTodosActives = activeTodos.filter((todo) => todo.id !== id);

this.setState({
allTodos: restOfTodosInAll,
activeTodos: restOfTodosActives,
});

if (restOfTodosInAll.length === 0) {
this.setState({
isEmpty: true,
});
}
}

handleChecked(id) {
const { allTodos } = this.state;

const arr = allTodos.map((todo) => {
if (todo.id === id) {
return { ...todo, complete: !todo.complete };
}
return todo;
});

this.setState({
allTodos: arr,
});
}

handleAddToComplete(id) {
const { allTodos, completeTodos } = this.state;

const todoFound = allTodos.find((item) => item.id === id);
const todoInComplete = completeTodos.find((todo) => todo.id === id);

if (todoInComplete) {
const updateTodo = completeTodos.filter((todo) => todo.id !== id);
this.setState({
completeTodos: updateTodo,
});
}

if (!todoInComplete) {
this.setState({
completeTodos: [...completeTodos, todoFound],
});
}
}

handleAddToActive(id) {
const { allTodos, completeTodos, activeTodos } = this.state;

const todoFound = allTodos.find((item) => item.id === id);
const todoInComplete = completeTodos.find((item) => item.id === id);
const todoInActive = activeTodos.find((todo) => todo.id === id);

if (todoInComplete && !todoInActive) {
this.setState({
activeTodos: [...activeTodos, todoFound],
});
}

if (todoInActive) {
const updateTodo = activeTodos.filter((todo) => todo.id !== id);
this.setState({
activeTodos: updateTodo,
});
}
}

render() {
const { allTodos, todoName, isEmpty, activeTodos, completeTodos } =
this.state;
return (
<BrowserRouter>
<Route
path="/"
exact
render={(routeProps) => (
<All
{...routeProps}
handleSubmit={this.handleSubmit}
handleChange={this.handleChange}
handleRemove={this.handleRemove}
handleChecked={this.handleChecked}
handleEditTodo={this.handleEditTodo}
handleAddToComplete={this.handleAddToComplete}
handleAddToActive={this.handleAddToActive}
allTodos={allTodos}
todoName={todoName}
isEmpty={isEmpty}
todoLength={activeTodos.length}
/>
)}
/>
<Route
path="/active"
exact
render={(routeProps) => (
<Actives
{...routeProps}
handleSubmit={this.handleSubmit}
handleChange={this.handleChange}
handleRemove={this.handleRemove}
handleEditTodo={this.handleEditTodo}
handleAddToComplete={this.handleAddToComplete}
handleAddToActive={this.handleAddToActive}
activeTodos={activeTodos}
todoName={todoName}
isEmpty={isEmpty}
todoLength={activeTodos.length}
/>
)}
/>
<Route
path="/completed"
exact
render={(routeProps) => (
<Completed
{...routeProps}
handleSubmit={this.handleSubmit}
handleChange={this.handleChange}
handleRemove={this.handleRemove}
// handleChecked={this.handleChecked}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

En este caso se ha dejado el prop comentado y si probamos de marca un todo como completado desde esta página fallará porque el prop handleChecked será ejecutado como función pero será undefined.

TypeError: handleChecked is not a function
onsetHandlecompleted
src/components/CompletedButton/CompletedButton.js:13
  10 |   handleAddToActive,
  11 | }) {
  12 |   function onsetHandlecompleted() {
> 13 |     handleChecked(id);
  14 |     handleAddToComplete(id);
  15 |     handleAddToActive(id);
  16 |   }
View compiled

handleEditTodo={this.handleEditTodo}
handleAddToComplete={this.handleAddToComplete}
handleAddToActive={this.handleAddToActive}
completeTodos={completeTodos}
todoName={todoName}
isEmpty={isEmpty}
todoLength={activeTodos.length}
/>
)}
/>
</BrowserRouter>
);
}
}

export default App;
9 changes: 9 additions & 0 deletions src/App.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@import url("https://fonts.googleapis.com/css2?family=Maven+Pro:wght@400;500&family=Montserrat:wght@600&display=swap");

* {
font-family: "Maven Pro", sans-serif;
}

ul {
padding-left: 0;
}
15 changes: 15 additions & 0 deletions src/components/AppFooter/AppFooter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";
import "./AppFooter.scss";
import NavItem from "../NavItem";

function AppFooter({ todoLength }) {
return (
<footer className="d-flex justify-content-between p-3 ">
<div className="mGreyFont"> {todoLength} items left</div>
<NavItem />
<div className="mGreyFont">Clear Completed</div>
</footer>
);
}

export default AppFooter;
9 changes: 9 additions & 0 deletions src/components/AppFooter/AppFooter.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
footer {
width: 100%;
height: 60px;
border-top: 0.5px solid #d3d3d3;

.mGreyFont {
color: #a8a8a8;
}
}
1 change: 1 addition & 0 deletions src/components/AppFooter/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./AppFooter";
14 changes: 14 additions & 0 deletions src/components/AppHeader/AppHeader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from "react";
import "./AppHeader.scss";
import DarkButton from "../DarkButton";

function AppHeader() {
return (
<header className="d-flex flex-row justify-content-between mb-4">
<h1 className="title">TODO</h1>
<DarkButton />
</header>
);
}

export default AppHeader;
6 changes: 6 additions & 0 deletions src/components/AppHeader/AppHeader.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.title {
color: white;
font-family: "Montserrat", sans-serif;
font-weight: 600;
letter-spacing: 12px;
}
1 change: 1 addition & 0 deletions src/components/AppHeader/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./AppHeader";
31 changes: 31 additions & 0 deletions src/components/CompletedButton/CompletedButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from "react";

import "./CompletedButton.scss";

function CompletedButton({
id,
handleChecked,
isComplete,
handleAddToComplete,
handleAddToActive,
}) {
function onsetHandlecompleted() {
handleChecked(id);
handleAddToComplete(id);
handleAddToActive(id);
}
return (
<>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

En este caso el Fragment no es necesario ya que el input no tiene elementos mostrados al mismo nivel.

<input
className="checkbox-round"
type="checkbox"
id={id}
// onClick={onsetHandlecompleted}
checked={isComplete}
onChange={onsetHandlecompleted}
/>
</>
);
}

export default CompletedButton;
Loading