Skip to content

Solution: Jonathan Cedeño & Alfonso García #12

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 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
52dcc2e
resolved initial errors
jonathancede May 19, 2021
a9f032d
first step of structure
jonathancede May 19, 2021
d02f1c9
Merge pull request #1 from jonathancede/initial-structure
jonathancede May 20, 2021
5cffae0
implemented first steps of Header component
jonathancede May 21, 2021
b823cdb
index.js for Header component
jonathancede May 21, 2021
b5c4251
added export in index for api
jonathancede May 21, 2021
352a8f6
added NewTask component with its index and schema for formik
jonathancede May 21, 2021
105f7ac
added lines to correct unused errors
jonathancede May 21, 2021
4ceed55
added formik and yup in npm packages
jonathancede May 21, 2021
0fd154b
implemented Header and NewTask in the main app
jonathancede May 21, 2021
0d81d12
start with styles of Header
jonathancede May 21, 2021
cb31525
TasksList and footer for merging
Kirtar May 21, 2021
cc6dd1b
implemented icons dark and light mode
jonathancede May 21, 2021
7ca9284
fixed conflicts
jonathancede May 21, 2021
0d5d32d
Merge pull request #2 from jonathancede/header-and-newtask
jonathancede May 21, 2021
d1eabe8
inserted uuid module
jonathancede May 21, 2021
4232191
eliminated provisional title of list
jonathancede May 21, 2021
c62865d
inserted id property in new task and changed taskText by title
jonathancede May 21, 2021
58dc33b
task update and basic styles
Kirtar May 21, 2021
5197d52
fixed margins
Kirtar May 21, 2021
e6eb9e5
imported files to sumit button
jonathancede May 24, 2021
2278da0
style of Header component
jonathancede May 24, 2021
faef38e
style of NewTask component
jonathancede May 24, 2021
aef14c1
deleted boxes check
jonathancede May 24, 2021
129d2d1
details of NewTask style
jonathancede May 24, 2021
af5428e
reset form after submit in NewTask component
jonathancede May 24, 2021
1c3718a
tasksList and listFooter styles
Kirtar May 25, 2021
b485eee
same class to text input when fail
jonathancede May 25, 2021
44035ed
style of checkbox custom
jonathancede May 25, 2021
fddec88
image check
jonathancede May 25, 2021
a8bc7f3
eliminate innecesary part
jonathancede May 25, 2021
0f1eb59
Merge pull request #3 from jonathancede/header-newtask-styles
jonathancede May 25, 2021
4d84df2
last details styles
jonathancede 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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,5 @@ dist
**/*.backup.*
**/*.back.*

node_modules
node_modules
package-lock.json
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
"tabWidth": 2,
"trailingComma": "all",
"useTabs": false,
"endOfLine": "lf"
"endOfLine": "auto"
}
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@testing-library/react": "^11.2.6",
"@testing-library/user-event": "^13.1.5",
"bootstrap": "^4.6.0",
"formik": "^2.2.7",
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
Expand All @@ -25,7 +26,9 @@
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"sass": "^1.32.11",
"web-vitals": "^1.1.1"
"uuid": "^8.3.2",
"web-vitals": "^1.1.1",
"yup": "^0.32.9"
},
"devDependencies": {
"@babel/core": "^7.13.16",
Expand Down
Binary file added public/img/enter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/fail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/moon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/sun.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
227 changes: 216 additions & 11 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,220 @@
import React from "react";

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

import Home from "./pages/Home";
import Completed from "./pages/Completed";
import Active from "./pages/Active";

import Main from "./components/Main";
import Header from "./components/Header";
import NewTask from "./components/NewTask";
import ListFooter from "./components/ListFooter";

import * as api from "./api";

const LOCAL_STORAGE_KEY = "react-tasks-state";

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

if (!prevItems) {
return null;
}

try {
return JSON.parse(prevItems);
} catch (error) {
return null;
}
}

class App extends Component {
constructor(props) {
super(props);

this.state = {
tasks: [],
isDark: false,
Copy link
Contributor

Choose a reason for hiding this comment

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

Muy buen uso de la propiedad de estado para indicar el dark mode! Como mejora se podría usar un nombre más descriptivo como isDarkModeActive.

};

this.saveNewTask = this.saveNewTask.bind(this);
this.toggleDarkLightMode = this.toggleDarkLightMode.bind(this);
this.handleDeleteTask = this.handleDeleteTask.bind(this);
this.handleClearCompleted = this.handleClearCompleted.bind(this);
this.handleUpdateTask = this.handleUpdateTask.bind(this);
this.handleToggleEditing = this.handleToggleEditing.bind(this);
this.handleToggleCheck = this.handleToggleCheck.bind(this);
}

componentDidMount() {
const prevItems = loadLocalStorageData();

if (!prevItems) {
api.getTasks().then((data) => {
this.setState({
tasks: data,
});
});
return;
}

this.setState({
tasks: prevItems.tasks,
});
}

componentDidUpdate() {
const { tasks } = this.state;

localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify({ tasks }));
}

handleDeleteTask(event, taskId) {
const { tasks } = this.state;

const updatedTasks = tasks.filter((task) => task.id !== taskId);

this.setState({ tasks: updatedTasks });
}

handleClearCompleted() {
const { tasks } = this.state;

const updatedTasks = tasks.filter((task) => !task.isCompleted);

this.setState({ tasks: updatedTasks });
}
Comment on lines +72 to +86
Copy link
Contributor

Choose a reason for hiding this comment

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

Muy limpio y bien implementado 👏🏻👏🏻


handleToggleCheck(taskId) {
const { tasks } = this.state;

const updatedTasks = tasks.map((task) => {
if (task.id === taskId) {
return {
...task,
isCompleted: !task.isCompleted,
};
}
return task;
});

this.setState({ tasks: updatedTasks });
}

handleToggleEditing(taskId) {
const { tasks } = this.state;

const updatedTasks = tasks.map((task) => {
if (task.id === taskId) {
return {
...task,
isEditing: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

Muy interesante la solución para indicar que el todo está siendo editado actualmente.

};
}

return task;
});

this.setState({ tasks: updatedTasks });
}

handleUpdateTask(data, taskId) {
const { tasks } = this.state;

const updatedTasks = tasks.map((task) => {
if (task.id === taskId && data.title !== task.title) {
return {
...task,
title: data.title,
isEditing: false,
};
}
return {
...task,
isEditing: false,
};
});

this.setState({ tasks: updatedTasks });
}

saveNewTask(newTask) {
this.setState((prevState) => ({
tasks: [newTask, ...prevState.tasks],
}));
}

toggleDarkLightMode() {
const { isDark } = this.state;
if (isDark === true) this.setState({ isDark: false });
else this.setState({ isDark: true });
}
Comment on lines +147 to +151
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 implementado!

Como mejora podríais usar el patrón de functional updates:

this.setState((prevState) => ({
  isDark: !prevState.isDark,
}));


render() {
const { tasks, isDark } = this.state;

return (
<Main>
<div className="list-header">
<Header
toggleDarkLightMode={this.toggleDarkLightMode}
isDark={isDark}
/>
<NewTask saveNewTask={this.saveNewTask} />
</div>
<div className="list-container">
<BrowserRouter>
<Route
path="/"
exact
render={(routeProps) => (
<Home
{...routeProps}
tasks={tasks}
handleDeleteTask={this.handleDeleteTask}
handleUpdateTask={this.handleUpdateTask}
handleToggleEditing={this.handleToggleEditing}
handleToggleCheck={this.handleToggleCheck}
/>
)}
/>
<Route
path="/completed"
exact
render={(routeProps) => (
<Completed
{...routeProps}
tasks={tasks}
handleDeleteTask={this.handleDeleteTask}
handleUpdateTask={this.handleUpdateTask}
handleToggleEditing={this.handleToggleEditing}
handleToggleCheck={this.handleToggleCheck}
/>
)}
/>
<Route
path="/active"
exact
render={(routeProps) => (
<Active
{...routeProps}
tasks={tasks}
handleDeleteTask={this.handleDeleteTask}
handleUpdateTask={this.handleUpdateTask}
handleToggleEditing={this.handleToggleEditing}
handleToggleCheck={this.handleToggleCheck}
/>
)}
/>
<ListFooter
handleClearCompleted={this.handleClearCompleted}
tasksLeft={tasks.filter((task) => !task.isCompleted).length}
/>
</BrowserRouter>
</div>
</section>
</main>
);
</Main>
);
}
}

export default App;
15 changes: 15 additions & 0 deletions src/api/getTasks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import tasks from "../utils/demo-data";

function getTasks(fail = false) {
return new Promise((res, rej) => {
setTimeout(() => {
if (fail) {
rej(new Error("Failed to fetch"));
}

res(tasks);
}, 1000);
});
}

export { getTasks };
1 change: 1 addition & 0 deletions src/api/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./getTasks";
41 changes: 41 additions & 0 deletions src/components/Checkbox/Checkbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { Component } from "react";

import "./Checkbox.scss";

class Checkbox extends Component {
constructor(props) {
super(props);
this.state = {
isChecked: props.defaultChecked,
taskId: props.taskId,
};
Comment on lines +8 to +11
Copy link
Contributor

Choose a reason for hiding this comment

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

No es recomendable inicializar el state basandose props ya que puede generar muchos bugs. Una solución más sencilla es pasar los props de checked o taskId desde el componente padre y en el componente Checkbox usar el spread operador para pasar todos los props.

<input
    type="checkbox"
    onChange={this.toggleChange}
/>

this.toggleChange = this.toggleChange.bind(this);
this.handleToggleCheck = props.handleToggleCheck;
}

toggleChange() {
this.setState((prevState) => ({
isChecked: !prevState.isChecked,
}));
const { taskId } = this.state;
this.handleToggleCheck(taskId);
}

render() {
const { taskId, isChecked } = this.state;
return (
<div className="round-checkbox">
<input
id={taskId}
type="checkbox"
defaultChecked={isChecked}
onChange={this.toggleChange}
/>
{/* eslint-disable-next-line */}
<label htmlFor={taskId} />
</div>
);
}
}

export default Checkbox;
49 changes: 49 additions & 0 deletions src/components/Checkbox/Checkbox.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.round-checkbox {
position: relative;
& input[type="checkbox"]:not(:checked) + label:hover {
border: 4px solid;
border-color: rgb(66, 126, 179);
}
& input[type="checkbox"]:checked + label:hover {
border: 1px solid;
border-color: rgb(20, 71, 116);
}
& label {
background-color: #fff;
border: 1px solid #ccc;
border-radius: 50%;
cursor: pointer;
height: 28px;
left: 0;
position: absolute;
top: 0;
width: 28px;
&:after {
border: 2px solid #fff;
border-top: none;
border-right: none;
content: "";
height: 6px;
left: 7px;
opacity: 0;
position: absolute;
top: 8px;
transform: rotate(-45deg);
width: 12px;
}
}
& input[type="checkbox"] {
visibility: hidden;
}
& input[type="checkbox"]:checked + label {
background: linear-gradient(
-45deg,
rgb(149, 184, 214),
rgb(66, 126, 179),
white
);
}
& input[type="checkbox"]:checked + label:after {
opacity: 1;
}
}
1 change: 1 addition & 0 deletions src/components/Checkbox/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./Checkbox";
Loading