fix(curriculum): correct form button text for todo app (#54594)

Co-authored-by: Deep <deep.dhanuka1@myntra.com>
Co-authored-by: Naomi <nhcarrigan@gmail.com>
This commit is contained in:
Deep Dhanuka
2024-05-03 01:58:55 +05:30
committed by GitHub
parent 7090307158
commit 9d6c5ebde1
16 changed files with 815 additions and 818 deletions
@@ -216,61 +216,61 @@
"title": "Step 51"
},
{
"id": "659ebe52d74b132a1d75c891",
"id": "64fb1c4dc0feb219149a7c7d",
"title": "Step 52"
},
{
"id": "64fb1c4dc0feb219149a7c7d",
"id": "64fb285637fa1e1c222033e3",
"title": "Step 53"
},
{
"id": "64fb285637fa1e1c222033e3",
"id": "64fb29348a60361ccd45c1e2",
"title": "Step 54"
},
{
"id": "64fb29348a60361ccd45c1e2",
"id": "64fefebad99209211ec30537",
"title": "Step 55"
},
{
"id": "64fefebad99209211ec30537",
"id": "64ff0313700dad264d19dfe4",
"title": "Step 56"
},
{
"id": "64ff0313700dad264d19dfe4",
"id": "64ff04cc33779427a6412449",
"title": "Step 57"
},
{
"id": "64ff04cc33779427a6412449",
"id": "64ff068e0426eb288874ed79",
"title": "Step 58"
},
{
"id": "64ff068e0426eb288874ed79",
"id": "64ff23daf176a92de95f24dc",
"title": "Step 59"
},
{
"id": "64ff23daf176a92de95f24dc",
"id": "64ff24b80431f62ec6b93f65",
"title": "Step 60"
},
{
"id": "64ff24b80431f62ec6b93f65",
"id": "65003986d17d1e1865b269c0",
"title": "Step 61"
},
{
"id": "65003986d17d1e1865b269c0",
"id": "650046832f92c01a35834bca",
"title": "Step 62"
},
{
"id": "650046832f92c01a35834bca",
"id": "650048b0764f9c1b798200e2",
"title": "Step 63"
},
{
"id": "650048b0764f9c1b798200e2",
"id": "65004ba581d03d1d5628b41c",
"title": "Step 64"
},
{
"id": "65004ba581d03d1d5628b41c",
"id": "6632420f81f3cc554a5e540b",
"title": "Step 65"
}
],
"helpCategory": "JavaScript"
}
}
@@ -1,8 +1,8 @@
---
id: 64fb1c4dc0feb219149a7c7d
title: Step 53
title: Step 52
challengeType: 0
dashedName: step-53
dashedName: step-52
---
# --description--
@@ -307,7 +307,6 @@ const taskData = [];
let currentTask = {};
const addOrUpdateTask = () => {
addOrUpdateTaskBtn.innerText = "Add Task";
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
@@ -1,8 +1,8 @@
---
id: 64fb285637fa1e1c222033e3
title: Step 54
title: Step 53
challengeType: 0
dashedName: step-54
dashedName: step-53
---
# --description--
@@ -288,7 +288,6 @@ const taskData = [];
let currentTask = {};
const addOrUpdateTask = () => {
addOrUpdateTaskBtn.innerText = "Add Task";
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
@@ -1,8 +1,8 @@
---
id: 64fb29348a60361ccd45c1e2
title: Step 55
title: Step 54
challengeType: 0
dashedName: step-55
dashedName: step-54
---
# --description--
@@ -304,7 +304,6 @@ const taskData = [];
let currentTask = {};
const addOrUpdateTask = () => {
addOrUpdateTaskBtn.innerText = "Add Task";
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
@@ -1,8 +1,8 @@
---
id: 64fefebad99209211ec30537
title: Step 56
title: Step 55
challengeType: 0
dashedName: step-56
dashedName: step-55
---
# --description--
@@ -294,7 +294,6 @@ const taskData = [];
let currentTask = {};
const addOrUpdateTask = () => {
addOrUpdateTaskBtn.innerText = "Add Task";
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
@@ -1,8 +1,8 @@
---
id: 64ff0313700dad264d19dfe4
title: Step 57
title: Step 56
challengeType: 0
dashedName: step-57
dashedName: step-56
---
# --description--
@@ -294,7 +294,6 @@ const taskData = [];
let currentTask = {};
const addOrUpdateTask = () => {
addOrUpdateTaskBtn.innerText = "Add Task";
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
@@ -1,8 +1,8 @@
---
id: 64ff04cc33779427a6412449
title: Step 58
title: Step 57
challengeType: 0
dashedName: step-58
dashedName: step-57
---
# --description--
@@ -296,7 +296,6 @@ const taskData = [];
let currentTask = {};
const addOrUpdateTask = () => {
addOrUpdateTaskBtn.innerText = "Add Task";
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
@@ -1,8 +1,8 @@
---
id: 64ff068e0426eb288874ed79
title: Step 59
title: Step 58
challengeType: 0
dashedName: step-59
dashedName: step-58
---
# --description--
@@ -288,7 +288,6 @@ const taskData = [];
let currentTask = {};
const addOrUpdateTask = () => {
addOrUpdateTaskBtn.innerText = "Add Task";
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
@@ -1,8 +1,8 @@
---
id: 64ff23daf176a92de95f24dc
title: Step 60
title: Step 59
challengeType: 0
dashedName: step-60
dashedName: step-59
---
# --description--
@@ -294,7 +294,6 @@ const taskData = [];
let currentTask = {};
const addOrUpdateTask = () => {
addOrUpdateTaskBtn.innerText = "Add Task";
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
@@ -1,8 +1,8 @@
---
id: 64ff24b80431f62ec6b93f65
title: Step 61
title: Step 60
challengeType: 0
dashedName: step-61
dashedName: step-60
---
# --description--
@@ -286,7 +286,6 @@ const taskData = [];
let currentTask = {};
const addOrUpdateTask = () => {
addOrUpdateTaskBtn.innerText = "Add Task";
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
@@ -1,8 +1,8 @@
---
id: 65003986d17d1e1865b269c0
title: Step 62
title: Step 61
challengeType: 0
dashedName: step-62
dashedName: step-61
---
# --description--
@@ -302,7 +302,6 @@ const taskData = [];
let currentTask = {};
const addOrUpdateTask = () => {
addOrUpdateTaskBtn.innerText = "Add Task";
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
@@ -1,8 +1,8 @@
---
id: 650046832f92c01a35834bca
title: Step 63
title: Step 62
challengeType: 0
dashedName: step-63
dashedName: step-62
---
# --description--
@@ -303,7 +303,6 @@ const taskData = [];
let currentTask = {};
const addOrUpdateTask = () => {
addOrUpdateTaskBtn.innerText = "Add Task";
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
@@ -1,8 +1,8 @@
---
id: 650048b0764f9c1b798200e2
title: Step 64
title: Step 63
challengeType: 0
dashedName: step-64
dashedName: step-63
---
# --description--
@@ -290,7 +290,6 @@ const taskData = [];
let currentTask = {};
const addOrUpdateTask = () => {
addOrUpdateTaskBtn.innerText = "Add Task";
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
@@ -1,8 +1,8 @@
---
id: 65004ba581d03d1d5628b41c
title: Step 65
title: Step 64
challengeType: 0
dashedName: step-65
dashedName: step-64
---
# --description--
@@ -23,8 +23,6 @@ In that example, if `arr` has a length greater than `0`, the code inside the `if
Check if there's a task inside `taskData`, then call the `updateTaskContainer()` inside the `if` statement block.
With that, you've completed this project.
# --hints--
You should create an `if` statement with `(taskData.length)` as the condition. As a reminder, `0` is a falsy value.
@@ -308,7 +306,6 @@ const taskData = JSON.parse(localStorage.getItem("data")) || [];
let currentTask = {};
const addOrUpdateTask = () => {
addOrUpdateTaskBtn.innerText = "Add Task";
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
@@ -413,377 +410,3 @@ taskForm.addEventListener("submit", (e) => {
addOrUpdateTask();
});
```
# --solutions--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Learn localStorage by Building a Todo App</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<main>
<h1>Todo App</h1>
<div class="todo-app">
<button id="open-task-form-btn" class="btn large-btn">
Add New Task
</button>
<form class="task-form hidden" id="task-form">
<div class="task-form-header">
<button id="close-task-form-btn" class="close-task-form-btn" type="button" aria-label="close">
<svg class="close-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px"><path fill="#F44336" d="M21.5 4.5H26.501V43.5H21.5z" transform="rotate(45.001 24 24)" /><path fill="#F44336" d="M21.5 4.5H26.5V43.501H21.5z" transform="rotate(135.008 24 24)" /></svg>
</button>
</div>
<div class="task-form-body">
<label class="task-form-label" for="title-input">Title</label>
<input required type="text" class="form-control" id="title-input" value="" />
<label class="task-form-label" for="date-input">Date</label>
<input type="date" class="form-control" id="date-input" value="" />
<label class="task-form-label" for="description-input">Description</label>
<textarea class="form-control" id="description-input" cols="30" rows="5"></textarea>
</div>
<div class="task-form-footer">
<button id="add-or-update-task-btn" class="btn large-btn" type="submit">
Add Task
</button>
</div>
</form>
<dialog id="confirm-close-dialog">
<form method="dialog">
<p class="discard-message-text">Discard unsaved changes?</p>
<div class="confirm-close-dialog-btn-container">
<button id="cancel-btn" class="btn">
Cancel
</button>
<button id="discard-btn" class="btn">
Discard
</button>
</div>
</form>
</dialog>
<div id="tasks-container"></div>
</div>
</main>
<script src="script.js"></script>
</body>
</html>
```
```css
:root {
--white: #fff;
--light-grey: #f5f6f7;
--dark-grey: #0a0a23;
--yellow: #f1be32;
--golden-yellow: #feac32;
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: var(--dark-grey);
}
main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
h1 {
color: var(--light-grey);
margin: 20px 0 40px 0;
}
.todo-app {
background-color: var(--white);
width: 300px;
height: 350px;
border: 5px solid var(--yellow);
border-radius: 8px;
padding: 15px;
position: relative;
display: flex;
flex-direction: column;
gap: 10px;
}
.btn {
cursor: pointer;
width: 100px;
margin: 10px;
color: var(--dark-grey);
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
.btn:hover {
background-image: linear-gradient(#ffcc4c, #f89808);
}
.large-btn {
width: 80%;
font-size: 1.2rem;
align-self: center;
justify-self: center;
}
.close-task-form-btn {
background: none;
border: none;
cursor: pointer;
}
.close-icon {
width: 20px;
height: 20px;
}
.task-form {
display: flex;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: var(--white);
border-radius: 5px;
padding: 15px;
width: 300px;
height: 350px;
flex-direction: column;
justify-content: space-between;
overflow: auto;
}
.task-form-header {
display: flex;
justify-content: flex-end;
}
.task-form-body {
display: flex;
flex-direction: column;
justify-content: space-around;
}
.task-form-footer {
display: flex;
justify-content: center;
}
.task-form-label,
#title-input,
#date-input,
#description-input {
display: block;
}
.task-form-label {
margin-bottom: 5px;
font-size: 1.3rem;
font-weight: bold;
}
#title-input,
#date-input,
#description-input {
width: 100%;
margin-bottom: 10px;
padding: 2px;
}
#confirm-close-dialog {
padding: 10px;
margin: 10px auto;
border-radius: 15px;
}
.confirm-close-dialog-btn-container {
display: flex;
justify-content: center;
margin-top: 10px;
}
.discard-message-text {
font-weight: bold;
font-size: 1.5rem;
}
#tasks-container {
height: 100%;
overflow-y: auto;
}
.task {
margin: 5px 0;
}
.hidden {
display: none;
}
@media (min-width: 576px) {
.todo-app,
.task-form {
width: 400px;
height: 450px;
}
.task-form-label {
font-size: 1.5rem;
}
#title-input,
#date-input {
height: 2rem;
}
#title-input,
#date-input,
#description-input {
padding: 5px;
margin-bottom: 20px;
}
}
```
```js
const taskForm = document.getElementById("task-form");
const confirmCloseDialog = document.getElementById("confirm-close-dialog");
const openTaskFormBtn = document.getElementById("open-task-form-btn");
const closeTaskFormBtn = document.getElementById("close-task-form-btn");
const addOrUpdateTaskBtn = document.getElementById("add-or-update-task-btn");
const cancelBtn = document.getElementById("cancel-btn");
const discardBtn = document.getElementById("discard-btn");
const tasksContainer = document.getElementById("tasks-container");
const titleInput = document.getElementById("title-input");
const dateInput = document.getElementById("date-input");
const descriptionInput = document.getElementById("description-input");
const taskData = JSON.parse(localStorage.getItem("data")) || [];
let currentTask = {};
const addOrUpdateTask = () => {
addOrUpdateTaskBtn.innerText = "Add Task";
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
title: titleInput.value,
date: dateInput.value,
description: descriptionInput.value,
};
if (dataArrIndex === -1) {
taskData.unshift(taskObj);
} else {
taskData[dataArrIndex] = taskObj;
}
localStorage.setItem("data", JSON.stringify(taskData));
updateTaskContainer()
reset()
};
const updateTaskContainer = () => {
tasksContainer.innerHTML = "";
taskData.forEach(
({ id, title, date, description }) => {
(tasksContainer.innerHTML += `
<div class="task" id="${id}">
<p><strong>Title:</strong> ${title}</p>
<p><strong>Date:</strong> ${date}</p>
<p><strong>Description:</strong> ${description}</p>
<button onclick="editTask(this)" type="button" class="btn">Edit</button>
<button onclick="deleteTask(this)" type="button" class="btn">Delete</button>
</div>
`)
}
);
};
const deleteTask = (buttonEl) => {
const dataArrIndex = taskData.findIndex(
(item) => item.id === buttonEl.parentElement.id
);
buttonEl.parentElement.remove();
taskData.splice(dataArrIndex, 1);
localStorage.setItem("data", JSON.stringify(taskData));
}
const editTask = (buttonEl) => {
const dataArrIndex = taskData.findIndex(
(item) => item.id === buttonEl.parentElement.id
);
currentTask = taskData[dataArrIndex];
titleInput.value = currentTask.title;
dateInput.value = currentTask.date;
descriptionInput.value = currentTask.description;
addOrUpdateTaskBtn.innerText = "Update Task";
taskForm.classList.toggle("hidden");
}
const reset = () => {
titleInput.value = "";
dateInput.value = "";
descriptionInput.value = "";
taskForm.classList.toggle("hidden");
currentTask = {};
}
if (taskData.length) {
updateTaskContainer();
}
openTaskFormBtn.addEventListener("click", () =>
taskForm.classList.toggle("hidden")
);
closeTaskFormBtn.addEventListener("click", () => {
const formInputsContainValues = titleInput.value || dateInput.value || descriptionInput.value;
const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description;
if (formInputsContainValues && formInputValuesUpdated) {
confirmCloseDialog.showModal();
} else {
reset();
}
});
cancelBtn.addEventListener("click", () => confirmCloseDialog.close());
discardBtn.addEventListener("click", () => {
confirmCloseDialog.close();
reset()
});
taskForm.addEventListener("submit", (e) => {
e.preventDefault();
addOrUpdateTask();
});
```
@@ -1,388 +0,0 @@
---
id: 659ebe52d74b132a1d75c891
title: Step 52
challengeType: 0
dashedName: step-52
---
# --description--
If you try to add a new task, edit that task, and then click on the `Add New Task` button, you will notice a bug.
The form button will display the incorrect text of `"Update Task"` instead of `"Add Task"`. To fix this, you will need to assign the string `"Add Task"` to `addOrUpdateTaskBtn.innerText` inside your `addOrUpdateTask` function.
# --hints--
You should assign the string `"Add Task"` to `addOrUpdateTaskBtn.innerText`.
```js
assert.match(code, /addOrUpdateTaskBtn\.innerText\s*=\s*('|")Add Task\1\s*/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Learn localStorage by Building a Todo App</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<main>
<h1>Todo App</h1>
<div class="todo-app">
<button id="open-task-form-btn" class="btn large-btn">
Add New Task
</button>
<form class="task-form hidden" id="task-form">
<div class="task-form-header">
<button id="close-task-form-btn" class="close-task-form-btn" type="button" aria-label="close">
<svg class="close-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px"><path fill="#F44336" d="M21.5 4.5H26.501V43.5H21.5z" transform="rotate(45.001 24 24)" /><path fill="#F44336" d="M21.5 4.5H26.5V43.501H21.5z" transform="rotate(135.008 24 24)" /></svg>
</button>
</div>
<div class="task-form-body">
<label class="task-form-label" for="title-input">Title</label>
<input required type="text" class="form-control" id="title-input" value="" />
<label class="task-form-label" for="date-input">Date</label>
<input type="date" class="form-control" id="date-input" value="" />
<label class="task-form-label" for="description-input">Description</label>
<textarea class="form-control" id="description-input" cols="30" rows="5"></textarea>
</div>
<div class="task-form-footer">
<button id="add-or-update-task-btn" class="btn large-btn" type="submit">
Add Task
</button>
</div>
</form>
<dialog id="confirm-close-dialog">
<form method="dialog">
<p class="discard-message-text">Discard unsaved changes?</p>
<div class="confirm-close-dialog-btn-container">
<button id="cancel-btn" class="btn">
Cancel
</button>
<button id="discard-btn" class="btn">
Discard
</button>
</div>
</form>
</dialog>
<div id="tasks-container"></div>
</div>
</main>
<script src="script.js"></script>
</body>
</html>
```
```css
:root {
--white: #fff;
--light-grey: #f5f6f7;
--dark-grey: #0a0a23;
--yellow: #f1be32;
--golden-yellow: #feac32;
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: var(--dark-grey);
}
main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
h1 {
color: var(--light-grey);
margin: 20px 0 40px 0;
}
.todo-app {
background-color: var(--white);
width: 300px;
height: 350px;
border: 5px solid var(--yellow);
border-radius: 8px;
padding: 15px;
position: relative;
display: flex;
flex-direction: column;
gap: 10px;
}
.btn {
cursor: pointer;
width: 100px;
margin: 10px;
color: var(--dark-grey);
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
.btn:hover {
background-image: linear-gradient(#ffcc4c, #f89808);
}
.large-btn {
width: 80%;
font-size: 1.2rem;
align-self: center;
justify-self: center;
}
.close-task-form-btn {
background: none;
border: none;
cursor: pointer;
}
.close-icon {
width: 20px;
height: 20px;
}
.task-form {
display: flex;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: var(--white);
border-radius: 5px;
padding: 15px;
width: 300px;
height: 350px;
flex-direction: column;
justify-content: space-between;
overflow: auto;
}
.task-form-header {
display: flex;
justify-content: flex-end;
}
.task-form-body {
display: flex;
flex-direction: column;
justify-content: space-around;
}
.task-form-footer {
display: flex;
justify-content: center;
}
.task-form-label,
#title-input,
#date-input,
#description-input {
display: block;
}
.task-form-label {
margin-bottom: 5px;
font-size: 1.3rem;
font-weight: bold;
}
#title-input,
#date-input,
#description-input {
width: 100%;
margin-bottom: 10px;
padding: 2px;
}
#confirm-close-dialog {
padding: 10px;
margin: 10px auto;
border-radius: 15px;
}
.confirm-close-dialog-btn-container {
display: flex;
justify-content: center;
margin-top: 10px;
}
.discard-message-text {
font-weight: bold;
font-size: 1.5rem;
}
#tasks-container {
height: 100%;
overflow-y: auto;
}
.task {
margin: 5px 0;
}
.hidden {
display: none;
}
@media (min-width: 576px) {
.todo-app,
.task-form {
width: 400px;
height: 450px;
}
.task-form-label {
font-size: 1.5rem;
}
#title-input,
#date-input {
height: 2rem;
}
#title-input,
#date-input,
#description-input {
padding: 5px;
margin-bottom: 20px;
}
}
```
```js
const taskForm = document.getElementById("task-form");
const confirmCloseDialog = document.getElementById("confirm-close-dialog");
const openTaskFormBtn = document.getElementById("open-task-form-btn");
const closeTaskFormBtn = document.getElementById("close-task-form-btn");
const addOrUpdateTaskBtn = document.getElementById("add-or-update-task-btn");
const cancelBtn = document.getElementById("cancel-btn");
const discardBtn = document.getElementById("discard-btn");
const tasksContainer = document.getElementById("tasks-container");
const titleInput = document.getElementById("title-input");
const dateInput = document.getElementById("date-input");
const descriptionInput = document.getElementById("description-input");
const taskData = [];
let currentTask = {};
const addOrUpdateTask = () => {
--fcc-editable-region--
--fcc-editable-region--
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
title: titleInput.value,
date: dateInput.value,
description: descriptionInput.value,
};
if (dataArrIndex === -1) {
taskData.unshift(taskObj);
} else {
taskData[dataArrIndex] = taskObj;
}
updateTaskContainer()
reset()
};
const updateTaskContainer = () => {
tasksContainer.innerHTML = "";
taskData.forEach(
({ id, title, date, description }) => {
(tasksContainer.innerHTML += `
<div class="task" id="${id}">
<p><strong>Title:</strong> ${title}</p>
<p><strong>Date:</strong> ${date}</p>
<p><strong>Description:</strong> ${description}</p>
<button onclick="editTask(this)" type="button" class="btn">Edit</button>
<button onclick="deleteTask(this)" type="button" class="btn">Delete</button>
</div>
`)
}
);
};
const deleteTask = (buttonEl) => {
const dataArrIndex = taskData.findIndex(
(item) => item.id === buttonEl.parentElement.id
);
buttonEl.parentElement.remove();
taskData.splice(dataArrIndex, 1);
}
const editTask = (buttonEl) => {
const dataArrIndex = taskData.findIndex(
(item) => item.id === buttonEl.parentElement.id
);
currentTask = taskData[dataArrIndex];
titleInput.value = currentTask.title;
dateInput.value = currentTask.date;
descriptionInput.value = currentTask.description;
addOrUpdateTaskBtn.innerText = "Update Task";
taskForm.classList.toggle("hidden");
}
const reset = () => {
titleInput.value = "";
dateInput.value = "";
descriptionInput.value = "";
taskForm.classList.toggle("hidden");
currentTask = {};
}
openTaskFormBtn.addEventListener("click", () =>
taskForm.classList.toggle("hidden")
);
closeTaskFormBtn.addEventListener("click", () => {
const formInputsContainValues = titleInput.value || dateInput.value || descriptionInput.value;
if (formInputsContainValues) {
confirmCloseDialog.showModal();
} else {
reset();
}
});
cancelBtn.addEventListener("click", () => confirmCloseDialog.close());
discardBtn.addEventListener("click", () => {
confirmCloseDialog.close();
reset()
});
taskForm.addEventListener("submit", (e) => {
e.preventDefault();
addOrUpdateTask();
});
```
@@ -0,0 +1,774 @@
---
id: 6632420f81f3cc554a5e540b
title: Step 65
challengeType: 0
dashedName: step-65
---
# --description--
If you try to add a new task, edit that task, and then click on the `Add New Task` button, you will notice a bug.
The form button will display the incorrect text of `"Update Task"` instead of `"Add Task"`. To fix this, you will need to assign the string `"Add Task"` to `addOrUpdateTaskBtn.innerText` inside your `reset` function.
With that, you've completed this project.
# --hints--
You should assign the string `"Add Task"` to `addOrUpdateTaskBtn.innerText`.
```js
assert.match(code, /addOrUpdateTaskBtn\.innerText\s*=\s*('|")Add Task\1\s*/)
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Learn localStorage by Building a Todo App</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<main>
<h1>Todo App</h1>
<div class="todo-app">
<button id="open-task-form-btn" class="btn large-btn">
Add New Task
</button>
<form class="task-form hidden" id="task-form">
<div class="task-form-header">
<button id="close-task-form-btn" class="close-task-form-btn" type="button" aria-label="close">
<svg class="close-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px"><path fill="#F44336" d="M21.5 4.5H26.501V43.5H21.5z" transform="rotate(45.001 24 24)" /><path fill="#F44336" d="M21.5 4.5H26.5V43.501H21.5z" transform="rotate(135.008 24 24)" /></svg>
</button>
</div>
<div class="task-form-body">
<label class="task-form-label" for="title-input">Title</label>
<input required type="text" class="form-control" id="title-input" value="" />
<label class="task-form-label" for="date-input">Date</label>
<input type="date" class="form-control" id="date-input" value="" />
<label class="task-form-label" for="description-input">Description</label>
<textarea class="form-control" id="description-input" cols="30" rows="5"></textarea>
</div>
<div class="task-form-footer">
<button id="add-or-update-task-btn" class="btn large-btn" type="submit">
Add Task
</button>
</div>
</form>
<dialog id="confirm-close-dialog">
<form method="dialog">
<p class="discard-message-text">Discard unsaved changes?</p>
<div class="confirm-close-dialog-btn-container">
<button id="cancel-btn" class="btn">
Cancel
</button>
<button id="discard-btn" class="btn">
Discard
</button>
</div>
</form>
</dialog>
<div id="tasks-container"></div>
</div>
</main>
<script src="script.js"></script>
</body>
</html>
```
```css
:root {
--white: #fff;
--light-grey: #f5f6f7;
--dark-grey: #0a0a23;
--yellow: #f1be32;
--golden-yellow: #feac32;
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: var(--dark-grey);
}
main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
h1 {
color: var(--light-grey);
margin: 20px 0 40px 0;
}
.todo-app {
background-color: var(--white);
width: 300px;
height: 350px;
border: 5px solid var(--yellow);
border-radius: 8px;
padding: 15px;
position: relative;
display: flex;
flex-direction: column;
gap: 10px;
}
.btn {
cursor: pointer;
width: 100px;
margin: 10px;
color: var(--dark-grey);
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
.btn:hover {
background-image: linear-gradient(#ffcc4c, #f89808);
}
.large-btn {
width: 80%;
font-size: 1.2rem;
align-self: center;
justify-self: center;
}
.close-task-form-btn {
background: none;
border: none;
cursor: pointer;
}
.close-icon {
width: 20px;
height: 20px;
}
.task-form {
display: flex;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: var(--white);
border-radius: 5px;
padding: 15px;
width: 300px;
height: 350px;
flex-direction: column;
justify-content: space-between;
overflow: auto;
}
.task-form-header {
display: flex;
justify-content: flex-end;
}
.task-form-body {
display: flex;
flex-direction: column;
justify-content: space-around;
}
.task-form-footer {
display: flex;
justify-content: center;
}
.task-form-label,
#title-input,
#date-input,
#description-input {
display: block;
}
.task-form-label {
margin-bottom: 5px;
font-size: 1.3rem;
font-weight: bold;
}
#title-input,
#date-input,
#description-input {
width: 100%;
margin-bottom: 10px;
padding: 2px;
}
#confirm-close-dialog {
padding: 10px;
margin: 10px auto;
border-radius: 15px;
}
.confirm-close-dialog-btn-container {
display: flex;
justify-content: center;
margin-top: 10px;
}
.discard-message-text {
font-weight: bold;
font-size: 1.5rem;
}
#tasks-container {
height: 100%;
overflow-y: auto;
}
.task {
margin: 5px 0;
}
.hidden {
display: none;
}
@media (min-width: 576px) {
.todo-app,
.task-form {
width: 400px;
height: 450px;
}
.task-form-label {
font-size: 1.5rem;
}
#title-input,
#date-input {
height: 2rem;
}
#title-input,
#date-input,
#description-input {
padding: 5px;
margin-bottom: 20px;
}
}
```
```js
const taskForm = document.getElementById("task-form");
const confirmCloseDialog = document.getElementById("confirm-close-dialog");
const openTaskFormBtn = document.getElementById("open-task-form-btn");
const closeTaskFormBtn = document.getElementById("close-task-form-btn");
const addOrUpdateTaskBtn = document.getElementById("add-or-update-task-btn");
const cancelBtn = document.getElementById("cancel-btn");
const discardBtn = document.getElementById("discard-btn");
const tasksContainer = document.getElementById("tasks-container");
const titleInput = document.getElementById("title-input");
const dateInput = document.getElementById("date-input");
const descriptionInput = document.getElementById("description-input");
const taskData = JSON.parse(localStorage.getItem("data")) || [];
let currentTask = {};
const addOrUpdateTask = () => {
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
title: titleInput.value,
date: dateInput.value,
description: descriptionInput.value,
};
if (dataArrIndex === -1) {
taskData.unshift(taskObj);
} else {
taskData[dataArrIndex] = taskObj;
}
localStorage.setItem("data", JSON.stringify(taskData));
updateTaskContainer()
reset()
};
const updateTaskContainer = () => {
tasksContainer.innerHTML = "";
taskData.forEach(
({ id, title, date, description }) => {
(tasksContainer.innerHTML += `
<div class="task" id="${id}">
<p><strong>Title:</strong> ${title}</p>
<p><strong>Date:</strong> ${date}</p>
<p><strong>Description:</strong> ${description}</p>
<button onclick="editTask(this)" type="button" class="btn">Edit</button>
<button onclick="deleteTask(this)" type="button" class="btn">Delete</button>
</div>
`)
}
);
};
const deleteTask = (buttonEl) => {
const dataArrIndex = taskData.findIndex(
(item) => item.id === buttonEl.parentElement.id
);
buttonEl.parentElement.remove();
taskData.splice(dataArrIndex, 1);
localStorage.setItem("data", JSON.stringify(taskData));
}
const editTask = (buttonEl) => {
const dataArrIndex = taskData.findIndex(
(item) => item.id === buttonEl.parentElement.id
);
currentTask = taskData[dataArrIndex];
titleInput.value = currentTask.title;
dateInput.value = currentTask.date;
descriptionInput.value = currentTask.description;
addOrUpdateTaskBtn.innerText = "Update Task";
taskForm.classList.toggle("hidden");
}
const reset = () => {
--fcc-editable-region--
--fcc-editable-region--
titleInput.value = "";
dateInput.value = "";
descriptionInput.value = "";
taskForm.classList.toggle("hidden");
currentTask = {};
}
if (taskData.length) {
updateTaskContainer();
}
openTaskFormBtn.addEventListener("click", () =>
taskForm.classList.toggle("hidden")
);
closeTaskFormBtn.addEventListener("click", () => {
const formInputsContainValues = titleInput.value || dateInput.value || descriptionInput.value;
const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description;
if (formInputsContainValues && formInputValuesUpdated) {
confirmCloseDialog.showModal();
} else {
reset();
}
});
cancelBtn.addEventListener("click", () => confirmCloseDialog.close());
discardBtn.addEventListener("click", () => {
confirmCloseDialog.close();
reset()
});
taskForm.addEventListener("submit", (e) => {
e.preventDefault();
addOrUpdateTask();
});
```
# --solutions--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Learn localStorage by Building a Todo App</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<main>
<h1>Todo App</h1>
<div class="todo-app">
<button id="open-task-form-btn" class="btn large-btn">
Add New Task
</button>
<form class="task-form hidden" id="task-form">
<div class="task-form-header">
<button id="close-task-form-btn" class="close-task-form-btn" type="button" aria-label="close">
<svg class="close-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px"><path fill="#F44336" d="M21.5 4.5H26.501V43.5H21.5z" transform="rotate(45.001 24 24)" /><path fill="#F44336" d="M21.5 4.5H26.5V43.501H21.5z" transform="rotate(135.008 24 24)" /></svg>
</button>
</div>
<div class="task-form-body">
<label class="task-form-label" for="title-input">Title</label>
<input required type="text" class="form-control" id="title-input" value="" />
<label class="task-form-label" for="date-input">Date</label>
<input type="date" class="form-control" id="date-input" value="" />
<label class="task-form-label" for="description-input">Description</label>
<textarea class="form-control" id="description-input" cols="30" rows="5"></textarea>
</div>
<div class="task-form-footer">
<button id="add-or-update-task-btn" class="btn large-btn" type="submit">
Add Task
</button>
</div>
</form>
<dialog id="confirm-close-dialog">
<form method="dialog">
<p class="discard-message-text">Discard unsaved changes?</p>
<div class="confirm-close-dialog-btn-container">
<button id="cancel-btn" class="btn">
Cancel
</button>
<button id="discard-btn" class="btn">
Discard
</button>
</div>
</form>
</dialog>
<div id="tasks-container"></div>
</div>
</main>
<script src="script.js"></script>
</body>
</html>
```
```css
:root {
--white: #fff;
--light-grey: #f5f6f7;
--dark-grey: #0a0a23;
--yellow: #f1be32;
--golden-yellow: #feac32;
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: var(--dark-grey);
}
main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
h1 {
color: var(--light-grey);
margin: 20px 0 40px 0;
}
.todo-app {
background-color: var(--white);
width: 300px;
height: 350px;
border: 5px solid var(--yellow);
border-radius: 8px;
padding: 15px;
position: relative;
display: flex;
flex-direction: column;
gap: 10px;
}
.btn {
cursor: pointer;
width: 100px;
margin: 10px;
color: var(--dark-grey);
background-color: var(--golden-yellow);
background-image: linear-gradient(#fecc4c, #ffac33);
border-color: var(--golden-yellow);
border-width: 3px;
}
.btn:hover {
background-image: linear-gradient(#ffcc4c, #f89808);
}
.large-btn {
width: 80%;
font-size: 1.2rem;
align-self: center;
justify-self: center;
}
.close-task-form-btn {
background: none;
border: none;
cursor: pointer;
}
.close-icon {
width: 20px;
height: 20px;
}
.task-form {
display: flex;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: var(--white);
border-radius: 5px;
padding: 15px;
width: 300px;
height: 350px;
flex-direction: column;
justify-content: space-between;
overflow: auto;
}
.task-form-header {
display: flex;
justify-content: flex-end;
}
.task-form-body {
display: flex;
flex-direction: column;
justify-content: space-around;
}
.task-form-footer {
display: flex;
justify-content: center;
}
.task-form-label,
#title-input,
#date-input,
#description-input {
display: block;
}
.task-form-label {
margin-bottom: 5px;
font-size: 1.3rem;
font-weight: bold;
}
#title-input,
#date-input,
#description-input {
width: 100%;
margin-bottom: 10px;
padding: 2px;
}
#confirm-close-dialog {
padding: 10px;
margin: 10px auto;
border-radius: 15px;
}
.confirm-close-dialog-btn-container {
display: flex;
justify-content: center;
margin-top: 10px;
}
.discard-message-text {
font-weight: bold;
font-size: 1.5rem;
}
#tasks-container {
height: 100%;
overflow-y: auto;
}
.task {
margin: 5px 0;
}
.hidden {
display: none;
}
@media (min-width: 576px) {
.todo-app,
.task-form {
width: 400px;
height: 450px;
}
.task-form-label {
font-size: 1.5rem;
}
#title-input,
#date-input {
height: 2rem;
}
#title-input,
#date-input,
#description-input {
padding: 5px;
margin-bottom: 20px;
}
}
```
```js
const taskForm = document.getElementById("task-form");
const confirmCloseDialog = document.getElementById("confirm-close-dialog");
const openTaskFormBtn = document.getElementById("open-task-form-btn");
const closeTaskFormBtn = document.getElementById("close-task-form-btn");
const addOrUpdateTaskBtn = document.getElementById("add-or-update-task-btn");
const cancelBtn = document.getElementById("cancel-btn");
const discardBtn = document.getElementById("discard-btn");
const tasksContainer = document.getElementById("tasks-container");
const titleInput = document.getElementById("title-input");
const dateInput = document.getElementById("date-input");
const descriptionInput = document.getElementById("description-input");
const taskData = JSON.parse(localStorage.getItem("data")) || [];
let currentTask = {};
const addOrUpdateTask = () => {
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
const taskObj = {
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
title: titleInput.value,
date: dateInput.value,
description: descriptionInput.value,
};
if (dataArrIndex === -1) {
taskData.unshift(taskObj);
} else {
taskData[dataArrIndex] = taskObj;
}
localStorage.setItem("data", JSON.stringify(taskData));
updateTaskContainer()
reset()
};
const updateTaskContainer = () => {
tasksContainer.innerHTML = "";
taskData.forEach(
({ id, title, date, description }) => {
(tasksContainer.innerHTML += `
<div class="task" id="${id}">
<p><strong>Title:</strong> ${title}</p>
<p><strong>Date:</strong> ${date}</p>
<p><strong>Description:</strong> ${description}</p>
<button onclick="editTask(this)" type="button" class="btn">Edit</button>
<button onclick="deleteTask(this)" type="button" class="btn">Delete</button>
</div>
`)
}
);
};
const deleteTask = (buttonEl) => {
const dataArrIndex = taskData.findIndex(
(item) => item.id === buttonEl.parentElement.id
);
buttonEl.parentElement.remove();
taskData.splice(dataArrIndex, 1);
localStorage.setItem("data", JSON.stringify(taskData));
}
const editTask = (buttonEl) => {
const dataArrIndex = taskData.findIndex(
(item) => item.id === buttonEl.parentElement.id
);
currentTask = taskData[dataArrIndex];
titleInput.value = currentTask.title;
dateInput.value = currentTask.date;
descriptionInput.value = currentTask.description;
addOrUpdateTaskBtn.innerText = "Update Task";
taskForm.classList.toggle("hidden");
}
const reset = () => {
addOrUpdateTaskBtn.innerText = "Add Task";
titleInput.value = "";
dateInput.value = "";
descriptionInput.value = "";
taskForm.classList.toggle("hidden");
currentTask = {};
}
if (taskData.length) {
updateTaskContainer();
}
openTaskFormBtn.addEventListener("click", () =>
taskForm.classList.toggle("hidden")
);
closeTaskFormBtn.addEventListener("click", () => {
const formInputsContainValues = titleInput.value || dateInput.value || descriptionInput.value;
const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description;
if (formInputsContainValues && formInputValuesUpdated) {
confirmCloseDialog.showModal();
} else {
reset();
}
});
cancelBtn.addEventListener("click", () => confirmCloseDialog.close());
discardBtn.addEventListener("click", () => {
confirmCloseDialog.close();
reset()
});
taskForm.addEventListener("submit", (e) => {
e.preventDefault();
addOrUpdateTask();
});
```