-
Notifications
You must be signed in to change notification settings - Fork 46
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
base: main
Are you sure you want to change the base?
Changes from all commits
8232bfb
7da705b
ebed14a
8bb3350
abbcb68
b80c35c
71acf68
481053d
cfd9eba
99f04cb
2742f5b
e349680
7425f17
04e8a15
cbe9f0f
55ff931
a98eff8
796866b
5a6698e
dfd0be7
bd6bb5c
edf7ed5
967ce2b
f7edbc1
cff0559
d3577af
176c48b
b151ef5
10c2a5e
301b2ab
a46259f
d6c0407
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
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"; | ||
|
||
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} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
|
||
handleEditTodo={this.handleEditTodo} | ||
handleAddToComplete={this.handleAddToComplete} | ||
handleAddToActive={this.handleAddToActive} | ||
completeTodos={completeTodos} | ||
todoName={todoName} | ||
isEmpty={isEmpty} | ||
todoLength={activeTodos.length} | ||
/> | ||
)} | ||
/> | ||
</BrowserRouter> | ||
); | ||
} | ||
} | ||
|
||
export default App; |
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; | ||
} |
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; |
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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from "./AppFooter"; |
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; |
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; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from "./AppHeader"; |
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 ( | ||
<> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; |
There was a problem hiding this comment.
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.