Skip to content
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

Solución de prueba técnica #69

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
2938c39
elements .main and .footer are hidden if there are no tasks
UnUsuarioMas67 Jun 24, 2024
a5c8875
commented out example li elements
UnUsuarioMas67 Jun 24, 2024
77a93f3
feat: allow adding new tasks by typing on the input field
UnUsuarioMas67 Jun 24, 2024
01ca080
refactor: refactor main and footer sections toggle into it's own method
UnUsuarioMas67 Jun 24, 2024
ad4e22a
fix: numpad enter can now be pressed to create new tasks
UnUsuarioMas67 Jun 24, 2024
89fca68
feat: clicking task checkboxes toggles completion status
UnUsuarioMas67 Jun 24, 2024
9b46d7c
feat: enable deleting tasks from the list
UnUsuarioMas67 Jun 24, 2024
a5273e7
feat: allow editing existing tasks
UnUsuarioMas67 Jun 24, 2024
773b5be
rename: updateTodoList to updateTodoListView
UnUsuarioMas67 Jun 24, 2024
8a0a50d
feat: pending task count is now shown at the footer
UnUsuarioMas67 Jun 24, 2024
ac25160
feat: clear-completed button is only shown if there are any completed…
UnUsuarioMas67 Jun 24, 2024
2fe76e0
feat: the list is now saved in LocalStorage
UnUsuarioMas67 Jun 24, 2024
c2cddf1
rename: renamed parameter at handleCheckBoxChange from liElement to t…
UnUsuarioMas67 Jun 24, 2024
5671b73
feat: allow clearing completed tasks
UnUsuarioMas67 Jun 24, 2024
7b33839
feat: allow filtering list by completion status
UnUsuarioMas67 Jun 27, 2024
dd52830
fix: fixed 'All' filter sometimes not being highlighted when applied
UnUsuarioMas67 Jun 27, 2024
4dd754f
refactor: deleted commented out elements
UnUsuarioMas67 Jun 27, 2024
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
24 changes: 0 additions & 24 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,6 @@ <h1>My Day</h1>
<ul class="todo-list">
<!-- These are here just to show the structure of the list items -->
<!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
<li class="completed">
<div class="view">
<input class="toggle" type="checkbox" checked />
<label>Learn JavaScript</label>
<button class="destroy"></button>
</div>
<input class="edit" value="Learn JavaScript" />
</li>
<li>
<div class="view">
<input class="toggle" type="checkbox" />
<label>Buy a unicorn</label>
<button class="destroy"></button>
</div>
<input class="edit" value="Buy a unicorn" />
</li>
<li class="editing">
<div class="view">
<input class="toggle" type="checkbox" />
<label>Make dishes</label>
<button class="destroy"></button>
</div>
<input class="edit" value="Make dishes" />
</li>
</ul>
</section>
<!-- This footer should be hidden by default and shown when there are todos -->
Expand Down
237 changes: 235 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,238 @@
import "./css/base.css";

import { sayHello } from "./js/utils";
// DOM elements
const mainElement = document.querySelector(".main");
const footerElement = document.querySelector(".footer");
const todoInput = document.querySelector(".new-todo");
const todoList = document.querySelector(".todo-list");
const todoCount = document.querySelector(".todo-count");
const clearCompletedButton = document.querySelector(".clear-completed");
const filterAnchors = document.querySelectorAll(".filters a");

console.log(sayHello("Hello"));
let tasks = getTasksFromLocalStorage();

window.onload = () => {
updateTodoListView();
toggleMainAndFooterView();
};

window.onpopstate = () => {
updateTodoListView();
};

todoInput.onkeypress = (e) => {
if (e.key !== "Enter") {
return;
}

addTask(e.target.value);
e.target.value = "";
};

filterAnchors.forEach((element) => {
element.onclick = handleFilterClick;
});

clearCompletedButton.onclick = clearCompletedTasks;

function getTasksFromLocalStorage() {
const tasks = JSON.parse(localStorage.getItem("mydayapp-js"));
return Array.isArray(tasks) ? tasks : [];
}

function saveTasksToLocalStorage() {
localStorage.setItem("mydayapp-js", JSON.stringify(tasks));
updateTodoListView();
}

function toggleMainAndFooterView() {
// hides the main and footer sections if the list is empty
const tasksEmpty = tasks === null || tasks.length === 0;
mainElement.hidden = tasksEmpty;
footerElement.hidden = tasksEmpty;
}

function addTask(task) {
// make sure the string isn't empty
if (task.trim().length === 0) {
return;
}

tasks.push({
id: tasks.length + 1,
title: task.trim(),
completed: false,
});

saveTasksToLocalStorage();
}

function removeTask(task) {
let taskIndex = tasks.findIndex((t) => t.id === task.id);
tasks.splice(taskIndex, 1);
saveTasksToLocalStorage();
}

function updateTodoListView() {
// clear todo-list element
todoList.textContent = ``;

// get filter from route
const filter = window.location.href.substring(
window.location.href.lastIndexOf("/") + 1
);

let filteredTasks = tasks;
if (filter === "pending") {
filteredTasks = tasks.filter((task) => !task.completed);
} else if (filter === "completed") {
filteredTasks = tasks.filter((task) => task.completed);
}

filteredTasks.forEach((task) => {
// EXAMPLE
//
// <li data-task-id="1">
// <div class="view">
// <input class="toggle" type="checkbox" />
// <label>Buy a unicorn</label>
// <button class="destroy"></button>
// </div>
// <input class="edit" value="Buy a unicorn" />
// </li>

const li = document.createElement("li");
li.dataset.taskId = task.id;
if (task.completed) {
li.classList.add("completed");
}

const div = document.createElement("div");
div.classList.add("view");

const checkBox = document.createElement("input");
checkBox.type = "checkbox";
checkBox.checked = task.completed;
checkBox.onchange = () => handleCheckBoxChange(checkBox, li, task);
checkBox.classList.add("toggle");

const label = document.createElement("label");
label.textContent = task.title;
label.ondblclick = () => enterEditing(li);

const button = document.createElement("button");
button.onclick = () => removeTask(task);
button.classList.add("destroy");

div.appendChild(checkBox);
div.appendChild(label);
div.appendChild(button);

const editInput = document.createElement("input");
editInput.value = task.title;
editInput.onkeydown = (e) => exitEditing(e, li, task);
editInput.classList.add("edit");

li.appendChild(div);
li.appendChild(editInput);

todoList.appendChild(li);
});

updateFilterList(filter);
toggleMainAndFooterView();
updatePendingCount();
}

function updateFilterList(filterName) {
const compareText =
filterName === "pending" || filterName === "completed" ? filterName : "";

filterAnchors.forEach((element) => {
const elementFilter = element.href.substring(
element.href.lastIndexOf("/") + 1
);

if (elementFilter === compareText) {
element.classList.add("selected");
return;
}

element.classList.remove("selected");
});
}

function updatePendingCount() {
clearCompletedButton.hidden = true;

let pendingTasks = 0;
for (let task of tasks) {
if (task.completed === false) {
pendingTasks++;
continue;
}

// enable clear-completed button if there is at least one completed task
clearCompletedButton.hidden = false;
}

todoCount.textContent = "";

const strongNumber = document.createElement("strong");
strongNumber.textContent = pendingTasks;
const textNode = document.createTextNode(
pendingTasks === 1 ? " item left" : " items left"
);

todoCount.appendChild(strongNumber);
todoCount.appendChild(textNode);
}

function handleCheckBoxChange(checkBox, taskElement, task) {
if (checkBox.checked) {
taskElement.classList.add("completed");
} else {
taskElement.classList.remove("completed");
}

task.completed = checkBox.checked;
saveTasksToLocalStorage();
}

function enterEditing(taskElement) {
taskElement.classList.add("editing");
const editInput = taskElement.querySelector(".edit");
editInput.focus();
}

function exitEditing(event, taskElement, task) {
const label = taskElement.querySelector("label");

switch (event.key) {
case "Enter":
// save changes
label.textContent = event.target.value.trim();
task.title = event.target.value.trim();
saveTasksToLocalStorage();
break;
case "Escape":
// discard changes
event.target.value = task.title;
break;
default:
return;
}

taskElement.classList.remove("editing");
}

function clearCompletedTasks() {
tasks = tasks.filter((task) => !task.completed);
saveTasksToLocalStorage();
}

function handleFilterClick(event) {
event.preventDefault();
window.history.pushState({}, "", event.target.href);
updateTodoListView();
}