diff --git a/curriculum/challenges/arabic/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md b/curriculum/challenges/arabic/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md index 0bcd89d2748..24595c7a237 100644 --- a/curriculum/challenges/arabic/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md +++ b/curriculum/challenges/arabic/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md @@ -54,7 +54,24 @@ assert.match(code, /myStorage\.car\.inside/); `gloveBoxContents` should still be declared with `const`. ```js -assert.match(code, /const\s+gloveBoxContents\s*=\s*myStorage\.car\.inside\[\s*("|')glove box\1\s*\]|const\s*{\s*('|")glove box\2:\s*gloveBoxContents\s*}\s*=\s*myStorage\.car\.inside;/); +assert.match(code, /const\s+gloveBoxContents\s*=/); +``` + +You should not change the `myStorage` object. + +```js +const expectedMyStorage = { + "car":{ + "inside":{ + "glove box":"maps", + "passenger seat":"crumbs" + }, + "outside":{ + "trunk":"jack" + } + } +}; +assert.deepStrictEqual(myStorage, expectedMyStorage); ``` # --seed-- diff --git a/curriculum/challenges/arabic/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md b/curriculum/challenges/arabic/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md index 5cae5df96ed..9027b7de5aa 100644 --- a/curriculum/challenges/arabic/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md +++ b/curriculum/challenges/arabic/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md @@ -10,7 +10,7 @@ dashedName: catch-misspelled-variable-and-function-names يكونا `console.log()` و `typeof` الطرقتان الرئيستان للتحقق من القيم وأنواعها في الكود. الآن حان الوقت للدخول في الأشكال الشائعة التي تأخذها الأخطاء (bugs). أحد المشاكل ويقع فيه بالذات الكتبة السريعين هو الخطأ الإملائي المتواضع (humble spelling error). -الأحرف المبدلة، المفقودة، أو الأحرف كبيرة بالخطأ (mis-capitalized) في اسم متغير أو وظيفة سيجعل المتصفح يبحث عن شيء غير موجود - ويبلغ عن خطأ مرجعي (reference error). في لغة JavaScript أسماء المتغير والوظائف هي حساسة لحالة الأحرف (case-sensitive). +Transposed, missing, or miscapitalized characters in a variable or function name will have the browser looking for an object that doesn't exist - and complain in the form of a reference error. في لغة JavaScript أسماء المتغير والوظائف هي حساسة لحالة الأحرف (case-sensitive). # --instructions-- diff --git a/curriculum/challenges/arabic/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md b/curriculum/challenges/arabic/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md index bf63a0b28ed..e7b4571452b 100644 --- a/curriculum/challenges/arabic/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md +++ b/curriculum/challenges/arabic/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md @@ -58,8 +58,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/arabic/06-quality-assurance/quality-assurance-projects/issue-tracker.md b/curriculum/challenges/arabic/06-quality-assurance/quality-assurance-projects/issue-tracker.md index 406e4ebaa93..b049277a0a6 100644 --- a/curriculum/challenges/arabic/06-quality-assurance/quality-assurance-projects/issue-tracker.md +++ b/curriculum/challenges/arabic/06-quality-assurance/quality-assurance-projects/issue-tracker.md @@ -231,13 +231,13 @@ async (getUserInput) => { }; const url = getUserInput('url') + '/api/issues/fcc-project'; const itemToUpdate = await $.post(url, initialData); - const updateSucccess = await $.ajax({ + const updateSuccess = await $.ajax({ url: url, type: 'PUT', data: { _id: itemToUpdate._id, issue_text: 'New Issue Text' } }); - assert.isObject(updateSucccess); - assert.deepEqual(updateSucccess, { + assert.isObject(updateSuccess); + assert.deepEqual(updateSuccess, { result: 'successfully updated', _id: itemToUpdate._id }); diff --git a/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/24-game.md b/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/24-game.md index ac16df78509..88cdeed88e7 100644 --- a/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/24-game.md +++ b/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/24-game.md @@ -82,7 +82,7 @@ const OPERATORS_ = { "/": (a, b) => a / b, } -const PRECIDENCE_ = { +const PRECEDENCE_ = { "+": 1, "-": 1, "*": 2, @@ -114,7 +114,7 @@ function evaluate_(expression) { case "*": case "/": while (stack.length && - PRECIDENCE_[c] <= PRECIDENCE_[stack[stack.length-1]]) { + PRECEDENCE_[c] <= PRECEDENCE_[stack[stack.length-1]]) { postfix += stack.pop(); } stack.push(c); diff --git a/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md b/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md index b6a78ff636a..46b65350213 100644 --- a/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md +++ b/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md @@ -175,7 +175,7 @@ function dealFreeCell(seed) { const rng = FreeCellRNG(seed); const deck = getDeck(); - const deltCards = [[], [], [], [], [], [], []]; + const dealtCards = [[], [], [], [], [], [], []]; let currentColumn = 0; let currentRow = 0; @@ -195,7 +195,7 @@ function dealFreeCell(seed) { card = deck.pop(); // Deal this card - deltCards[currentRow].push(card); + dealtCards[currentRow].push(card); currentColumn += 1; if (currentColumn === 8) { currentColumn = 0; @@ -203,6 +203,6 @@ function dealFreeCell(seed) { } } - return deltCards; + return dealtCards; } ``` diff --git a/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/department-numbers.md b/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/department-numbers.md index 1117963ac08..5414cdcf18d 100644 --- a/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/department-numbers.md +++ b/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/department-numbers.md @@ -101,7 +101,7 @@ function combinations(possibleNumbers, total) { function combinations(possibleNumbers, total) { let firstNumber; let secondNumber; - let thridNumber; + let thirdNumber; const allCombinations = []; for (let i = 0; i < possibleNumbers.length; i += 1) { @@ -112,10 +112,10 @@ function combinations(possibleNumbers, total) { secondNumber = possibleNumbers[j]; if (j !== i && firstNumber + secondNumber <= total) { - thridNumber = total - firstNumber - secondNumber; + thirdNumber = total - firstNumber - secondNumber; - if (thridNumber !== firstNumber && thridNumber !== secondNumber && possibleNumbers.includes(thridNumber)) { - allCombinations.push([firstNumber, secondNumber, thridNumber]); + if (thirdNumber !== firstNumber && thirdNumber !== secondNumber && possibleNumbers.includes(thirdNumber)) { + allCombinations.push([firstNumber, secondNumber, thirdNumber]); } } } diff --git a/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/lu-decomposition.md b/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/lu-decomposition.md index 45759df22aa..9255204dd79 100644 --- a/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/lu-decomposition.md +++ b/curriculum/challenges/arabic/10-coding-interview-prep/rosetta-code/lu-decomposition.md @@ -80,7 +80,7 @@ $PA = LU$ # --instructions-- -The task is to implement a routine which will take a square nxn matrix $A$ and return a lower triangular matrix $L$, a upper triangular matrix $U$ and a permutation matrix $P$, so that the above equation is fullfilled. The returned value should be in the form `[L, U, P]`. +The task is to implement a routine which will take a square nxn matrix $A$ and return a lower triangular matrix $L$, a upper triangular matrix $U$ and a permutation matrix $P$, so that the above equation is fulfilled. The returned value should be in the form `[L, U, P]`. # --hints-- diff --git a/curriculum/challenges/arabic/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md b/curriculum/challenges/arabic/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md index 7801e5f0550..5b8f58a051d 100644 --- a/curriculum/challenges/arabic/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md +++ b/curriculum/challenges/arabic/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md @@ -9,7 +9,7 @@ dashedName: step-82 يشترك منقار البِطْرِيق وقدميه في نفس اللون `color`. -أنشئ متغير CSS مخصص جديد يسمى `--penguin-picorna`، واستبدل جميع قيم الخاصية ذات صلة بهما. +Create a new custom CSS variable named `--penguin-picorna`, and replace all relevant property values with it. # --hints-- diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md index c335f2d7bc9..a1c1c26f120 100644 --- a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md @@ -11,7 +11,7 @@ Inside `pick`, use `let` to initialize a variable named `numbers` and set it to # --hints-- -Your `pick` function should initalize `numbers` to an empty array `[]`. +Your `pick` function should initialize `numbers` to an empty array `[]`. ```js assert.match(pick.toString(), /numbers\s*=\s*\[\]/); diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md new file mode 100644 index 00000000000..4b042aa87ba --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md @@ -0,0 +1,310 @@ +--- +id: 64e4e4c4ec263b62ae7bf54d +title: Step 1 +challengeType: 0 +dashedName: step-1 +--- + +# --description-- + +In this project, you will learn how `localStorage` works in JavaScript by building a Todo app. LocalStorage is a web storage feature of JavaScript that lets you persist data by storing the data as a **key:value** pair. + +The HTML and CSS for this project have been provided for you. Take a look at the files to get yourself familiarized with them. + +Begin by accessing the `task-form`, `confirm-close-dialog`, and `open-task-form-btn` elements with the `getElementById()` method. Save them in the variables `taskForm`, `confirmCloseDialog`, and `openTaskFormBtn`. + +# --hints-- + +You should use `getElementById()` to access the `task-form` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should assign the `task-form` element to the variable `taskForm`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+taskForm\s*=\s*document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should use `getElementById()` to access the `confirm-close-dialog` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should assign the `confirm-close-dialog` element to the variable `confirmCloseDialog`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+confirmCloseDialog\s*=\s*document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should use `getElementById()` to access the `open-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +You should assign the `open-task-form-btn` element to the variable `openTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+openTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md new file mode 100644 index 00000000000..32310409728 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md @@ -0,0 +1,310 @@ +--- +id: 64e4e6c86954de67a3e44ee3 +title: Step 2 +challengeType: 0 +dashedName: step-2 +--- + +# --description-- + +You need to access more elements with the `getElementById()` method. This time you need the `close-task-form-btn`, `add-or-update-task-btn`, and `cancel-btn` elements. Save them in the variables `closeTaskFormBtn`, `addOrUpdateTaskBtn`, and `cancelBtn`. + +# --hints-- + +You should use `getElementById()` to access the `close-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should assign the `close-task-form-btn` element to the variable `closeTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+closeTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `add-or-update-task-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should assign the `add-or-update-task-btn` element to the variable `addOrUpdateTaskBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+addOrUpdateTaskBtn\s*=\s*document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `cancel-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +You should assign the `cancel-btn` element to the variable `cancelBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+cancelBtn\s*=\s*document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md new file mode 100644 index 00000000000..17fc6b76517 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md @@ -0,0 +1,313 @@ +--- +id: 64e4e6fe78b5aa67ef2fc3e7 +title: Step 3 +challengeType: 0 +dashedName: step-3 +--- + +# --description-- + +Next, access the `discard-btn`, `tasks-container`, and `title-input` elements using the `getElementById()` method. Save them in variables named `discardBtn`, `tasksContainer`, and `titleInput`, respectively. + +# --hints-- + +You should use `getElementById()` to access the `discard-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should assign the `discard-btn` element to the variable `discardBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+discardBtn\s*=\s*document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `tasks-container` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should assign the `tasks-container` element to the variable `tasksContainer`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+tasksContainer\s*=\s*document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should use `getElementById()` to access the `title-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +You should assign the `title-input` element to the variable `titleInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+titleInput\s*=\s*document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md new file mode 100644 index 00000000000..6e543d36acc --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md @@ -0,0 +1,304 @@ +--- +id: 64e4e7241f52bb682eeb8211 +title: Step 4 +challengeType: 0 +dashedName: step-4 +--- + +# --description-- + +The last set of elements you need to get from the HTML file are the `date-input` and `description-input` elements. Save them in the variables `dateInput` and `descriptionInput` respectively. + +# --hints-- + +You should use `getElementById()` to access the `date-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should assign the `date-input` element to the variable `dateInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+dateInput\s*=\s*document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should use `getElementById()` to access the `description-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +You should assign the `description-input` element to the variable `descriptionInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+descriptionInput\s*=\s*document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md new file mode 100644 index 00000000000..6f0cd6e9fc4 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md @@ -0,0 +1,296 @@ +--- +id: 64e4e74d0fb4f0687bf4145d +title: Step 5 +challengeType: 0 +dashedName: step-5 +--- + +# --description-- + +Create a `taskData` constant and set it to an empty array. This array will store all the tasks along with their associated data, including title, due date, and description. This storage will enable you to keep track of tasks, display them on the page, and save them to `localStorage`. + +Use `let` to create a `currentTask` variable and set it to an empty object. This variable will be used to track the state when editing and discarding tasks. + +# --hints-- + +You should create a `taskData` constant set to an empty array. + +```js +assert.match(code, /const\s+taskData\s*=\s*\[\];?/) +``` + +You should use `let` to create an empty `currentTask` object. + +```js +assert.match(code, /let\s+currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md new file mode 100644 index 00000000000..7eba2896bf4 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md @@ -0,0 +1,307 @@ +--- +id: 64e4e78a7ea4a168de4e6a38 +title: Step 6 +challengeType: 0 +dashedName: step-6 +--- + +# --description-- + +Now, you will work on opening and closing the form modal. + +Add a `click` event listener to the `openTaskFormBtn` variable, and then use `classList` to toggle the `hidden` class on the `taskForm` element. + +Now you can click on the "Add new Task" button and see the form modal. + +# --hints-- + +You should call the `addEventListener()` method on your `openTaskFormBtn` variable. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax, then use `classList` property and it's `toggle` method on the `taskForm`, to toggle the class `hidden`. Don't use curly braces. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*taskForm\.classList\.toggle\(\1hidden\1\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md new file mode 100644 index 00000000000..6a29158134e --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md @@ -0,0 +1,309 @@ +--- +id: 64e4e7bbedb22d6939001ad3 +title: Step 7 +challengeType: 0 +dashedName: step-7 +--- + +# --description-- + +Add a `click` event listener to the `closeTaskFormBtn` variable, then use the `showModal()` method on your `confirmCloseDialog` variable. This will display a modal with the `Discard` and `Cancel` buttons. + +`showModal()` is a method associated with the HTML `dialog` element. It is used to display a modal dialog box on a web page. + +# --hints-- + +You should call the `addEventListener()` method on your `closeTaskFormBtn` variable. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `showModal()` method on `confirmCloseDialog`. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(["'`]click["'`],\s*\(.*\)\s*=>\s*{?\s*confirmCloseDialog\.showModal\(\);?\s*}?\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md new file mode 100644 index 00000000000..311984043a0 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md @@ -0,0 +1,316 @@ +--- +id: 64e4eaaa9070a66aecbfe603 +title: Step 8 +challengeType: 0 +dashedName: step-8 +--- + +# --description-- + +If the user clicks the `Cancel` button, you want to cancel the process (close the modal showing the two buttons) so the user can continue editing. + +Add a `click` event listener to the `cancelBtn` element, then use the `close()` method on the `confirmCloseDialog` variable. + +`close()` is a method of the window object you can use to close the current window, or a modal you create with the `dialog` element. + +# --hints-- + +You should call the `addEventListener()` method on your `cancelBtn` variable. + +```js +assert.match(code, /cancelBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `close()` method on `confirmCloseDialog`. Don't use curly braces. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*confirmCloseDialog\.close\(\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- + +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md new file mode 100644 index 00000000000..66a40c9771b --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md @@ -0,0 +1,327 @@ +--- +id: 64e4ebc7eabc5a6babd479cd +title: Step 9 +challengeType: 0 +dashedName: step-9 +--- + +# --description-- + +If the user clicks the `Discard` button, you want to close the modal showing the `Cancel` and `Discard` buttons, then hide the form modal. + +Add a click event listener to `discardBtn`, then use the `close()` method on the `confirmCloseDialog` variable. Also, use `classList` to toggle the class `hidden` on `taskForm` so the form modal will close too. + +# --hints-- + +You should call the `addEventListener()` method on your `discardBtn` variable. + +```js +assert.match(code, /discardBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{/) +``` + +Your event listener should use the `close()` method on `confirmCloseDialog`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?/) +``` + +Your event listener should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?\s*taskForm\.classList\.toggle\(\1hidden\1\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md new file mode 100644 index 00000000000..f4002dc0937 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md @@ -0,0 +1,326 @@ +--- +id: 64e4ecd7735a566c9266a338 +title: Step 10 +challengeType: 0 +dashedName: step-10 +--- + +# --description-- + +Now that you've worked on opening and closing the modal, it's time to get the values from the input fields, save them into the `taskData` array, and display them on the page. + +To start, add a `submit` event listener to your `taskForm` element and pass in `e` as the parameter of your arrow function. Inside the curly braces, use the `preventDefault()` method to stop the browser from refreshing the page after submitting the form. + +# --hints-- + +You should call the `addEventListener()` method on your `taskForm` variable. + +```js +assert.match(code, /taskForm\.addEventListener\(/) +``` + +Your event listener should listen for a `submit` event. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces with `e` as the parameter. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{/) +``` + +You should use the `e.preventDefault()` method to stop the browser from reloading the page. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{\s*e\.preventDefault\(\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md new file mode 100644 index 00000000000..148e52f3e93 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md @@ -0,0 +1,337 @@ +--- +id: 64e4eec13546c06d61a63d59 +title: Step 11 +challengeType: 0 +dashedName: step-11 +--- + +# --description-- + +You need to determine whether the task being added already exists or not. If it doesn't exist, you will add it, and if it already exists, you will set it up for editing. You can use the `findIndex()` method to accomplish this. + +`findIndex` is an array method that lets find the index of the first element in an array that satisfies a given testing function. + +Here's an example: + +```js +const numbers = [3, 1, 5, 6, 10, 9, 8]; +const firstEvenNumIndex = numbers.findIndex((num) => num % 2 === 0); + +console.log(firstEvenNumIndex); // Output: 3 – because the first even number (6) is at index 3 +``` + +Declare a `dataArrIndex` variable using `const` and set it to the result of the `findIndex()` method applied to the `taskData` array. Utilize arrow syntax to provide a callback function with `item` as the parameter, and within the callback, check if the `id` property of `item` is equal to the `id` property of `currentTask`. + +If the task exists, this returns the index, and if it doesn't exist, it returns `-1`. + +# --hints-- + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === currentTask.id`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?\s*=>\s*item.id\s*===\s*currentTask.id\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md new file mode 100644 index 00000000000..10826de6f17 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md @@ -0,0 +1,340 @@ +--- +id: 64e4f01d6c72086e016a8626 +title: Step 12 +challengeType: 0 +dashedName: step-12 +--- + +# --description-- + +Next, retrieve the values from the input fields and store them in a `taskObj` object. Each task should also have a unique `id`. + +Create a `taskObj` object with an `id` property as the first property. For the value of the `id` property, retrieve the value of the `titleInput` field, convert it to lowercase, and then use the `split()` and `join()` methods to hyphenate it. + +Make sure all of those are in template literals because you need the `id` property value as a string. + +# --hints-- + +You should use `const` to create a `taskObj` object. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*/) +``` + +Your `taskObj` object should have a key of `id`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id/) +``` + +You should use template strings to get `titleInput.value` as the value of your `id` key and convert it to lowercase. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)/) +``` + +You should use `.split(' ')` on `titleInput.value.toLowerCase()`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)/) +``` + +You should use `.join('-')` on `titleInput.value.toLowerCase().split(' ')`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md new file mode 100644 index 00000000000..764de1b0918 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md @@ -0,0 +1,327 @@ +--- +id: 64ec89ee549ecf802de2b3e2 +title: Step 14 +challengeType: 0 +dashedName: step-14 +--- + +# --description-- + +Retrieve the values from the `titleInput`, `dateInput`, and `descriptionInput` fields, and then save them in the properties `title`, `date`, and `description` of the `taskObj` object. + +# --hints-- + +You should get the value of `titleInput` and save it in a `title` key. + +```js +assert.match(code, /title:\s*titleInput\.value,/) +``` + +You should get the value of `dateInput` and save it in a `date` key. + +```js +assert.match(code, /date:\s*dateInput\.value,/) +``` + +You should get the value of `descriptionInput` and save it in a `description` key. + +```js +assert.match(code, /description:\s*descriptionInput\.value,?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + const taskObj = { + id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`, + --fcc-editable-region-- + + --fcc-editable-region-- + }; +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md new file mode 100644 index 00000000000..3d009188d81 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md @@ -0,0 +1,332 @@ +--- +id: 64ec8f717b261e824d82d6a5 +title: Step 15 +challengeType: 0 +dashedName: step-15 +--- + +# --description-- + +Now that you have obtained the values from the input fields and generated an `id`, you want to add them to your `taskData` array to keep track of each task. However, you should only do this if the task is new. If the task already exists, you will set it up for editing. This is why you have the `dataArrIndex` variable, which provides the index of each task. + +Create an `if` statement with the condition `dataArrIndex === -1`. Within the `if` statement, use the `unshift()` method to add the `taskObj` object to the beginning of the `taskData` array. + +`unshift()` is an array method that is used to add one or more elements to the beginning of an array. + +**N.B:** Try adding a task and log `taskData` to the console to see what it looks like. + +# --hints-- + +You should create an `if` statement with the condition `dataArrIndex === -1`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{/) +``` + +Your `if` statement should have `taskData.unshift(taskObj)` in it's body. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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, + }; + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md new file mode 100644 index 00000000000..f1202bb93fe --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md @@ -0,0 +1,331 @@ +--- +id: 64ec9145e424d8835a4e0f28 +title: Step 16 +challengeType: 0 +dashedName: step-16 +--- + +# --description-- + +Now that you have saved the task in the `taskData` array, you should display the task on the page by looping through it. + +Use `forEach()` on `taskData`, then destructure `id`, `title`, `date`, `description` as the parameters. Don't return anything yet. + +# --hints-- + +You should use `forEach()` on `taskData`. + +```js +assert.match(code, /taskData\.forEach\(\s*/) +``` + +You should destructure `id`, `title`, `date`, `description` as the parameters of the callback of your `forEach()`. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md new file mode 100644 index 00000000000..06bdd7c6ae5 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md @@ -0,0 +1,336 @@ +--- +id: 64ec9282cd547785258cecf2 +title: Step 17 +challengeType: 0 +dashedName: step-17 +--- + +# --description-- + +Using arrow syntax, create an implicit return and use addition assignment to set the `innerHTML` of `tasksContainer` to empty backticks. + +# --hints-- + +You should not alter the existing `taskData.forEach()` and the values you destructured. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +You should use arrow syntax and attach `innerHTML` to `tasksContainer`. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\(\s*tasksContainer\.innerHTML\s*\))?/) +``` + +You should use addition assignment to set the `innerHTML` of `tasksContainer` to an empty pair of backticks. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\s*\(?tasksContainer\.innerHTML\s*\+=\s*`\s*`\)?)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + taskData.forEach(({id, title, date, description})); + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md new file mode 100644 index 00000000000..b5115f99a51 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md @@ -0,0 +1,333 @@ +--- +id: 64ec9343769e8f85c1e17e05 +title: Step 18 +challengeType: 0 +dashedName: step-18 +--- + +# --description-- + +Create a `div` element with the class of `task`. Utilize template strings to set the `id` attribute of the `div` to the `id` you destructured from the task data. + +# --hints-- + +You should create a `div` element with the class `task`. + +```js +assert.match(code, /\s*<\/div>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` + --fcc-editable-region-- + + --fcc-editable-region-- + `) + ); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md new file mode 100644 index 00000000000..81dd3530420 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md @@ -0,0 +1,348 @@ +--- +id: 64ec94f0de20c086e09b0fc3 +title: Step 19 +challengeType: 0 +dashedName: step-19 +--- + +# --description-- + +Create a `p` element and use template strings to set its content to the `title` you destructured. Right before the content of the `p` element, create a `strong` element with the text `Title:`. + +# --hints-- + +You should create a `p` element. + +```js +assert.match(code, /

/) +``` + +You should interpolate `${title}` as the text of your `p` element. + +```js +assert.match(code, /

.*\$\{title\}<\/p>/) +``` + +You should create a `strong` element after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)/) +``` + +Your `strong` element should have the text `Title:`. + +```js +assert.match(code, /(?<=

)Title:\s*<\/strong>\s*/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md new file mode 100644 index 00000000000..4820d100d4d --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md @@ -0,0 +1,336 @@ +--- +id: 64ec959a76336c8767f5cd4d +title: Step 20 +challengeType: 0 +dashedName: step-20 +--- + +# --description-- + +Similarly to the previous step, create another `p` element, and interpolate the `date` you destructured as the text content. Inside this paragraph, create a `strong` element with the text `Date:`. + +# --hints-- + +You should create a `p` element and interpolate `${date}` as the text. + +```js +assert.match(code, /

.*\$\{date\}<\/p>/) +``` + +You should create a `strong` element with the text `Date:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Date:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md new file mode 100644 index 00000000000..4525b0e154c --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md @@ -0,0 +1,340 @@ +--- +id: 64ec96761156a187ed32b274 +title: Step 22 +challengeType: 0 +dashedName: step-22 +--- + +# --description-- + +To allow for task management, you need to include both a delete and an edit button for each task. + +Create two `button` elements with the `type` attribute set to `button` and the `class` attribute set to `btn`. Set the text of the first button to `Edit` and the text of the second button to `Delete`. + +# --hints-- + +You should create a `button` element of type `button`, a class `btn` and `Edit` as the text, in that order. + +```js +assert.match(code, /Edit<\/button/) +``` + +You should create a `button` element of type `button` a class `btn` and `Delete` as the text, in that order. + +```js +assert.match(code, /Delete<\/button/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md new file mode 100644 index 00000000000..0081fa88e1a --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md @@ -0,0 +1,336 @@ +--- +id: 64ec9b10356c2d8aa05d9ce1 +title: Step 23 +challengeType: 0 +dashedName: step-23 +--- + +# --description-- + +After adding the task to the page, you should close the form modal to view the task. To do this, utilize `classList` to toggle the `hidden` class on the `taskForm` element. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +const splitter = code.split("taskData.forEach") +assert.match(splitter[1], /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md new file mode 100644 index 00000000000..6ae1e4dff48 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md @@ -0,0 +1,347 @@ +--- +id: 64ec9c55fdeef78bacd2fc3b +title: Step 24 +challengeType: 0 +dashedName: step-24 +--- + +# --description-- + +If you attempt to add another task now, you'll notice that the input fields retain the values you entered for the previous task. To resolve this, you need to clear the input fields after adding a task. + +Instead of clearing the input fields one by one, it's a good practice to create a function that handles clearing those fields. You can then call this function whenever you need to clear the input fields again. + +Use arrow syntax to create a `reset` function and set it to a pair of curly braces. + +# --hints-- + +You should use `const` and arrow syntax to create a `reset` function. + +```js +assert.match(code, /const\s+reset\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +Your `reset` function should be empty. + +```js +assert.match(reset.toString(), /\(\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md new file mode 100644 index 00000000000..e12fb12ab81 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md @@ -0,0 +1,366 @@ +--- +id: 64fac365aeb8ad70b69b366f +title: Step 25 +challengeType: 0 +dashedName: step-25 +--- + +# --description-- + +Inside the `reset` function, set each value of `titleInput`, `dateInput`, `descriptionInput` to an empty string. + +Also, use `classList` to toggle the class `hidden` on the `taskForm` and set `currentTask` to an empty object. That's because at this point, `currentTask` will be filled with the task the user might have added. + +# --hints-- + +You should set `titleInput.value` to an empty string. + +```js +assert.match(reset.toString(), /titleInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `dateInput.value` to an empty string. + +```js +assert.match(reset.toString(), /dateInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `descriptionInput.value` to an empty string. + +```js +assert.match(reset.toString(), /descriptionInput\.value\s*=\s*('|")\1;?/) +``` + +You should use `classList` to toggle the class `hidden` on `taskForm` + +```js +assert.match(reset.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +You should set `currentTask` to an empty object. + +```js +assert.match(reset.toString(), /currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- +const reset = () => { + +} +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md new file mode 100644 index 00000000000..5f281d0de16 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md @@ -0,0 +1,351 @@ +--- +id: 64fac4d1773e7a719b1254de +title: Step 26 +challengeType: 0 +dashedName: step-26 +--- + +# --description-- + +Remove the existing code toggling the class of `hidden` on `taskForm` and call the `reset` function instead. This would clear the input fields and also hide the form modal for the user to see the added task. + +# --hints-- + +Yous should remove the code toggling the `hidden` class on `taskForm`. + +```js +const splitter = code.split('') +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /reset\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + +--fcc-editable-region-- + taskForm.classList.toggle("hidden"); +--fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md new file mode 100644 index 00000000000..c1a7b0f8965 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md @@ -0,0 +1,351 @@ +--- +id: 64fac6a497811572b338e5e5 +title: Step 27 +challengeType: 0 +dashedName: step-27 +--- + +# --description-- + +Also, remove the existing code toggling the class `hidden` on `taskForm` inside the `discardBtn` event listener and call the `reset` function instead. That's because when you click the `Discard` button, everything in the input fields should go away. + +# --hints-- + +Yous should remove the code toggling the class `hidden` on `taskForm`. + +```js +const splitter = code.split("confirmCloseDialog.close();") +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /confirmCloseDialog\.close\(\);?\s*reset\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); +--fcc-editable-region-- + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md new file mode 100644 index 00000000000..84005e1b01a --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md @@ -0,0 +1,346 @@ +--- +id: 64faca774fd9fd74bc084cc9 +title: Step 28 +challengeType: 0 +dashedName: step-28 +--- + +# --description-- + +You should display the `Cancel` and `Discard` buttons to the user only if there is some text present in the input fields. + +To begin, within the `closeTaskFormBtn` event listener, create a `formInputsContainValues` variable to check if there is a value in the `titleInput` field **or** the `dateInput` field **or** the `descriptionInput` field. + +# --hints-- + +You should use `const` to create a variable `formInputsContainValues` with the value `titleInput.value || dateInput.value || descriptionInput.value;` + +```js +assert.match(code, /const\s*formInputsContainValues\s*=\s*titleInput\.value\s*\|\|\s*dateInput\.value\s*\|\|\s*descriptionInput\.value;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); + +}); +--fcc-editable-region-- + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md new file mode 100644 index 00000000000..07482464ebf --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md @@ -0,0 +1,362 @@ +--- +id: 64facf6180824876f70a2e86 +title: Step 29 +challengeType: 0 +dashedName: step-29 +--- + +# --description-- + +Create an `if` statement to check if `formInputsContainValues` is true. If `formInputsContainValues` is true, indicating that there are changes, use the `showModal()` method on `confirmCloseDialog`. Otherwise, if there are no changes, call the `reset()` function to clear the input fields and hide the form modal. + +# --hints-- + +You should create an `if` statement with the condition `formInputsContainValues`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{/) +``` + +The `if` block of your `if` statement should contain `confirmCloseDialog.showModal();`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?/) +``` + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{/) +``` + +You should call the `reset()` function in the `else` block of your `if` statement. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{\s*reset\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 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; +--fcc-editable-region-- + confirmCloseDialog.showModal(); +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md new file mode 100644 index 00000000000..95b71fba69f --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md @@ -0,0 +1,372 @@ +--- +id: 64fad07f43a101779cb8692a +title: Step 30 +challengeType: 0 +dashedName: step-30 +--- + +# --description-- + +You can enhance code readability and maintainability by refactoring the `submit` event listener into two separate functions. The first function can be used to add the input values to `taskData`, while the second function can be responsible for adding the tasks to the DOM. + +Use arrow syntax to create an `addOrUpdateTask` function. Then move the `dataArrIndex` variable, the `taskObj` object, and the `if` statement into the `addOrUpdateTask` function. + +# --hints-- + +You should use `const` and arrow syntax to create an `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +You should move the `dataArrIndex` variable into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?/) +``` + +You should move the `taskObj` object into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?/) +``` + +You should move the `if` statement with the condition `dataArrIndex === 1` into your `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?\s*if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md new file mode 100644 index 00000000000..8192699bb61 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md @@ -0,0 +1,358 @@ +--- +id: 64fad9cd2eeb1e7ca2ca8c8b +title: Step 31 +challengeType: 0 +dashedName: step-31 +--- + +# --description-- + +Use arrow syntax to create an `updateTaskContainer` function. Then move the `taskData.forEach()` and its content into it. + +# --hints-- + +You should use `const` and arrow syntax to create a `updateTaskContainer` function. + +```js +assert.match(code, /const\s*updateTaskContainer\s*=\s*\(\)\s*=>\s*\{/) +``` + +You should move `taskData.forEach()` and its content into the `updateTaskContainer()` function. + +```js +assert.match(code, /const\s+updateTaskContainer\s+=\s*\(\)\s*=>\s*\{\s*taskData\.forEach\(\s*\(\{\s*id,\s*title,\s*date,\s*description\s*\}\)\s*=>\s*\(tasksContainer\.innerHTML\s*\+=\s*`\s*\s*

Title:<\/strong>\s*\$\{title\}<\/p>\s*

Date:<\/strong>\s*\$\{date\}<\/p>\s*

Description:<\/strong>\s*\$\{description\}<\/p>\s*Edit<\/button>\s*Delete<\/button>\s*<\/div>\s*`\)\s*\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md new file mode 100644 index 00000000000..18b2a462de4 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md @@ -0,0 +1,360 @@ +--- +id: 64fadae4f2d51b7d5d8b98d8 +title: Step 32 +challengeType: 0 +dashedName: step-32 +--- + +# --description-- + +Inside the `addOrUpdateTask` function, call the `updateTaskContainer` and `reset` functions. + +# --hints-- + +You should call the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\)\s*/) +``` + +You should call the `reset` function after calling the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\);?\s*reset\(\);?\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + reset() +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md new file mode 100644 index 00000000000..657bb4aa05b --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md @@ -0,0 +1,355 @@ +--- +id: 64fadff23375f27ff06c6d40 +title: Step 33 +challengeType: 0 +dashedName: step-33 +--- + +# --description-- + +Now remove the `reset()` call inside the `taskForm` submit event listener and call the `addOrUpdateTask` function instead. + +# --hints-- + +You should remove `reset()` and call `addOrUpdateTask()`. + +```js +assert.match(code, /addOrUpdateTask\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + --fcc-editable-region-- + reset() + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md new file mode 100644 index 00000000000..bd759fb7e5f --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md @@ -0,0 +1,364 @@ +--- +id: 64fae068bcdc9c805bd8399e +title: Step 35 +challengeType: 0 +dashedName: step-35 +--- + +# --description-- + +To enable editing and deleting for each task, add an `onclick` attribute to both buttons. Set the value of the `onclick` attribute to `editTask(this)` for the `Edit` button and `deleteTask(this)` for the `Delete` button. The `editTask(this)` function will handle editing, while the `deleteTask(this)` function will handle deletion. + +`this` is a keyword that refers to the current context. In this case, `this` points to the element that triggers the event – the buttons. + +# --hints-- + +You should add `onclick="editTask(this)"` as the first attribute of the edit button. + +```js +assert.match(code, /Edit<\/button>/) +``` + +You should add `onclick="deleteTask(this)"` as the first attribute of the delete button. + +```js +assert.match(code, /Delete<\/button>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + + --fcc-editable-region-- +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md new file mode 100644 index 00000000000..c6fac350818 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md @@ -0,0 +1,371 @@ +--- +id: 64faedcd16a1e985c4c2dc94 +title: Step 36 +challengeType: 0 +dashedName: step-36 +--- + +# --description-- + +Create a `deleteTask` function using arrow syntax. Pass `buttonEl` as the parameter and define an empty set of curly braces for the function body. + +# --hints-- + +You should use `const` and arrow syntax to create a `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `deleteTask` function should take a `buttonEl` parameter. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `deleteTask` function should be empty. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md new file mode 100644 index 00000000000..27631c77f2f --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md @@ -0,0 +1,359 @@ +--- +id: 64faf0418e828c0114a558a7 +title: Step 34 +challengeType: 0 +dashedName: step-34 +--- + +# --description-- + +There's a problem. If you add a task, and then add another, the previous task gets duplicated. This means you need to clear out the existing contents of `tasksContainer` before adding a new task. + +Set the `innerHTML` of `taskContainer` back to an empty string. + + +# --hints-- + +You should set the `innerHTML` of `tasksContainer` to an empty string. + +```js +assert.match(code, /tasksContainer\.innerHTML\s*=\s*("|')\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + --fcc-editable-region-- + + --fcc-editable-region-- + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md new file mode 100644 index 00000000000..9cdbd7860ca --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md @@ -0,0 +1,382 @@ +--- +id: 64faf65b22ad8d07df9be14d +title: Step 37 +challengeType: 0 +dashedName: step-37 +--- + +# --description-- + +You need to find the index of the task you want to delete first. + +Create a `dataArrIndex` variable and set its value using the `findIndex()` method on the `taskData` array. Pass `item` as the parameter for the arrow callback function, and within the callback, check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. Don't use curly braces. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?\s*item\s*\)?\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\s*\);?\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md new file mode 100644 index 00000000000..0e54adadf06 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md @@ -0,0 +1,398 @@ +--- +id: 64faf874364ec308f875f636 +title: Step 38 +challengeType: 0 +dashedName: step-38 +--- + +# --description-- + +You need to remove the task from the DOM using `remove()` and from the `taskData` array using `splice()`. + +`splice()` is an array method that modifies arrays by removing, replacing, or adding elements at a specified index, while also returning the removed elements. It can take up to three parameters: the first one is the mandatory index at which to start, the second is the number of items to remove, and the third is an optional replacement element. Here's an example: + +```js +const fruits = ["mango", "date", "cherry", "banana", "apple"]; + +// Remove date and cherry from the array starting at index 1 +const removedFruits = fruits.splice(1, 2); + +console.log(fruits); // [ 'mango', 'banana', 'apple' ] +console.log(removedFruits); // [ 'date', 'cherry' ] +``` + +Use the `remove()` method to remove the `parentElement` of the `buttonEl` from the DOM. Then use `splice()` to remove the task from the `taskData` array. Pass in `dataArrIndex` and `1` as the parameters of your `splice()`. + +`dataArrIndex` is the index to start and `1` is the number of items to remove. + +# --hints-- + +You should use the `remove()` method to remove the parent element of `buttonEl`. + +```js +assert.match(deleteTask.toString(), /buttonEl\.parentElement\.remove\(\);?/) +``` + +You should use `splice()` on the `taskData` array. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(/) +``` + +The first parameter of your `splice()` method should be `dataArrIndex`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex/) +``` + +The second parameter of your `splice()` method should be `1`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex,\s*1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md new file mode 100644 index 00000000000..3f379d51764 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md @@ -0,0 +1,381 @@ +--- +id: 64fafac95328110a69bcb75f +title: Step 39 +challengeType: 0 +dashedName: step-39 +--- + +# --description-- + +Use arrow syntax to create an `editTask` function. Pass in `buttonEl` as the parameter and add empty curly braces for the body. + +# --hints-- + +You should use `const` and arrow syntax to create an `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `editTask` function should take a `buttonEl` parameter. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `editTask` function should be empty. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md new file mode 100644 index 00000000000..bc0e2a84503 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md @@ -0,0 +1,391 @@ +--- +id: 64fb0fa0968f2b113b2d90e9 +title: Step 40 +challengeType: 0 +dashedName: step-40 +--- + +# --description-- + +As you did in the `deleteTask` function, you need to find the index of the task to be edited. + +Create a `dataArrIndex` variable. For its value, utilize the `findIndex()` method on `taskData`. Pass `item` as the parameter to its callback function and check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md new file mode 100644 index 00000000000..14d527ecf40 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md @@ -0,0 +1,373 @@ +--- +id: 64fb1061ca838611ed6a7d6b +title: Step 41 +challengeType: 0 +dashedName: step-41 +--- + +# --description-- + +Use square bracket notation to retrieve the task to be edited from the `taskData` array using the `dataArrIndex`. Then, assign it to the `currentTask` object to keep track of it. + +# --hints-- + +You should assign `taskData[dataArrIndex]` to `currentTask`. + +```js +assert.match(code, /currentTask\s*=\s*taskData\[dataArrIndex\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex((item) => item.id === buttonEl.parentElement.id); + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md new file mode 100644 index 00000000000..73ef19befcc --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md @@ -0,0 +1,389 @@ +--- +id: 64fb1321e189a6136d200f77 +title: Step 42 +challengeType: 0 +dashedName: step-42 +--- + +# --description-- + +The task to be edited is now in the `currentTask` object. Stage it for editing inside the input fields by setting the value of `titleInput` to `currentTask.title`, `dateInput` to `currentTask.date`, and `descriptionInput` to `currentTask.description`. + +# --hints-- + +You should set `titleInput.value` to `currentTask.title`. + +```js +assert.match(editTask.toString(), /titleInput\.value\s*=\s*currentTask\.title;?/) +``` + +You should set `dateInput.value` to `currentTask.date`. + +```js +assert.match(editTask.toString(), /dateInput\.value\s*=\s*currentTask\.date;?/) +``` + +You should set `descriptionInput.value` to `currentTask.description`. + +```js +assert.match(editTask.toString(), /descriptionInput\.value\s*=\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + currentTask = taskData[dataArrIndex]; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md new file mode 100644 index 00000000000..c6312bf6bd0 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md @@ -0,0 +1,386 @@ +--- +id: 64fb1436adef3e145b4c3501 +title: Step 43 +challengeType: 0 +dashedName: step-43 +--- + +# --description-- + +Set the `innerText` of the `addOrUpdateTaskBtn` button to `Update Task` and its `aria-label` to `Update Task` as well. + +# --hints-- + +You should set the inner text of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.innerText\s*=\s*("|')Update Task\1;?/) +``` + +You should set the `ariaLabel` of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.ariaLabel\s*=\s*("|')Update Task\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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; + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md new file mode 100644 index 00000000000..101870d098a --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md @@ -0,0 +1,384 @@ +--- +id: 64fb14d890415c14f93069ce +title: Step 44 +challengeType: 0 +dashedName: step-44 +--- + +# --description-- + +Finally, display the `form` modal with the values of the input fields by using `classList` to toggle the `hidden` class on `taskForm`. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(editTask.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md new file mode 100644 index 00000000000..7ad62060776 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md @@ -0,0 +1,391 @@ +--- +id: 64fb154a7c48cd159924bb18 +title: Step 45 +challengeType: 0 +dashedName: step-45 +--- + +# --description-- + +At this point, editing a task won't reflect when you submit the task. To make the editing functional, go back to the `if` statement inside the `addOrUpdateTask` function. Create an `else` block and set `taskData[dataArrIndex]` to `taskObj`. + +# --hints-- + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*/) +``` + +Your `else` block should have the code `taskData[dataArrIndex] = taskObj`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*taskData\[dataArrIndex\]\s*=\s*taskObj;?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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, + }; + + --fcc-editable-region-- + if (dataArrIndex === -1) { + taskData.unshift(taskObj); + } + + --fcc-editable-region-- + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md new file mode 100644 index 00000000000..4ec33172878 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md @@ -0,0 +1,401 @@ +--- +id: 64fb1c4dc0feb219149a7c7d +title: Step 46 +challengeType: 0 +dashedName: step-46 +--- + +# --description-- + +If the user attempts to edit a task but decides not to make any changes before closing the form, there is no need to display the modal with the `Cancel` and `Discard` buttons. + +Inside the `closeTaskFormBtn` event listener, use `const` to create another variable named `formInputValuesUpdated`. Check if the user made changes while trying to edit a task by verifying that the `titleInput` value **is not equal to** `currentTask.title`, the `dateInput` value **is not equal to** `currentTask.date`, and the `descriptionInput` value **is not equal to** `currentTask.description`. + +# --hints-- + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*/) +``` + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title` or `dateInput.value` is not equal to `currentTask.date`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date/) +``` + +Your `formInputValuesUpdated` variable should have the value `titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date\s*\|\|\s*descriptionInput\.value\s*!==\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + --fcc-editable-region-- + + --fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md new file mode 100644 index 00000000000..bb8a87acf60 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md @@ -0,0 +1,390 @@ +--- +id: 64fb285637fa1e1c222033e3 +title: Step 47 +challengeType: 0 +dashedName: step-47 +--- + +# --description-- + +Now add `formInputValuesUpdated` as the second mandatory condition in the `if` statement using the `AND` operator. + +This way, the `Cancel` and `Discard` buttons in the modal won't be displayed to the user if they haven't made any changes to the input fields while attempting to edit a task. + +# --hints-- + +Your `if` should have the condition `formInputsContainValues && formInputValuesUpdated`. + +```js +assert.match(code.split("if (formInputsContainValues"), /\s*&&\s*formInputValuesUpdated/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description; + +--fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md new file mode 100644 index 00000000000..94fdc40185a --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md @@ -0,0 +1,414 @@ +--- +id: 64fb29348a60361ccd45c1e2 +title: Step 48 +challengeType: 0 +dashedName: step-48 +--- + +# --description-- + +`localStorage` offers methods for saving, retrieving, and deleting items. The items you save can be of any JavaScript data type. + +For instance, the `setItem()` method is used to save an item, and the `getItem()` method retrieves the item. To delete a specific item, you can utilize the `removeItem()` method, or if you want to delete all items in the storage, you can use `clear()`. + +Here's how you can save an item: + +```js +localStorage.setItem("key", value); // value could be string, number, or any other data type +``` + +A `myTaskArr` array has been provided for you. Use the `setItem()` method to save it with a key of `data`. + +After that, open your browser console and go to the `Applications` tab, select `Local Storage`, and the freeCodeCamp domain you see. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +Your `localStorage.setItem()` should have a key of `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*myTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md new file mode 100644 index 00000000000..2ce0b111937 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md @@ -0,0 +1,404 @@ +--- +id: 64fefebad99209211ec30537 +title: Step 49 +challengeType: 0 +dashedName: step-49 +--- + +# --description-- + +If you check the `Application` tab of your browser console, you'll notice a series of `[object Object]`. This is because everything you save in `localStorage` needs to be in string format. + +To resolve the issue, wrap the data you're saving in the `JSON.stringify()` method. Then, check local storage again to observe the results. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +You should wrap `JSON.stringify()` around `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*JSON\.stringify\(myTaskArr\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- +localStorage.setItem("data", myTaskArr); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md new file mode 100644 index 00000000000..1cb0c0c5930 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md @@ -0,0 +1,406 @@ +--- +id: 64ff0313700dad264d19dfe4 +title: Step 50 +challengeType: 0 +dashedName: step-50 +--- + +# --description-- + +Now that you have the `myTaskArr` array saved in `localStorage` correctly, you can retrieve it with `getItem()` by specifying the key you used to save the item. + +Use the `getItem()` method to retrieve the `myTaskArr` array and assign it to the variable `getTaskArr`. Then, log the `getTaskArr` variable to the console to see the result. + +# --hints-- + +You should use `const` to create a `getTaskArr` variable and assign `localStorage.getItem("data")` to it. + +```js +assert.match(code, /const\s*getTaskArr\s*=\s*localStorage\.getItem\(('|")data\1\);?/) +``` + +You should log the `getTaskArr` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md new file mode 100644 index 00000000000..608713897b5 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md @@ -0,0 +1,411 @@ +--- +id: 64ff04cc33779427a6412449 +title: Step 51 +challengeType: 0 +dashedName: step-51 +--- + +# --description-- + +The item you retrieve is a string, as you saved it with `JSON.stringify()`. To view it in its original form before saving, you need to use `JSON.parse()`. + +Use `getItem()` to retrieve the `myTaskArr` array again. This time, wrap it inside `JSON.parse()`, assign it to the variable `getTaskArrObj` and log the `getTaskArrObj` to the console. + +Check the console to see the difference between `getTaskArr` and `getTaskObj`. + +# --hints-- + +You should use `const` to create a `getTaskArrObj` variable and assign it to `JSON.parse(localStorage.getItem('data'));`. + +```js +assert.match(code, /const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\(('|")data\1\)\);?/) +``` + +You should log the `getTaskArrObj` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md new file mode 100644 index 00000000000..09bad9433fa --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md @@ -0,0 +1,406 @@ +--- +id: 64ff068e0426eb288874ed79 +title: Step 52 +challengeType: 0 +dashedName: step-52 +--- + +# --description-- + +You can use `localStorage.removeItem()` to remove a specific item and `localStorage.clear()` to clear all items in the local storage. + +Remove the `data` item from local storage and open the console to observe the result. You should see `null`. + +# --hints-- + +You should use `localStorage.removeItem()` to remove the `data` item from the browser's local storage. + +```js +assert.match(code, /localStorage\.removeItem\(('|")data\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md new file mode 100644 index 00000000000..5dc3bd6e1d1 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md @@ -0,0 +1,413 @@ +--- +id: 64ff23daf176a92de95f24dc +title: Step 53 +challengeType: 0 +dashedName: step-53 +--- + +# --description-- + +Using `localStorage.clear()` won't just delete a single item from local storage but will remove all items. + +Remove `localStorage.removeItem()` and use `localStorage.clear()` instead. You don't need to pass in anything. You should also see `null` in the console. + +# --hints-- + +You should remove `localStorage.removeItem("data")`. + +```js +assert.notMatch(code, /localStorage\.removeItem\(('|")data\1\);/) +``` + +You should remove everything from the browser `local storage` with `localStorage.clear()`. + +```js +assert.match(code, /localStorage\.clear\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- +localStorage.removeItem("data"); + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md new file mode 100644 index 00000000000..0d6c42c989e --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md @@ -0,0 +1,404 @@ +--- +id: 64ff24b80431f62ec6b93f65 +title: Step 54 +challengeType: 0 +dashedName: step-54 +--- + +# --description-- + +Remove the `myTaskArr` array and all of the code for `localStorage` because you don't need them anymore. + +# --hints-- + +You should remove `myTaskArr` and all the code related to `localStorage` that you've just learned. + +```js +assert.notMatch(code, /const\s*myTaskArr\s*=\s*\[\s*\{\s*task:\s"Walk\s*the\s*Dog",\s*date:\s*"22-04-2022"\s*\},\s*\{\s*task:\s"Read\s*some\s*books",\s*date:\s*"02-11-2023"\s*\},\s*\{\s*task:\s"Watch\s*football",\s*date:\s*"10-08-2021"\s*\},\s*\];?\s*localStorage\.setItem\("data", JSON\.stringify\(myTaskArr\)\);\s*localStorage\.clear\(\);?\s*const\s*getTaskArr\s*=\s*localStorage\.getItem\("data"\)\s*console\.log\(getTaskArr\)\s*const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\("data"\)\);?\s*console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +--fcc-editable-region-- +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +localStorage.clear(); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md new file mode 100644 index 00000000000..cbbcd4df6e1 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md @@ -0,0 +1,404 @@ +--- +id: 65003986d17d1e1865b269c0 +title: Step 55 +challengeType: 0 +dashedName: step-55 +--- + +# --description-- + +Now you should save the task items to local storage when the user adds, updates, or removes a task. + +Inside the `addOrUpdateTask` function, use `setItem()` to save the tasks with a key of `data`, then pass the `taskData` array as its parameter. Ensure that you stringify the `taskData`. + +This would persist data once the user adds or updates tasks. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +assert.match(code, /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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; + } + --fcc-editable-region-- + + --fcc-editable-region-- + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md new file mode 100644 index 00000000000..ec3a700096a --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md @@ -0,0 +1,407 @@ +--- +id: 650046832f92c01a35834bca +title: Step 56 +challengeType: 0 +dashedName: step-56 +--- + +# --description-- + +You also want a deleted task to be removed from local storage. For this, you don't need the `removeItem()` or `clear()` methods. Since you already use `splice()` to remove the deleted task from `taskData`, all you need to do now is save `taskData` to local storage again. + +Use `setItem()` to save the `taskData` array again. Pass in `data` as the key and ensure that `taskData` is stringified before saving. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); + --fcc-editable-region-- + + --fcc-editable-region-- +} + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md new file mode 100644 index 00000000000..40cf6384c4e --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md @@ -0,0 +1,392 @@ +--- +id: 650048b0764f9c1b798200e2 +title: Step 57 +challengeType: 0 +dashedName: step-57 +--- + +# --description-- + +If you add, update, or remove a task, it should reflect in the UI. However, that's not happening now because you have yet to retrieve the tasks. To do this, you need to modify your initial `taskData` to be an empty array. + +Set `taskData` to the retrieval of `data` from local storage **or** an empty array. Make sure you parse the data coming with `JSON.parse()` because you saved it as a string. + +# --hints-- + +You should set the `taskData` variable to `JSON.parse(localStorage.getItem("data")) || [];`. + +```js +assert.match(code, /const\s*taskData\s*=\s*JSON.parse\(localStorage\.getItem\(('|")data\1\)\)\s*\|\|\s*\[\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- +const taskData = []; +--fcc-editable-region-- +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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md new file mode 100644 index 00000000000..08ced41b63f --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md @@ -0,0 +1,776 @@ +--- +id: 65004ba581d03d1d5628b41c +title: Step 58 +challengeType: 0 +dashedName: step-58 +--- + +# --description-- + +You've retrieved the task item(s) now, but they still don't reflect in the UI when the page loads. However, they appear when you add a new task. + +To fix this issue, you can check if there's a task inside `taskData`, and then call the `updateTaskContainer()`. + +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 the condition `taskData.length`. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*/) +``` + +You should call the `updateTaskContainer` function in your `if` statement. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*updateTaskContainer\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + taskForm.classList.toggle("hidden"); +} + +const reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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 + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md new file mode 100644 index 00000000000..6b21e6888ce --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md @@ -0,0 +1,314 @@ +--- +id: 650300a25b6f72964ab8aca6 +title: Step 13 +challengeType: 0 +dashedName: step-13 +--- + +# --description-- + +To make the `id` more unique, add another hyphen and use `Date.now()`. + +# --hints-- + +You should attach `-${Date.now()}` to the existing value of the `id` key. Don't forget it needs to be inside the template string. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + const taskObj = { + id: `${titleInput.value.toLowerCase().split(' ').join('-')}`, + }; + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md new file mode 100644 index 00000000000..0c9fbc152b7 --- /dev/null +++ b/curriculum/challenges/arabic/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md @@ -0,0 +1,337 @@ +--- +id: 65099dbd8f137d58e5c0ff16 +title: Step 21 +challengeType: 0 +dashedName: step-21 +--- + +# --description-- + +Create one more `p` element and interpolate the `description` you destructured as the text. Also, create a `strong` element inside the paragraph with the text `Description:`. + +# --hints-- + +You should create a `p` element with `${description}` as the text. + +```js +assert.match(code, /

.*\$\{description\}<\/p>/) +``` + +You should create a `strong` element with the text `Description:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Description:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/arabic/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md b/curriculum/challenges/arabic/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md index 7cce6981cca..b4cada2af12 100644 --- a/curriculum/challenges/arabic/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md +++ b/curriculum/challenges/arabic/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md @@ -12,7 +12,7 @@ This video will help you understand the equations of simple and compound interes Here is the Colab notebook to go along with this video. -Here is an additional Colab notebook that shows you one way to put many of these interest and payment forumlas into Python functions. Also you will see an example of using some formulas to output results, notice a trend, and follow up with other formulas to analyze patterns. +Here is an additional Colab notebook that shows you one way to put many of these interest and payment formulas into Python functions. Also you will see an example of using some formulas to output results, notice a trend, and follow up with other formulas to analyze patterns. # --question-- diff --git a/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md b/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md index 39ab831dad7..144796de1b0 100644 --- a/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md +++ b/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md @@ -71,8 +71,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md b/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md index 53ae7b50350..871e1408079 100644 --- a/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md +++ b/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md @@ -73,7 +73,7 @@ substringDivisibility(5); ```js function substringDivisibility(n) { - function isSubDivisable(digits) { + function isSubDivisible(digits) { const factors = [2, 3, 5, 7, 11, 13, 17]; for (let i = 1; i < digits.length - 2; i++) { @@ -108,17 +108,17 @@ function substringDivisibility(n) { } const allowedDigits = [...new Array(n + 1).keys()]; - const divisablePandigitals = []; + const divisiblePandigitals = []; heapsPermutations( allowedDigits.length, allowedDigits, - isSubDivisable, - divisablePandigitals + isSubDivisible, + divisiblePandigitals ); let sum = 0; - for (let i = 0; i < divisablePandigitals.length; i++) { - sum += divisablePandigitals[i]; + for (let i = 0; i < divisiblePandigitals.length; i++) { + sum += divisiblePandigitals[i]; } return sum; diff --git a/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md b/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md index 7cd5bc67b14..2d583a52355 100644 --- a/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md +++ b/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md @@ -67,8 +67,8 @@ class PrimeSeive { const prime = 2 * i + 3; primes.push(prime); // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } @@ -101,10 +101,10 @@ class PrimeSeive { }; function consecutivePrimeSum(limit) { - // Initalize seive + // Initialize seive const primeSeive = new PrimeSeive(limit); - // Initalize for longest sum < 100 + // Initialize for longest sum < 100 let bestPrime = 41; let bestI = 0; let bestJ = 5; diff --git a/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md b/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md index cdbfc3e568b..5b55ddd8079 100644 --- a/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md +++ b/curriculum/challenges/arabic/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/arabic/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md b/curriculum/challenges/arabic/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md index 78c29aa7952..4fa15980a0d 100644 --- a/curriculum/challenges/arabic/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md +++ b/curriculum/challenges/arabic/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md @@ -31,10 +31,10 @@ For $1 ≤ k ≤ 200$, find $\sum{m(k)}$. # --hints-- -`efficientExponentation()` should return `1582`. +`efficientExponentiation()` should return `1582`. ```js -assert.strictEqual(efficientExponentation(), 1582); +assert.strictEqual(efficientExponentiation(), 1582); ``` # --seed-- @@ -42,12 +42,12 @@ assert.strictEqual(efficientExponentation(), 1582); ## --seed-contents-- ```js -function efficientExponentation() { +function efficientExponentiation() { return true; } -efficientExponentation(); +efficientExponentiation(); ``` # --solutions-- diff --git a/curriculum/challenges/arabic/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md b/curriculum/challenges/arabic/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md index 49d59948762..e402ff347c5 100644 --- a/curriculum/challenges/arabic/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md +++ b/curriculum/challenges/arabic/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/arabic/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md b/curriculum/challenges/arabic/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md index 596f703da28..b843f6f2be2 100644 --- a/curriculum/challenges/arabic/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md +++ b/curriculum/challenges/arabic/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md @@ -35,7 +35,7 @@ concealedSquare(); ```js // Check if n**2 matches the pattern -function squareMatchs(n) { +function squareMatches(n) { // Need BigInt due to size of values let nSquared = (BigInt(n) * BigInt(n)).toString(); @@ -55,8 +55,8 @@ function concealedSquare() { for (let x = maxSquareRoot; x >= minSquareRoot; x -= 10) { // Note: 3*3 = 9 and 7*7 = 49 are only trailing digits // that can produce 9 as trailing digit in square - if (squareMatchs(x + 3)) return (x + 3)*10; - if (squareMatchs(x + 7)) return (x + 7)*10; + if (squareMatches(x + 3)) return (x + 3)*10; + if (squareMatches(x + 7)) return (x + 7)*10; } return -1; } diff --git a/curriculum/challenges/chinese-traditional/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md b/curriculum/challenges/chinese-traditional/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md index 30f40214c57..2d669ab8a2c 100644 --- a/curriculum/challenges/chinese-traditional/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md +++ b/curriculum/challenges/chinese-traditional/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md @@ -54,7 +54,24 @@ assert.match(code, /myStorage\.car\.inside/); 應該用 `const` 聲明 `gloveBoxContents`。 ```js -assert.match(code, /const\s+gloveBoxContents\s*=\s*myStorage\.car\.inside\[\s*("|')glove box\1\s*\]|const\s*{\s*('|")glove box\2:\s*gloveBoxContents\s*}\s*=\s*myStorage\.car\.inside;/); +assert.match(code, /const\s+gloveBoxContents\s*=/); +``` + +You should not change the `myStorage` object. + +```js +const expectedMyStorage = { + "car":{ + "inside":{ + "glove box":"maps", + "passenger seat":"crumbs" + }, + "outside":{ + "trunk":"jack" + } + } +}; +assert.deepStrictEqual(myStorage, expectedMyStorage); ``` # --seed-- diff --git a/curriculum/challenges/chinese-traditional/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md b/curriculum/challenges/chinese-traditional/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md index 510a2d80048..fb19823bd27 100644 --- a/curriculum/challenges/chinese-traditional/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md +++ b/curriculum/challenges/chinese-traditional/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md @@ -10,7 +10,7 @@ dashedName: catch-misspelled-variable-and-function-names `console.log()` 和 `typeof` 方法是檢查中間值和程序輸出類型的兩種主要方法。 現在是時候瞭解一下 bug 出現的常見的情形。 一個語法級別的問題是打字太快帶來的低級拼寫錯誤。 -變量或函數名的錯寫、漏寫或大小寫弄混都會讓瀏覽器嘗試查找並不存在的東西,並報出“引用錯誤”。 JavaScript 變量和函數名稱區分大小寫。 +Transposed, missing, or miscapitalized characters in a variable or function name will have the browser looking for an object that doesn't exist - and complain in the form of a reference error. JavaScript 變量和函數名稱區分大小寫。 # --instructions-- diff --git a/curriculum/challenges/chinese-traditional/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md b/curriculum/challenges/chinese-traditional/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md index 4457789ddae..b61338463fa 100644 --- a/curriculum/challenges/chinese-traditional/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md +++ b/curriculum/challenges/chinese-traditional/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md @@ -58,8 +58,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/issue-tracker.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/issue-tracker.md index 5188f476ae8..f4e3680426f 100644 --- a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/issue-tracker.md +++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/issue-tracker.md @@ -231,13 +231,13 @@ async (getUserInput) => { }; const url = getUserInput('url') + '/api/issues/fcc-project'; const itemToUpdate = await $.post(url, initialData); - const updateSucccess = await $.ajax({ + const updateSuccess = await $.ajax({ url: url, type: 'PUT', data: { _id: itemToUpdate._id, issue_text: 'New Issue Text' } }); - assert.isObject(updateSucccess); - assert.deepEqual(updateSucccess, { + assert.isObject(updateSuccess); + assert.deepEqual(updateSuccess, { result: 'successfully updated', _id: itemToUpdate._id }); diff --git a/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/24-game.md b/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/24-game.md index a6da5861b7e..193b53ba281 100644 --- a/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/24-game.md +++ b/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/24-game.md @@ -82,7 +82,7 @@ const OPERATORS_ = { "/": (a, b) => a / b, } -const PRECIDENCE_ = { +const PRECEDENCE_ = { "+": 1, "-": 1, "*": 2, @@ -114,7 +114,7 @@ function evaluate_(expression) { case "*": case "/": while (stack.length && - PRECIDENCE_[c] <= PRECIDENCE_[stack[stack.length-1]]) { + PRECEDENCE_[c] <= PRECEDENCE_[stack[stack.length-1]]) { postfix += stack.pop(); } stack.push(c); diff --git a/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md b/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md index b6a78ff636a..46b65350213 100644 --- a/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md +++ b/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md @@ -175,7 +175,7 @@ function dealFreeCell(seed) { const rng = FreeCellRNG(seed); const deck = getDeck(); - const deltCards = [[], [], [], [], [], [], []]; + const dealtCards = [[], [], [], [], [], [], []]; let currentColumn = 0; let currentRow = 0; @@ -195,7 +195,7 @@ function dealFreeCell(seed) { card = deck.pop(); // Deal this card - deltCards[currentRow].push(card); + dealtCards[currentRow].push(card); currentColumn += 1; if (currentColumn === 8) { currentColumn = 0; @@ -203,6 +203,6 @@ function dealFreeCell(seed) { } } - return deltCards; + return dealtCards; } ``` diff --git a/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/department-numbers.md b/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/department-numbers.md index 50d9ef1e732..d9216a81055 100644 --- a/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/department-numbers.md +++ b/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/department-numbers.md @@ -101,7 +101,7 @@ function combinations(possibleNumbers, total) { function combinations(possibleNumbers, total) { let firstNumber; let secondNumber; - let thridNumber; + let thirdNumber; const allCombinations = []; for (let i = 0; i < possibleNumbers.length; i += 1) { @@ -112,10 +112,10 @@ function combinations(possibleNumbers, total) { secondNumber = possibleNumbers[j]; if (j !== i && firstNumber + secondNumber <= total) { - thridNumber = total - firstNumber - secondNumber; + thirdNumber = total - firstNumber - secondNumber; - if (thridNumber !== firstNumber && thridNumber !== secondNumber && possibleNumbers.includes(thridNumber)) { - allCombinations.push([firstNumber, secondNumber, thridNumber]); + if (thirdNumber !== firstNumber && thirdNumber !== secondNumber && possibleNumbers.includes(thirdNumber)) { + allCombinations.push([firstNumber, secondNumber, thirdNumber]); } } } diff --git a/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/lu-decomposition.md b/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/lu-decomposition.md index 45759df22aa..9255204dd79 100644 --- a/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/lu-decomposition.md +++ b/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/lu-decomposition.md @@ -80,7 +80,7 @@ $PA = LU$ # --instructions-- -The task is to implement a routine which will take a square nxn matrix $A$ and return a lower triangular matrix $L$, a upper triangular matrix $U$ and a permutation matrix $P$, so that the above equation is fullfilled. The returned value should be in the form `[L, U, P]`. +The task is to implement a routine which will take a square nxn matrix $A$ and return a lower triangular matrix $L$, a upper triangular matrix $U$ and a permutation matrix $P$, so that the above equation is fulfilled. The returned value should be in the form `[L, U, P]`. # --hints-- diff --git a/curriculum/challenges/chinese-traditional/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md b/curriculum/challenges/chinese-traditional/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md index b9c000c01b6..2fa9af8b9e6 100644 --- a/curriculum/challenges/chinese-traditional/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md +++ b/curriculum/challenges/chinese-traditional/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md @@ -9,7 +9,7 @@ dashedName: step-82 企鵝的嘴和腳的 `color` 相同。 -創建一個名爲 `--penguin-picorna` 的新自定義 CSS 變量,並用它替換所有相關的屬性。 +Create a new custom CSS variable named `--penguin-picorna`, and replace all relevant property values with it. # --hints-- diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md index c335f2d7bc9..a1c1c26f120 100644 --- a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md @@ -11,7 +11,7 @@ Inside `pick`, use `let` to initialize a variable named `numbers` and set it to # --hints-- -Your `pick` function should initalize `numbers` to an empty array `[]`. +Your `pick` function should initialize `numbers` to an empty array `[]`. ```js assert.match(pick.toString(), /numbers\s*=\s*\[\]/); diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md new file mode 100644 index 00000000000..4b042aa87ba --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md @@ -0,0 +1,310 @@ +--- +id: 64e4e4c4ec263b62ae7bf54d +title: Step 1 +challengeType: 0 +dashedName: step-1 +--- + +# --description-- + +In this project, you will learn how `localStorage` works in JavaScript by building a Todo app. LocalStorage is a web storage feature of JavaScript that lets you persist data by storing the data as a **key:value** pair. + +The HTML and CSS for this project have been provided for you. Take a look at the files to get yourself familiarized with them. + +Begin by accessing the `task-form`, `confirm-close-dialog`, and `open-task-form-btn` elements with the `getElementById()` method. Save them in the variables `taskForm`, `confirmCloseDialog`, and `openTaskFormBtn`. + +# --hints-- + +You should use `getElementById()` to access the `task-form` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should assign the `task-form` element to the variable `taskForm`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+taskForm\s*=\s*document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should use `getElementById()` to access the `confirm-close-dialog` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should assign the `confirm-close-dialog` element to the variable `confirmCloseDialog`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+confirmCloseDialog\s*=\s*document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should use `getElementById()` to access the `open-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +You should assign the `open-task-form-btn` element to the variable `openTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+openTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md new file mode 100644 index 00000000000..32310409728 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md @@ -0,0 +1,310 @@ +--- +id: 64e4e6c86954de67a3e44ee3 +title: Step 2 +challengeType: 0 +dashedName: step-2 +--- + +# --description-- + +You need to access more elements with the `getElementById()` method. This time you need the `close-task-form-btn`, `add-or-update-task-btn`, and `cancel-btn` elements. Save them in the variables `closeTaskFormBtn`, `addOrUpdateTaskBtn`, and `cancelBtn`. + +# --hints-- + +You should use `getElementById()` to access the `close-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should assign the `close-task-form-btn` element to the variable `closeTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+closeTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `add-or-update-task-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should assign the `add-or-update-task-btn` element to the variable `addOrUpdateTaskBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+addOrUpdateTaskBtn\s*=\s*document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `cancel-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +You should assign the `cancel-btn` element to the variable `cancelBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+cancelBtn\s*=\s*document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md new file mode 100644 index 00000000000..17fc6b76517 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md @@ -0,0 +1,313 @@ +--- +id: 64e4e6fe78b5aa67ef2fc3e7 +title: Step 3 +challengeType: 0 +dashedName: step-3 +--- + +# --description-- + +Next, access the `discard-btn`, `tasks-container`, and `title-input` elements using the `getElementById()` method. Save them in variables named `discardBtn`, `tasksContainer`, and `titleInput`, respectively. + +# --hints-- + +You should use `getElementById()` to access the `discard-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should assign the `discard-btn` element to the variable `discardBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+discardBtn\s*=\s*document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `tasks-container` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should assign the `tasks-container` element to the variable `tasksContainer`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+tasksContainer\s*=\s*document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should use `getElementById()` to access the `title-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +You should assign the `title-input` element to the variable `titleInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+titleInput\s*=\s*document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md new file mode 100644 index 00000000000..6e543d36acc --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md @@ -0,0 +1,304 @@ +--- +id: 64e4e7241f52bb682eeb8211 +title: Step 4 +challengeType: 0 +dashedName: step-4 +--- + +# --description-- + +The last set of elements you need to get from the HTML file are the `date-input` and `description-input` elements. Save them in the variables `dateInput` and `descriptionInput` respectively. + +# --hints-- + +You should use `getElementById()` to access the `date-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should assign the `date-input` element to the variable `dateInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+dateInput\s*=\s*document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should use `getElementById()` to access the `description-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +You should assign the `description-input` element to the variable `descriptionInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+descriptionInput\s*=\s*document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md new file mode 100644 index 00000000000..6f0cd6e9fc4 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md @@ -0,0 +1,296 @@ +--- +id: 64e4e74d0fb4f0687bf4145d +title: Step 5 +challengeType: 0 +dashedName: step-5 +--- + +# --description-- + +Create a `taskData` constant and set it to an empty array. This array will store all the tasks along with their associated data, including title, due date, and description. This storage will enable you to keep track of tasks, display them on the page, and save them to `localStorage`. + +Use `let` to create a `currentTask` variable and set it to an empty object. This variable will be used to track the state when editing and discarding tasks. + +# --hints-- + +You should create a `taskData` constant set to an empty array. + +```js +assert.match(code, /const\s+taskData\s*=\s*\[\];?/) +``` + +You should use `let` to create an empty `currentTask` object. + +```js +assert.match(code, /let\s+currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md new file mode 100644 index 00000000000..7eba2896bf4 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md @@ -0,0 +1,307 @@ +--- +id: 64e4e78a7ea4a168de4e6a38 +title: Step 6 +challengeType: 0 +dashedName: step-6 +--- + +# --description-- + +Now, you will work on opening and closing the form modal. + +Add a `click` event listener to the `openTaskFormBtn` variable, and then use `classList` to toggle the `hidden` class on the `taskForm` element. + +Now you can click on the "Add new Task" button and see the form modal. + +# --hints-- + +You should call the `addEventListener()` method on your `openTaskFormBtn` variable. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax, then use `classList` property and it's `toggle` method on the `taskForm`, to toggle the class `hidden`. Don't use curly braces. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*taskForm\.classList\.toggle\(\1hidden\1\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md new file mode 100644 index 00000000000..6a29158134e --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md @@ -0,0 +1,309 @@ +--- +id: 64e4e7bbedb22d6939001ad3 +title: Step 7 +challengeType: 0 +dashedName: step-7 +--- + +# --description-- + +Add a `click` event listener to the `closeTaskFormBtn` variable, then use the `showModal()` method on your `confirmCloseDialog` variable. This will display a modal with the `Discard` and `Cancel` buttons. + +`showModal()` is a method associated with the HTML `dialog` element. It is used to display a modal dialog box on a web page. + +# --hints-- + +You should call the `addEventListener()` method on your `closeTaskFormBtn` variable. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `showModal()` method on `confirmCloseDialog`. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(["'`]click["'`],\s*\(.*\)\s*=>\s*{?\s*confirmCloseDialog\.showModal\(\);?\s*}?\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md new file mode 100644 index 00000000000..311984043a0 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md @@ -0,0 +1,316 @@ +--- +id: 64e4eaaa9070a66aecbfe603 +title: Step 8 +challengeType: 0 +dashedName: step-8 +--- + +# --description-- + +If the user clicks the `Cancel` button, you want to cancel the process (close the modal showing the two buttons) so the user can continue editing. + +Add a `click` event listener to the `cancelBtn` element, then use the `close()` method on the `confirmCloseDialog` variable. + +`close()` is a method of the window object you can use to close the current window, or a modal you create with the `dialog` element. + +# --hints-- + +You should call the `addEventListener()` method on your `cancelBtn` variable. + +```js +assert.match(code, /cancelBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `close()` method on `confirmCloseDialog`. Don't use curly braces. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*confirmCloseDialog\.close\(\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- + +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md new file mode 100644 index 00000000000..66a40c9771b --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md @@ -0,0 +1,327 @@ +--- +id: 64e4ebc7eabc5a6babd479cd +title: Step 9 +challengeType: 0 +dashedName: step-9 +--- + +# --description-- + +If the user clicks the `Discard` button, you want to close the modal showing the `Cancel` and `Discard` buttons, then hide the form modal. + +Add a click event listener to `discardBtn`, then use the `close()` method on the `confirmCloseDialog` variable. Also, use `classList` to toggle the class `hidden` on `taskForm` so the form modal will close too. + +# --hints-- + +You should call the `addEventListener()` method on your `discardBtn` variable. + +```js +assert.match(code, /discardBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{/) +``` + +Your event listener should use the `close()` method on `confirmCloseDialog`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?/) +``` + +Your event listener should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?\s*taskForm\.classList\.toggle\(\1hidden\1\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md new file mode 100644 index 00000000000..f4002dc0937 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md @@ -0,0 +1,326 @@ +--- +id: 64e4ecd7735a566c9266a338 +title: Step 10 +challengeType: 0 +dashedName: step-10 +--- + +# --description-- + +Now that you've worked on opening and closing the modal, it's time to get the values from the input fields, save them into the `taskData` array, and display them on the page. + +To start, add a `submit` event listener to your `taskForm` element and pass in `e` as the parameter of your arrow function. Inside the curly braces, use the `preventDefault()` method to stop the browser from refreshing the page after submitting the form. + +# --hints-- + +You should call the `addEventListener()` method on your `taskForm` variable. + +```js +assert.match(code, /taskForm\.addEventListener\(/) +``` + +Your event listener should listen for a `submit` event. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces with `e` as the parameter. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{/) +``` + +You should use the `e.preventDefault()` method to stop the browser from reloading the page. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{\s*e\.preventDefault\(\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md new file mode 100644 index 00000000000..148e52f3e93 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md @@ -0,0 +1,337 @@ +--- +id: 64e4eec13546c06d61a63d59 +title: Step 11 +challengeType: 0 +dashedName: step-11 +--- + +# --description-- + +You need to determine whether the task being added already exists or not. If it doesn't exist, you will add it, and if it already exists, you will set it up for editing. You can use the `findIndex()` method to accomplish this. + +`findIndex` is an array method that lets find the index of the first element in an array that satisfies a given testing function. + +Here's an example: + +```js +const numbers = [3, 1, 5, 6, 10, 9, 8]; +const firstEvenNumIndex = numbers.findIndex((num) => num % 2 === 0); + +console.log(firstEvenNumIndex); // Output: 3 – because the first even number (6) is at index 3 +``` + +Declare a `dataArrIndex` variable using `const` and set it to the result of the `findIndex()` method applied to the `taskData` array. Utilize arrow syntax to provide a callback function with `item` as the parameter, and within the callback, check if the `id` property of `item` is equal to the `id` property of `currentTask`. + +If the task exists, this returns the index, and if it doesn't exist, it returns `-1`. + +# --hints-- + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === currentTask.id`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?\s*=>\s*item.id\s*===\s*currentTask.id\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md new file mode 100644 index 00000000000..10826de6f17 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md @@ -0,0 +1,340 @@ +--- +id: 64e4f01d6c72086e016a8626 +title: Step 12 +challengeType: 0 +dashedName: step-12 +--- + +# --description-- + +Next, retrieve the values from the input fields and store them in a `taskObj` object. Each task should also have a unique `id`. + +Create a `taskObj` object with an `id` property as the first property. For the value of the `id` property, retrieve the value of the `titleInput` field, convert it to lowercase, and then use the `split()` and `join()` methods to hyphenate it. + +Make sure all of those are in template literals because you need the `id` property value as a string. + +# --hints-- + +You should use `const` to create a `taskObj` object. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*/) +``` + +Your `taskObj` object should have a key of `id`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id/) +``` + +You should use template strings to get `titleInput.value` as the value of your `id` key and convert it to lowercase. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)/) +``` + +You should use `.split(' ')` on `titleInput.value.toLowerCase()`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)/) +``` + +You should use `.join('-')` on `titleInput.value.toLowerCase().split(' ')`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md new file mode 100644 index 00000000000..764de1b0918 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md @@ -0,0 +1,327 @@ +--- +id: 64ec89ee549ecf802de2b3e2 +title: Step 14 +challengeType: 0 +dashedName: step-14 +--- + +# --description-- + +Retrieve the values from the `titleInput`, `dateInput`, and `descriptionInput` fields, and then save them in the properties `title`, `date`, and `description` of the `taskObj` object. + +# --hints-- + +You should get the value of `titleInput` and save it in a `title` key. + +```js +assert.match(code, /title:\s*titleInput\.value,/) +``` + +You should get the value of `dateInput` and save it in a `date` key. + +```js +assert.match(code, /date:\s*dateInput\.value,/) +``` + +You should get the value of `descriptionInput` and save it in a `description` key. + +```js +assert.match(code, /description:\s*descriptionInput\.value,?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + const taskObj = { + id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`, + --fcc-editable-region-- + + --fcc-editable-region-- + }; +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md new file mode 100644 index 00000000000..3d009188d81 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md @@ -0,0 +1,332 @@ +--- +id: 64ec8f717b261e824d82d6a5 +title: Step 15 +challengeType: 0 +dashedName: step-15 +--- + +# --description-- + +Now that you have obtained the values from the input fields and generated an `id`, you want to add them to your `taskData` array to keep track of each task. However, you should only do this if the task is new. If the task already exists, you will set it up for editing. This is why you have the `dataArrIndex` variable, which provides the index of each task. + +Create an `if` statement with the condition `dataArrIndex === -1`. Within the `if` statement, use the `unshift()` method to add the `taskObj` object to the beginning of the `taskData` array. + +`unshift()` is an array method that is used to add one or more elements to the beginning of an array. + +**N.B:** Try adding a task and log `taskData` to the console to see what it looks like. + +# --hints-- + +You should create an `if` statement with the condition `dataArrIndex === -1`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{/) +``` + +Your `if` statement should have `taskData.unshift(taskObj)` in it's body. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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, + }; + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md new file mode 100644 index 00000000000..f1202bb93fe --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md @@ -0,0 +1,331 @@ +--- +id: 64ec9145e424d8835a4e0f28 +title: Step 16 +challengeType: 0 +dashedName: step-16 +--- + +# --description-- + +Now that you have saved the task in the `taskData` array, you should display the task on the page by looping through it. + +Use `forEach()` on `taskData`, then destructure `id`, `title`, `date`, `description` as the parameters. Don't return anything yet. + +# --hints-- + +You should use `forEach()` on `taskData`. + +```js +assert.match(code, /taskData\.forEach\(\s*/) +``` + +You should destructure `id`, `title`, `date`, `description` as the parameters of the callback of your `forEach()`. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md new file mode 100644 index 00000000000..06bdd7c6ae5 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md @@ -0,0 +1,336 @@ +--- +id: 64ec9282cd547785258cecf2 +title: Step 17 +challengeType: 0 +dashedName: step-17 +--- + +# --description-- + +Using arrow syntax, create an implicit return and use addition assignment to set the `innerHTML` of `tasksContainer` to empty backticks. + +# --hints-- + +You should not alter the existing `taskData.forEach()` and the values you destructured. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +You should use arrow syntax and attach `innerHTML` to `tasksContainer`. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\(\s*tasksContainer\.innerHTML\s*\))?/) +``` + +You should use addition assignment to set the `innerHTML` of `tasksContainer` to an empty pair of backticks. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\s*\(?tasksContainer\.innerHTML\s*\+=\s*`\s*`\)?)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + taskData.forEach(({id, title, date, description})); + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md new file mode 100644 index 00000000000..b5115f99a51 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md @@ -0,0 +1,333 @@ +--- +id: 64ec9343769e8f85c1e17e05 +title: Step 18 +challengeType: 0 +dashedName: step-18 +--- + +# --description-- + +Create a `div` element with the class of `task`. Utilize template strings to set the `id` attribute of the `div` to the `id` you destructured from the task data. + +# --hints-- + +You should create a `div` element with the class `task`. + +```js +assert.match(code, /\s*<\/div>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` + --fcc-editable-region-- + + --fcc-editable-region-- + `) + ); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md new file mode 100644 index 00000000000..81dd3530420 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md @@ -0,0 +1,348 @@ +--- +id: 64ec94f0de20c086e09b0fc3 +title: Step 19 +challengeType: 0 +dashedName: step-19 +--- + +# --description-- + +Create a `p` element and use template strings to set its content to the `title` you destructured. Right before the content of the `p` element, create a `strong` element with the text `Title:`. + +# --hints-- + +You should create a `p` element. + +```js +assert.match(code, /

/) +``` + +You should interpolate `${title}` as the text of your `p` element. + +```js +assert.match(code, /

.*\$\{title\}<\/p>/) +``` + +You should create a `strong` element after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)/) +``` + +Your `strong` element should have the text `Title:`. + +```js +assert.match(code, /(?<=

)Title:\s*<\/strong>\s*/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md new file mode 100644 index 00000000000..4820d100d4d --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md @@ -0,0 +1,336 @@ +--- +id: 64ec959a76336c8767f5cd4d +title: Step 20 +challengeType: 0 +dashedName: step-20 +--- + +# --description-- + +Similarly to the previous step, create another `p` element, and interpolate the `date` you destructured as the text content. Inside this paragraph, create a `strong` element with the text `Date:`. + +# --hints-- + +You should create a `p` element and interpolate `${date}` as the text. + +```js +assert.match(code, /

.*\$\{date\}<\/p>/) +``` + +You should create a `strong` element with the text `Date:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Date:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md new file mode 100644 index 00000000000..4525b0e154c --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md @@ -0,0 +1,340 @@ +--- +id: 64ec96761156a187ed32b274 +title: Step 22 +challengeType: 0 +dashedName: step-22 +--- + +# --description-- + +To allow for task management, you need to include both a delete and an edit button for each task. + +Create two `button` elements with the `type` attribute set to `button` and the `class` attribute set to `btn`. Set the text of the first button to `Edit` and the text of the second button to `Delete`. + +# --hints-- + +You should create a `button` element of type `button`, a class `btn` and `Edit` as the text, in that order. + +```js +assert.match(code, /Edit<\/button/) +``` + +You should create a `button` element of type `button` a class `btn` and `Delete` as the text, in that order. + +```js +assert.match(code, /Delete<\/button/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md new file mode 100644 index 00000000000..0081fa88e1a --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md @@ -0,0 +1,336 @@ +--- +id: 64ec9b10356c2d8aa05d9ce1 +title: Step 23 +challengeType: 0 +dashedName: step-23 +--- + +# --description-- + +After adding the task to the page, you should close the form modal to view the task. To do this, utilize `classList` to toggle the `hidden` class on the `taskForm` element. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +const splitter = code.split("taskData.forEach") +assert.match(splitter[1], /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md new file mode 100644 index 00000000000..6ae1e4dff48 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md @@ -0,0 +1,347 @@ +--- +id: 64ec9c55fdeef78bacd2fc3b +title: Step 24 +challengeType: 0 +dashedName: step-24 +--- + +# --description-- + +If you attempt to add another task now, you'll notice that the input fields retain the values you entered for the previous task. To resolve this, you need to clear the input fields after adding a task. + +Instead of clearing the input fields one by one, it's a good practice to create a function that handles clearing those fields. You can then call this function whenever you need to clear the input fields again. + +Use arrow syntax to create a `reset` function and set it to a pair of curly braces. + +# --hints-- + +You should use `const` and arrow syntax to create a `reset` function. + +```js +assert.match(code, /const\s+reset\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +Your `reset` function should be empty. + +```js +assert.match(reset.toString(), /\(\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md new file mode 100644 index 00000000000..e12fb12ab81 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md @@ -0,0 +1,366 @@ +--- +id: 64fac365aeb8ad70b69b366f +title: Step 25 +challengeType: 0 +dashedName: step-25 +--- + +# --description-- + +Inside the `reset` function, set each value of `titleInput`, `dateInput`, `descriptionInput` to an empty string. + +Also, use `classList` to toggle the class `hidden` on the `taskForm` and set `currentTask` to an empty object. That's because at this point, `currentTask` will be filled with the task the user might have added. + +# --hints-- + +You should set `titleInput.value` to an empty string. + +```js +assert.match(reset.toString(), /titleInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `dateInput.value` to an empty string. + +```js +assert.match(reset.toString(), /dateInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `descriptionInput.value` to an empty string. + +```js +assert.match(reset.toString(), /descriptionInput\.value\s*=\s*('|")\1;?/) +``` + +You should use `classList` to toggle the class `hidden` on `taskForm` + +```js +assert.match(reset.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +You should set `currentTask` to an empty object. + +```js +assert.match(reset.toString(), /currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- +const reset = () => { + +} +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md new file mode 100644 index 00000000000..5f281d0de16 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md @@ -0,0 +1,351 @@ +--- +id: 64fac4d1773e7a719b1254de +title: Step 26 +challengeType: 0 +dashedName: step-26 +--- + +# --description-- + +Remove the existing code toggling the class of `hidden` on `taskForm` and call the `reset` function instead. This would clear the input fields and also hide the form modal for the user to see the added task. + +# --hints-- + +Yous should remove the code toggling the `hidden` class on `taskForm`. + +```js +const splitter = code.split('') +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /reset\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + +--fcc-editable-region-- + taskForm.classList.toggle("hidden"); +--fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md new file mode 100644 index 00000000000..c1a7b0f8965 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md @@ -0,0 +1,351 @@ +--- +id: 64fac6a497811572b338e5e5 +title: Step 27 +challengeType: 0 +dashedName: step-27 +--- + +# --description-- + +Also, remove the existing code toggling the class `hidden` on `taskForm` inside the `discardBtn` event listener and call the `reset` function instead. That's because when you click the `Discard` button, everything in the input fields should go away. + +# --hints-- + +Yous should remove the code toggling the class `hidden` on `taskForm`. + +```js +const splitter = code.split("confirmCloseDialog.close();") +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /confirmCloseDialog\.close\(\);?\s*reset\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); +--fcc-editable-region-- + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md new file mode 100644 index 00000000000..84005e1b01a --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md @@ -0,0 +1,346 @@ +--- +id: 64faca774fd9fd74bc084cc9 +title: Step 28 +challengeType: 0 +dashedName: step-28 +--- + +# --description-- + +You should display the `Cancel` and `Discard` buttons to the user only if there is some text present in the input fields. + +To begin, within the `closeTaskFormBtn` event listener, create a `formInputsContainValues` variable to check if there is a value in the `titleInput` field **or** the `dateInput` field **or** the `descriptionInput` field. + +# --hints-- + +You should use `const` to create a variable `formInputsContainValues` with the value `titleInput.value || dateInput.value || descriptionInput.value;` + +```js +assert.match(code, /const\s*formInputsContainValues\s*=\s*titleInput\.value\s*\|\|\s*dateInput\.value\s*\|\|\s*descriptionInput\.value;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); + +}); +--fcc-editable-region-- + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md new file mode 100644 index 00000000000..07482464ebf --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md @@ -0,0 +1,362 @@ +--- +id: 64facf6180824876f70a2e86 +title: Step 29 +challengeType: 0 +dashedName: step-29 +--- + +# --description-- + +Create an `if` statement to check if `formInputsContainValues` is true. If `formInputsContainValues` is true, indicating that there are changes, use the `showModal()` method on `confirmCloseDialog`. Otherwise, if there are no changes, call the `reset()` function to clear the input fields and hide the form modal. + +# --hints-- + +You should create an `if` statement with the condition `formInputsContainValues`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{/) +``` + +The `if` block of your `if` statement should contain `confirmCloseDialog.showModal();`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?/) +``` + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{/) +``` + +You should call the `reset()` function in the `else` block of your `if` statement. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{\s*reset\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 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; +--fcc-editable-region-- + confirmCloseDialog.showModal(); +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md new file mode 100644 index 00000000000..95b71fba69f --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md @@ -0,0 +1,372 @@ +--- +id: 64fad07f43a101779cb8692a +title: Step 30 +challengeType: 0 +dashedName: step-30 +--- + +# --description-- + +You can enhance code readability and maintainability by refactoring the `submit` event listener into two separate functions. The first function can be used to add the input values to `taskData`, while the second function can be responsible for adding the tasks to the DOM. + +Use arrow syntax to create an `addOrUpdateTask` function. Then move the `dataArrIndex` variable, the `taskObj` object, and the `if` statement into the `addOrUpdateTask` function. + +# --hints-- + +You should use `const` and arrow syntax to create an `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +You should move the `dataArrIndex` variable into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?/) +``` + +You should move the `taskObj` object into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?/) +``` + +You should move the `if` statement with the condition `dataArrIndex === 1` into your `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?\s*if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md new file mode 100644 index 00000000000..8192699bb61 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md @@ -0,0 +1,358 @@ +--- +id: 64fad9cd2eeb1e7ca2ca8c8b +title: Step 31 +challengeType: 0 +dashedName: step-31 +--- + +# --description-- + +Use arrow syntax to create an `updateTaskContainer` function. Then move the `taskData.forEach()` and its content into it. + +# --hints-- + +You should use `const` and arrow syntax to create a `updateTaskContainer` function. + +```js +assert.match(code, /const\s*updateTaskContainer\s*=\s*\(\)\s*=>\s*\{/) +``` + +You should move `taskData.forEach()` and its content into the `updateTaskContainer()` function. + +```js +assert.match(code, /const\s+updateTaskContainer\s+=\s*\(\)\s*=>\s*\{\s*taskData\.forEach\(\s*\(\{\s*id,\s*title,\s*date,\s*description\s*\}\)\s*=>\s*\(tasksContainer\.innerHTML\s*\+=\s*`\s*\s*

Title:<\/strong>\s*\$\{title\}<\/p>\s*

Date:<\/strong>\s*\$\{date\}<\/p>\s*

Description:<\/strong>\s*\$\{description\}<\/p>\s*Edit<\/button>\s*Delete<\/button>\s*<\/div>\s*`\)\s*\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md new file mode 100644 index 00000000000..18b2a462de4 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md @@ -0,0 +1,360 @@ +--- +id: 64fadae4f2d51b7d5d8b98d8 +title: Step 32 +challengeType: 0 +dashedName: step-32 +--- + +# --description-- + +Inside the `addOrUpdateTask` function, call the `updateTaskContainer` and `reset` functions. + +# --hints-- + +You should call the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\)\s*/) +``` + +You should call the `reset` function after calling the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\);?\s*reset\(\);?\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + reset() +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md new file mode 100644 index 00000000000..657bb4aa05b --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md @@ -0,0 +1,355 @@ +--- +id: 64fadff23375f27ff06c6d40 +title: Step 33 +challengeType: 0 +dashedName: step-33 +--- + +# --description-- + +Now remove the `reset()` call inside the `taskForm` submit event listener and call the `addOrUpdateTask` function instead. + +# --hints-- + +You should remove `reset()` and call `addOrUpdateTask()`. + +```js +assert.match(code, /addOrUpdateTask\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + --fcc-editable-region-- + reset() + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md new file mode 100644 index 00000000000..bd759fb7e5f --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md @@ -0,0 +1,364 @@ +--- +id: 64fae068bcdc9c805bd8399e +title: Step 35 +challengeType: 0 +dashedName: step-35 +--- + +# --description-- + +To enable editing and deleting for each task, add an `onclick` attribute to both buttons. Set the value of the `onclick` attribute to `editTask(this)` for the `Edit` button and `deleteTask(this)` for the `Delete` button. The `editTask(this)` function will handle editing, while the `deleteTask(this)` function will handle deletion. + +`this` is a keyword that refers to the current context. In this case, `this` points to the element that triggers the event – the buttons. + +# --hints-- + +You should add `onclick="editTask(this)"` as the first attribute of the edit button. + +```js +assert.match(code, /Edit<\/button>/) +``` + +You should add `onclick="deleteTask(this)"` as the first attribute of the delete button. + +```js +assert.match(code, /Delete<\/button>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + + --fcc-editable-region-- +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md new file mode 100644 index 00000000000..c6fac350818 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md @@ -0,0 +1,371 @@ +--- +id: 64faedcd16a1e985c4c2dc94 +title: Step 36 +challengeType: 0 +dashedName: step-36 +--- + +# --description-- + +Create a `deleteTask` function using arrow syntax. Pass `buttonEl` as the parameter and define an empty set of curly braces for the function body. + +# --hints-- + +You should use `const` and arrow syntax to create a `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `deleteTask` function should take a `buttonEl` parameter. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `deleteTask` function should be empty. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md new file mode 100644 index 00000000000..27631c77f2f --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md @@ -0,0 +1,359 @@ +--- +id: 64faf0418e828c0114a558a7 +title: Step 34 +challengeType: 0 +dashedName: step-34 +--- + +# --description-- + +There's a problem. If you add a task, and then add another, the previous task gets duplicated. This means you need to clear out the existing contents of `tasksContainer` before adding a new task. + +Set the `innerHTML` of `taskContainer` back to an empty string. + + +# --hints-- + +You should set the `innerHTML` of `tasksContainer` to an empty string. + +```js +assert.match(code, /tasksContainer\.innerHTML\s*=\s*("|')\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + --fcc-editable-region-- + + --fcc-editable-region-- + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md new file mode 100644 index 00000000000..9cdbd7860ca --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md @@ -0,0 +1,382 @@ +--- +id: 64faf65b22ad8d07df9be14d +title: Step 37 +challengeType: 0 +dashedName: step-37 +--- + +# --description-- + +You need to find the index of the task you want to delete first. + +Create a `dataArrIndex` variable and set its value using the `findIndex()` method on the `taskData` array. Pass `item` as the parameter for the arrow callback function, and within the callback, check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. Don't use curly braces. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?\s*item\s*\)?\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\s*\);?\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md new file mode 100644 index 00000000000..0e54adadf06 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md @@ -0,0 +1,398 @@ +--- +id: 64faf874364ec308f875f636 +title: Step 38 +challengeType: 0 +dashedName: step-38 +--- + +# --description-- + +You need to remove the task from the DOM using `remove()` and from the `taskData` array using `splice()`. + +`splice()` is an array method that modifies arrays by removing, replacing, or adding elements at a specified index, while also returning the removed elements. It can take up to three parameters: the first one is the mandatory index at which to start, the second is the number of items to remove, and the third is an optional replacement element. Here's an example: + +```js +const fruits = ["mango", "date", "cherry", "banana", "apple"]; + +// Remove date and cherry from the array starting at index 1 +const removedFruits = fruits.splice(1, 2); + +console.log(fruits); // [ 'mango', 'banana', 'apple' ] +console.log(removedFruits); // [ 'date', 'cherry' ] +``` + +Use the `remove()` method to remove the `parentElement` of the `buttonEl` from the DOM. Then use `splice()` to remove the task from the `taskData` array. Pass in `dataArrIndex` and `1` as the parameters of your `splice()`. + +`dataArrIndex` is the index to start and `1` is the number of items to remove. + +# --hints-- + +You should use the `remove()` method to remove the parent element of `buttonEl`. + +```js +assert.match(deleteTask.toString(), /buttonEl\.parentElement\.remove\(\);?/) +``` + +You should use `splice()` on the `taskData` array. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(/) +``` + +The first parameter of your `splice()` method should be `dataArrIndex`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex/) +``` + +The second parameter of your `splice()` method should be `1`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex,\s*1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md new file mode 100644 index 00000000000..3f379d51764 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md @@ -0,0 +1,381 @@ +--- +id: 64fafac95328110a69bcb75f +title: Step 39 +challengeType: 0 +dashedName: step-39 +--- + +# --description-- + +Use arrow syntax to create an `editTask` function. Pass in `buttonEl` as the parameter and add empty curly braces for the body. + +# --hints-- + +You should use `const` and arrow syntax to create an `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `editTask` function should take a `buttonEl` parameter. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `editTask` function should be empty. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md new file mode 100644 index 00000000000..bc0e2a84503 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md @@ -0,0 +1,391 @@ +--- +id: 64fb0fa0968f2b113b2d90e9 +title: Step 40 +challengeType: 0 +dashedName: step-40 +--- + +# --description-- + +As you did in the `deleteTask` function, you need to find the index of the task to be edited. + +Create a `dataArrIndex` variable. For its value, utilize the `findIndex()` method on `taskData`. Pass `item` as the parameter to its callback function and check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md new file mode 100644 index 00000000000..14d527ecf40 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md @@ -0,0 +1,373 @@ +--- +id: 64fb1061ca838611ed6a7d6b +title: Step 41 +challengeType: 0 +dashedName: step-41 +--- + +# --description-- + +Use square bracket notation to retrieve the task to be edited from the `taskData` array using the `dataArrIndex`. Then, assign it to the `currentTask` object to keep track of it. + +# --hints-- + +You should assign `taskData[dataArrIndex]` to `currentTask`. + +```js +assert.match(code, /currentTask\s*=\s*taskData\[dataArrIndex\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex((item) => item.id === buttonEl.parentElement.id); + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md new file mode 100644 index 00000000000..73ef19befcc --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md @@ -0,0 +1,389 @@ +--- +id: 64fb1321e189a6136d200f77 +title: Step 42 +challengeType: 0 +dashedName: step-42 +--- + +# --description-- + +The task to be edited is now in the `currentTask` object. Stage it for editing inside the input fields by setting the value of `titleInput` to `currentTask.title`, `dateInput` to `currentTask.date`, and `descriptionInput` to `currentTask.description`. + +# --hints-- + +You should set `titleInput.value` to `currentTask.title`. + +```js +assert.match(editTask.toString(), /titleInput\.value\s*=\s*currentTask\.title;?/) +``` + +You should set `dateInput.value` to `currentTask.date`. + +```js +assert.match(editTask.toString(), /dateInput\.value\s*=\s*currentTask\.date;?/) +``` + +You should set `descriptionInput.value` to `currentTask.description`. + +```js +assert.match(editTask.toString(), /descriptionInput\.value\s*=\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + currentTask = taskData[dataArrIndex]; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md new file mode 100644 index 00000000000..c6312bf6bd0 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md @@ -0,0 +1,386 @@ +--- +id: 64fb1436adef3e145b4c3501 +title: Step 43 +challengeType: 0 +dashedName: step-43 +--- + +# --description-- + +Set the `innerText` of the `addOrUpdateTaskBtn` button to `Update Task` and its `aria-label` to `Update Task` as well. + +# --hints-- + +You should set the inner text of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.innerText\s*=\s*("|')Update Task\1;?/) +``` + +You should set the `ariaLabel` of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.ariaLabel\s*=\s*("|')Update Task\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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; + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md new file mode 100644 index 00000000000..101870d098a --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md @@ -0,0 +1,384 @@ +--- +id: 64fb14d890415c14f93069ce +title: Step 44 +challengeType: 0 +dashedName: step-44 +--- + +# --description-- + +Finally, display the `form` modal with the values of the input fields by using `classList` to toggle the `hidden` class on `taskForm`. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(editTask.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md new file mode 100644 index 00000000000..7ad62060776 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md @@ -0,0 +1,391 @@ +--- +id: 64fb154a7c48cd159924bb18 +title: Step 45 +challengeType: 0 +dashedName: step-45 +--- + +# --description-- + +At this point, editing a task won't reflect when you submit the task. To make the editing functional, go back to the `if` statement inside the `addOrUpdateTask` function. Create an `else` block and set `taskData[dataArrIndex]` to `taskObj`. + +# --hints-- + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*/) +``` + +Your `else` block should have the code `taskData[dataArrIndex] = taskObj`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*taskData\[dataArrIndex\]\s*=\s*taskObj;?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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, + }; + + --fcc-editable-region-- + if (dataArrIndex === -1) { + taskData.unshift(taskObj); + } + + --fcc-editable-region-- + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md new file mode 100644 index 00000000000..4ec33172878 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md @@ -0,0 +1,401 @@ +--- +id: 64fb1c4dc0feb219149a7c7d +title: Step 46 +challengeType: 0 +dashedName: step-46 +--- + +# --description-- + +If the user attempts to edit a task but decides not to make any changes before closing the form, there is no need to display the modal with the `Cancel` and `Discard` buttons. + +Inside the `closeTaskFormBtn` event listener, use `const` to create another variable named `formInputValuesUpdated`. Check if the user made changes while trying to edit a task by verifying that the `titleInput` value **is not equal to** `currentTask.title`, the `dateInput` value **is not equal to** `currentTask.date`, and the `descriptionInput` value **is not equal to** `currentTask.description`. + +# --hints-- + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*/) +``` + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title` or `dateInput.value` is not equal to `currentTask.date`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date/) +``` + +Your `formInputValuesUpdated` variable should have the value `titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date\s*\|\|\s*descriptionInput\.value\s*!==\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + --fcc-editable-region-- + + --fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md new file mode 100644 index 00000000000..bb8a87acf60 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md @@ -0,0 +1,390 @@ +--- +id: 64fb285637fa1e1c222033e3 +title: Step 47 +challengeType: 0 +dashedName: step-47 +--- + +# --description-- + +Now add `formInputValuesUpdated` as the second mandatory condition in the `if` statement using the `AND` operator. + +This way, the `Cancel` and `Discard` buttons in the modal won't be displayed to the user if they haven't made any changes to the input fields while attempting to edit a task. + +# --hints-- + +Your `if` should have the condition `formInputsContainValues && formInputValuesUpdated`. + +```js +assert.match(code.split("if (formInputsContainValues"), /\s*&&\s*formInputValuesUpdated/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description; + +--fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md new file mode 100644 index 00000000000..94fdc40185a --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md @@ -0,0 +1,414 @@ +--- +id: 64fb29348a60361ccd45c1e2 +title: Step 48 +challengeType: 0 +dashedName: step-48 +--- + +# --description-- + +`localStorage` offers methods for saving, retrieving, and deleting items. The items you save can be of any JavaScript data type. + +For instance, the `setItem()` method is used to save an item, and the `getItem()` method retrieves the item. To delete a specific item, you can utilize the `removeItem()` method, or if you want to delete all items in the storage, you can use `clear()`. + +Here's how you can save an item: + +```js +localStorage.setItem("key", value); // value could be string, number, or any other data type +``` + +A `myTaskArr` array has been provided for you. Use the `setItem()` method to save it with a key of `data`. + +After that, open your browser console and go to the `Applications` tab, select `Local Storage`, and the freeCodeCamp domain you see. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +Your `localStorage.setItem()` should have a key of `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*myTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md new file mode 100644 index 00000000000..2ce0b111937 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md @@ -0,0 +1,404 @@ +--- +id: 64fefebad99209211ec30537 +title: Step 49 +challengeType: 0 +dashedName: step-49 +--- + +# --description-- + +If you check the `Application` tab of your browser console, you'll notice a series of `[object Object]`. This is because everything you save in `localStorage` needs to be in string format. + +To resolve the issue, wrap the data you're saving in the `JSON.stringify()` method. Then, check local storage again to observe the results. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +You should wrap `JSON.stringify()` around `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*JSON\.stringify\(myTaskArr\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- +localStorage.setItem("data", myTaskArr); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md new file mode 100644 index 00000000000..1cb0c0c5930 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md @@ -0,0 +1,406 @@ +--- +id: 64ff0313700dad264d19dfe4 +title: Step 50 +challengeType: 0 +dashedName: step-50 +--- + +# --description-- + +Now that you have the `myTaskArr` array saved in `localStorage` correctly, you can retrieve it with `getItem()` by specifying the key you used to save the item. + +Use the `getItem()` method to retrieve the `myTaskArr` array and assign it to the variable `getTaskArr`. Then, log the `getTaskArr` variable to the console to see the result. + +# --hints-- + +You should use `const` to create a `getTaskArr` variable and assign `localStorage.getItem("data")` to it. + +```js +assert.match(code, /const\s*getTaskArr\s*=\s*localStorage\.getItem\(('|")data\1\);?/) +``` + +You should log the `getTaskArr` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md new file mode 100644 index 00000000000..608713897b5 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md @@ -0,0 +1,411 @@ +--- +id: 64ff04cc33779427a6412449 +title: Step 51 +challengeType: 0 +dashedName: step-51 +--- + +# --description-- + +The item you retrieve is a string, as you saved it with `JSON.stringify()`. To view it in its original form before saving, you need to use `JSON.parse()`. + +Use `getItem()` to retrieve the `myTaskArr` array again. This time, wrap it inside `JSON.parse()`, assign it to the variable `getTaskArrObj` and log the `getTaskArrObj` to the console. + +Check the console to see the difference between `getTaskArr` and `getTaskObj`. + +# --hints-- + +You should use `const` to create a `getTaskArrObj` variable and assign it to `JSON.parse(localStorage.getItem('data'));`. + +```js +assert.match(code, /const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\(('|")data\1\)\);?/) +``` + +You should log the `getTaskArrObj` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md new file mode 100644 index 00000000000..09bad9433fa --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md @@ -0,0 +1,406 @@ +--- +id: 64ff068e0426eb288874ed79 +title: Step 52 +challengeType: 0 +dashedName: step-52 +--- + +# --description-- + +You can use `localStorage.removeItem()` to remove a specific item and `localStorage.clear()` to clear all items in the local storage. + +Remove the `data` item from local storage and open the console to observe the result. You should see `null`. + +# --hints-- + +You should use `localStorage.removeItem()` to remove the `data` item from the browser's local storage. + +```js +assert.match(code, /localStorage\.removeItem\(('|")data\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md new file mode 100644 index 00000000000..5dc3bd6e1d1 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md @@ -0,0 +1,413 @@ +--- +id: 64ff23daf176a92de95f24dc +title: Step 53 +challengeType: 0 +dashedName: step-53 +--- + +# --description-- + +Using `localStorage.clear()` won't just delete a single item from local storage but will remove all items. + +Remove `localStorage.removeItem()` and use `localStorage.clear()` instead. You don't need to pass in anything. You should also see `null` in the console. + +# --hints-- + +You should remove `localStorage.removeItem("data")`. + +```js +assert.notMatch(code, /localStorage\.removeItem\(('|")data\1\);/) +``` + +You should remove everything from the browser `local storage` with `localStorage.clear()`. + +```js +assert.match(code, /localStorage\.clear\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- +localStorage.removeItem("data"); + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md new file mode 100644 index 00000000000..0d6c42c989e --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md @@ -0,0 +1,404 @@ +--- +id: 64ff24b80431f62ec6b93f65 +title: Step 54 +challengeType: 0 +dashedName: step-54 +--- + +# --description-- + +Remove the `myTaskArr` array and all of the code for `localStorage` because you don't need them anymore. + +# --hints-- + +You should remove `myTaskArr` and all the code related to `localStorage` that you've just learned. + +```js +assert.notMatch(code, /const\s*myTaskArr\s*=\s*\[\s*\{\s*task:\s"Walk\s*the\s*Dog",\s*date:\s*"22-04-2022"\s*\},\s*\{\s*task:\s"Read\s*some\s*books",\s*date:\s*"02-11-2023"\s*\},\s*\{\s*task:\s"Watch\s*football",\s*date:\s*"10-08-2021"\s*\},\s*\];?\s*localStorage\.setItem\("data", JSON\.stringify\(myTaskArr\)\);\s*localStorage\.clear\(\);?\s*const\s*getTaskArr\s*=\s*localStorage\.getItem\("data"\)\s*console\.log\(getTaskArr\)\s*const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\("data"\)\);?\s*console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +--fcc-editable-region-- +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +localStorage.clear(); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md new file mode 100644 index 00000000000..cbbcd4df6e1 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md @@ -0,0 +1,404 @@ +--- +id: 65003986d17d1e1865b269c0 +title: Step 55 +challengeType: 0 +dashedName: step-55 +--- + +# --description-- + +Now you should save the task items to local storage when the user adds, updates, or removes a task. + +Inside the `addOrUpdateTask` function, use `setItem()` to save the tasks with a key of `data`, then pass the `taskData` array as its parameter. Ensure that you stringify the `taskData`. + +This would persist data once the user adds or updates tasks. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +assert.match(code, /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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; + } + --fcc-editable-region-- + + --fcc-editable-region-- + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md new file mode 100644 index 00000000000..ec3a700096a --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md @@ -0,0 +1,407 @@ +--- +id: 650046832f92c01a35834bca +title: Step 56 +challengeType: 0 +dashedName: step-56 +--- + +# --description-- + +You also want a deleted task to be removed from local storage. For this, you don't need the `removeItem()` or `clear()` methods. Since you already use `splice()` to remove the deleted task from `taskData`, all you need to do now is save `taskData` to local storage again. + +Use `setItem()` to save the `taskData` array again. Pass in `data` as the key and ensure that `taskData` is stringified before saving. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); + --fcc-editable-region-- + + --fcc-editable-region-- +} + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md new file mode 100644 index 00000000000..40cf6384c4e --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md @@ -0,0 +1,392 @@ +--- +id: 650048b0764f9c1b798200e2 +title: Step 57 +challengeType: 0 +dashedName: step-57 +--- + +# --description-- + +If you add, update, or remove a task, it should reflect in the UI. However, that's not happening now because you have yet to retrieve the tasks. To do this, you need to modify your initial `taskData` to be an empty array. + +Set `taskData` to the retrieval of `data` from local storage **or** an empty array. Make sure you parse the data coming with `JSON.parse()` because you saved it as a string. + +# --hints-- + +You should set the `taskData` variable to `JSON.parse(localStorage.getItem("data")) || [];`. + +```js +assert.match(code, /const\s*taskData\s*=\s*JSON.parse\(localStorage\.getItem\(('|")data\1\)\)\s*\|\|\s*\[\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- +const taskData = []; +--fcc-editable-region-- +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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md new file mode 100644 index 00000000000..08ced41b63f --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md @@ -0,0 +1,776 @@ +--- +id: 65004ba581d03d1d5628b41c +title: Step 58 +challengeType: 0 +dashedName: step-58 +--- + +# --description-- + +You've retrieved the task item(s) now, but they still don't reflect in the UI when the page loads. However, they appear when you add a new task. + +To fix this issue, you can check if there's a task inside `taskData`, and then call the `updateTaskContainer()`. + +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 the condition `taskData.length`. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*/) +``` + +You should call the `updateTaskContainer` function in your `if` statement. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*updateTaskContainer\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + taskForm.classList.toggle("hidden"); +} + +const reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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 + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md new file mode 100644 index 00000000000..6b21e6888ce --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md @@ -0,0 +1,314 @@ +--- +id: 650300a25b6f72964ab8aca6 +title: Step 13 +challengeType: 0 +dashedName: step-13 +--- + +# --description-- + +To make the `id` more unique, add another hyphen and use `Date.now()`. + +# --hints-- + +You should attach `-${Date.now()}` to the existing value of the `id` key. Don't forget it needs to be inside the template string. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + const taskObj = { + id: `${titleInput.value.toLowerCase().split(' ').join('-')}`, + }; + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md new file mode 100644 index 00000000000..0c9fbc152b7 --- /dev/null +++ b/curriculum/challenges/chinese-traditional/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md @@ -0,0 +1,337 @@ +--- +id: 65099dbd8f137d58e5c0ff16 +title: Step 21 +challengeType: 0 +dashedName: step-21 +--- + +# --description-- + +Create one more `p` element and interpolate the `description` you destructured as the text. Also, create a `strong` element inside the paragraph with the text `Description:`. + +# --hints-- + +You should create a `p` element with `${description}` as the text. + +```js +assert.match(code, /

.*\$\{description\}<\/p>/) +``` + +You should create a `strong` element with the text `Description:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Description:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/chinese-traditional/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md b/curriculum/challenges/chinese-traditional/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md index 7277ab3af1c..175c6b8e430 100644 --- a/curriculum/challenges/chinese-traditional/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md +++ b/curriculum/challenges/chinese-traditional/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md @@ -12,7 +12,7 @@ dashedName: simple-and-compound-interest 這是和這個視頻配套的 Colab 筆記本。 -這裏有一個額外的 Colab 筆記本,它向你展示了將許多這些利息和付款公式圖放入 Python 函數的一種方法。同時你將看到一個使用一些公式輸出結果的例子,注意一個趨勢,並學習其他公式來分析模式。 +Here is an additional Colab notebook that shows you one way to put many of these interest and payment formulas into Python functions. Also you will see an example of using some formulas to output results, notice a trend, and follow up with other formulas to analyze patterns. # --question-- diff --git a/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md b/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md index 39ab831dad7..144796de1b0 100644 --- a/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md +++ b/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md @@ -71,8 +71,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md b/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md index 53ae7b50350..871e1408079 100644 --- a/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md +++ b/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md @@ -73,7 +73,7 @@ substringDivisibility(5); ```js function substringDivisibility(n) { - function isSubDivisable(digits) { + function isSubDivisible(digits) { const factors = [2, 3, 5, 7, 11, 13, 17]; for (let i = 1; i < digits.length - 2; i++) { @@ -108,17 +108,17 @@ function substringDivisibility(n) { } const allowedDigits = [...new Array(n + 1).keys()]; - const divisablePandigitals = []; + const divisiblePandigitals = []; heapsPermutations( allowedDigits.length, allowedDigits, - isSubDivisable, - divisablePandigitals + isSubDivisible, + divisiblePandigitals ); let sum = 0; - for (let i = 0; i < divisablePandigitals.length; i++) { - sum += divisablePandigitals[i]; + for (let i = 0; i < divisiblePandigitals.length; i++) { + sum += divisiblePandigitals[i]; } return sum; diff --git a/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md b/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md index 7cd5bc67b14..2d583a52355 100644 --- a/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md +++ b/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md @@ -67,8 +67,8 @@ class PrimeSeive { const prime = 2 * i + 3; primes.push(prime); // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } @@ -101,10 +101,10 @@ class PrimeSeive { }; function consecutivePrimeSum(limit) { - // Initalize seive + // Initialize seive const primeSeive = new PrimeSeive(limit); - // Initalize for longest sum < 100 + // Initialize for longest sum < 100 let bestPrime = 41; let bestI = 0; let bestJ = 5; diff --git a/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md b/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md index cdbfc3e568b..5b55ddd8079 100644 --- a/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md +++ b/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md b/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md index 78c29aa7952..4fa15980a0d 100644 --- a/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md +++ b/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md @@ -31,10 +31,10 @@ For $1 ≤ k ≤ 200$, find $\sum{m(k)}$. # --hints-- -`efficientExponentation()` should return `1582`. +`efficientExponentiation()` should return `1582`. ```js -assert.strictEqual(efficientExponentation(), 1582); +assert.strictEqual(efficientExponentiation(), 1582); ``` # --seed-- @@ -42,12 +42,12 @@ assert.strictEqual(efficientExponentation(), 1582); ## --seed-contents-- ```js -function efficientExponentation() { +function efficientExponentiation() { return true; } -efficientExponentation(); +efficientExponentiation(); ``` # --solutions-- diff --git a/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md b/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md index 49d59948762..e402ff347c5 100644 --- a/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md +++ b/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md b/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md index 596f703da28..b843f6f2be2 100644 --- a/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md +++ b/curriculum/challenges/chinese-traditional/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md @@ -35,7 +35,7 @@ concealedSquare(); ```js // Check if n**2 matches the pattern -function squareMatchs(n) { +function squareMatches(n) { // Need BigInt due to size of values let nSquared = (BigInt(n) * BigInt(n)).toString(); @@ -55,8 +55,8 @@ function concealedSquare() { for (let x = maxSquareRoot; x >= minSquareRoot; x -= 10) { // Note: 3*3 = 9 and 7*7 = 49 are only trailing digits // that can produce 9 as trailing digit in square - if (squareMatchs(x + 3)) return (x + 3)*10; - if (squareMatchs(x + 7)) return (x + 7)*10; + if (squareMatches(x + 3)) return (x + 3)*10; + if (squareMatches(x + 7)) return (x + 7)*10; } return -1; } diff --git a/curriculum/challenges/chinese/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md b/curriculum/challenges/chinese/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md index f07b62114a2..1533634a15d 100644 --- a/curriculum/challenges/chinese/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md +++ b/curriculum/challenges/chinese/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md @@ -54,7 +54,24 @@ assert.match(code, /myStorage\.car\.inside/); 应该用 `const` 声明 `gloveBoxContents`。 ```js -assert.match(code, /const\s+gloveBoxContents\s*=\s*myStorage\.car\.inside\[\s*("|')glove box\1\s*\]|const\s*{\s*('|")glove box\2:\s*gloveBoxContents\s*}\s*=\s*myStorage\.car\.inside;/); +assert.match(code, /const\s+gloveBoxContents\s*=/); +``` + +You should not change the `myStorage` object. + +```js +const expectedMyStorage = { + "car":{ + "inside":{ + "glove box":"maps", + "passenger seat":"crumbs" + }, + "outside":{ + "trunk":"jack" + } + } +}; +assert.deepStrictEqual(myStorage, expectedMyStorage); ``` # --seed-- diff --git a/curriculum/challenges/chinese/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md b/curriculum/challenges/chinese/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md index a58e2bb6e3e..3e0bdf2e154 100644 --- a/curriculum/challenges/chinese/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md +++ b/curriculum/challenges/chinese/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md @@ -10,7 +10,7 @@ dashedName: catch-misspelled-variable-and-function-names `console.log()` 和 `typeof` 方法是检查中间值和程序输出类型的两种主要方法。 现在是时候了解一下 bug 出现的常见的情形。 一个语法级别的问题是打字太快带来的低级拼写错误。 -变量或函数名的错写、漏写或大小写弄混都会让浏览器尝试查找并不存在的东西,并报出“引用错误”。 JavaScript 变量和函数名称区分大小写。 +Transposed, missing, or miscapitalized characters in a variable or function name will have the browser looking for an object that doesn't exist - and complain in the form of a reference error. JavaScript 变量和函数名称区分大小写。 # --instructions-- diff --git a/curriculum/challenges/chinese/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md b/curriculum/challenges/chinese/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md index 6c6b92d2310..5be0aa61230 100644 --- a/curriculum/challenges/chinese/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md +++ b/curriculum/challenges/chinese/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md @@ -58,8 +58,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/chinese/06-quality-assurance/quality-assurance-projects/issue-tracker.md b/curriculum/challenges/chinese/06-quality-assurance/quality-assurance-projects/issue-tracker.md index 54fa9e1d5c4..4fd3b5d90c7 100644 --- a/curriculum/challenges/chinese/06-quality-assurance/quality-assurance-projects/issue-tracker.md +++ b/curriculum/challenges/chinese/06-quality-assurance/quality-assurance-projects/issue-tracker.md @@ -231,13 +231,13 @@ async (getUserInput) => { }; const url = getUserInput('url') + '/api/issues/fcc-project'; const itemToUpdate = await $.post(url, initialData); - const updateSucccess = await $.ajax({ + const updateSuccess = await $.ajax({ url: url, type: 'PUT', data: { _id: itemToUpdate._id, issue_text: 'New Issue Text' } }); - assert.isObject(updateSucccess); - assert.deepEqual(updateSucccess, { + assert.isObject(updateSuccess); + assert.deepEqual(updateSuccess, { result: 'successfully updated', _id: itemToUpdate._id }); diff --git a/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/24-game.md b/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/24-game.md index 589bc792418..855e0411f4c 100644 --- a/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/24-game.md +++ b/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/24-game.md @@ -82,7 +82,7 @@ const OPERATORS_ = { "/": (a, b) => a / b, } -const PRECIDENCE_ = { +const PRECEDENCE_ = { "+": 1, "-": 1, "*": 2, @@ -114,7 +114,7 @@ function evaluate_(expression) { case "*": case "/": while (stack.length && - PRECIDENCE_[c] <= PRECIDENCE_[stack[stack.length-1]]) { + PRECEDENCE_[c] <= PRECEDENCE_[stack[stack.length-1]]) { postfix += stack.pop(); } stack.push(c); diff --git a/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md b/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md index b6a78ff636a..46b65350213 100644 --- a/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md +++ b/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md @@ -175,7 +175,7 @@ function dealFreeCell(seed) { const rng = FreeCellRNG(seed); const deck = getDeck(); - const deltCards = [[], [], [], [], [], [], []]; + const dealtCards = [[], [], [], [], [], [], []]; let currentColumn = 0; let currentRow = 0; @@ -195,7 +195,7 @@ function dealFreeCell(seed) { card = deck.pop(); // Deal this card - deltCards[currentRow].push(card); + dealtCards[currentRow].push(card); currentColumn += 1; if (currentColumn === 8) { currentColumn = 0; @@ -203,6 +203,6 @@ function dealFreeCell(seed) { } } - return deltCards; + return dealtCards; } ``` diff --git a/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/department-numbers.md b/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/department-numbers.md index cffc60a8ab3..df00a6e693d 100644 --- a/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/department-numbers.md +++ b/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/department-numbers.md @@ -101,7 +101,7 @@ function combinations(possibleNumbers, total) { function combinations(possibleNumbers, total) { let firstNumber; let secondNumber; - let thridNumber; + let thirdNumber; const allCombinations = []; for (let i = 0; i < possibleNumbers.length; i += 1) { @@ -112,10 +112,10 @@ function combinations(possibleNumbers, total) { secondNumber = possibleNumbers[j]; if (j !== i && firstNumber + secondNumber <= total) { - thridNumber = total - firstNumber - secondNumber; + thirdNumber = total - firstNumber - secondNumber; - if (thridNumber !== firstNumber && thridNumber !== secondNumber && possibleNumbers.includes(thridNumber)) { - allCombinations.push([firstNumber, secondNumber, thridNumber]); + if (thirdNumber !== firstNumber && thirdNumber !== secondNumber && possibleNumbers.includes(thirdNumber)) { + allCombinations.push([firstNumber, secondNumber, thirdNumber]); } } } diff --git a/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/lu-decomposition.md b/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/lu-decomposition.md index 45759df22aa..9255204dd79 100644 --- a/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/lu-decomposition.md +++ b/curriculum/challenges/chinese/10-coding-interview-prep/rosetta-code/lu-decomposition.md @@ -80,7 +80,7 @@ $PA = LU$ # --instructions-- -The task is to implement a routine which will take a square nxn matrix $A$ and return a lower triangular matrix $L$, a upper triangular matrix $U$ and a permutation matrix $P$, so that the above equation is fullfilled. The returned value should be in the form `[L, U, P]`. +The task is to implement a routine which will take a square nxn matrix $A$ and return a lower triangular matrix $L$, a upper triangular matrix $U$ and a permutation matrix $P$, so that the above equation is fulfilled. The returned value should be in the form `[L, U, P]`. # --hints-- diff --git a/curriculum/challenges/chinese/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md b/curriculum/challenges/chinese/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md index 8670e841b30..265ac261a8b 100644 --- a/curriculum/challenges/chinese/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md +++ b/curriculum/challenges/chinese/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md @@ -9,7 +9,7 @@ dashedName: step-82 企鹅的嘴和脚的 `color` 相同。 -创建一个名为 `--penguin-picorna` 的新自定义 CSS 变量,并用它替换所有相关的属性。 +Create a new custom CSS variable named `--penguin-picorna`, and replace all relevant property values with it. # --hints-- diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md index c335f2d7bc9..a1c1c26f120 100644 --- a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md @@ -11,7 +11,7 @@ Inside `pick`, use `let` to initialize a variable named `numbers` and set it to # --hints-- -Your `pick` function should initalize `numbers` to an empty array `[]`. +Your `pick` function should initialize `numbers` to an empty array `[]`. ```js assert.match(pick.toString(), /numbers\s*=\s*\[\]/); diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md new file mode 100644 index 00000000000..4b042aa87ba --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md @@ -0,0 +1,310 @@ +--- +id: 64e4e4c4ec263b62ae7bf54d +title: Step 1 +challengeType: 0 +dashedName: step-1 +--- + +# --description-- + +In this project, you will learn how `localStorage` works in JavaScript by building a Todo app. LocalStorage is a web storage feature of JavaScript that lets you persist data by storing the data as a **key:value** pair. + +The HTML and CSS for this project have been provided for you. Take a look at the files to get yourself familiarized with them. + +Begin by accessing the `task-form`, `confirm-close-dialog`, and `open-task-form-btn` elements with the `getElementById()` method. Save them in the variables `taskForm`, `confirmCloseDialog`, and `openTaskFormBtn`. + +# --hints-- + +You should use `getElementById()` to access the `task-form` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should assign the `task-form` element to the variable `taskForm`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+taskForm\s*=\s*document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should use `getElementById()` to access the `confirm-close-dialog` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should assign the `confirm-close-dialog` element to the variable `confirmCloseDialog`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+confirmCloseDialog\s*=\s*document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should use `getElementById()` to access the `open-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +You should assign the `open-task-form-btn` element to the variable `openTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+openTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md new file mode 100644 index 00000000000..32310409728 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md @@ -0,0 +1,310 @@ +--- +id: 64e4e6c86954de67a3e44ee3 +title: Step 2 +challengeType: 0 +dashedName: step-2 +--- + +# --description-- + +You need to access more elements with the `getElementById()` method. This time you need the `close-task-form-btn`, `add-or-update-task-btn`, and `cancel-btn` elements. Save them in the variables `closeTaskFormBtn`, `addOrUpdateTaskBtn`, and `cancelBtn`. + +# --hints-- + +You should use `getElementById()` to access the `close-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should assign the `close-task-form-btn` element to the variable `closeTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+closeTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `add-or-update-task-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should assign the `add-or-update-task-btn` element to the variable `addOrUpdateTaskBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+addOrUpdateTaskBtn\s*=\s*document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `cancel-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +You should assign the `cancel-btn` element to the variable `cancelBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+cancelBtn\s*=\s*document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md new file mode 100644 index 00000000000..17fc6b76517 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md @@ -0,0 +1,313 @@ +--- +id: 64e4e6fe78b5aa67ef2fc3e7 +title: Step 3 +challengeType: 0 +dashedName: step-3 +--- + +# --description-- + +Next, access the `discard-btn`, `tasks-container`, and `title-input` elements using the `getElementById()` method. Save them in variables named `discardBtn`, `tasksContainer`, and `titleInput`, respectively. + +# --hints-- + +You should use `getElementById()` to access the `discard-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should assign the `discard-btn` element to the variable `discardBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+discardBtn\s*=\s*document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `tasks-container` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should assign the `tasks-container` element to the variable `tasksContainer`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+tasksContainer\s*=\s*document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should use `getElementById()` to access the `title-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +You should assign the `title-input` element to the variable `titleInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+titleInput\s*=\s*document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md new file mode 100644 index 00000000000..6e543d36acc --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md @@ -0,0 +1,304 @@ +--- +id: 64e4e7241f52bb682eeb8211 +title: Step 4 +challengeType: 0 +dashedName: step-4 +--- + +# --description-- + +The last set of elements you need to get from the HTML file are the `date-input` and `description-input` elements. Save them in the variables `dateInput` and `descriptionInput` respectively. + +# --hints-- + +You should use `getElementById()` to access the `date-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should assign the `date-input` element to the variable `dateInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+dateInput\s*=\s*document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should use `getElementById()` to access the `description-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +You should assign the `description-input` element to the variable `descriptionInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+descriptionInput\s*=\s*document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md new file mode 100644 index 00000000000..6f0cd6e9fc4 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md @@ -0,0 +1,296 @@ +--- +id: 64e4e74d0fb4f0687bf4145d +title: Step 5 +challengeType: 0 +dashedName: step-5 +--- + +# --description-- + +Create a `taskData` constant and set it to an empty array. This array will store all the tasks along with their associated data, including title, due date, and description. This storage will enable you to keep track of tasks, display them on the page, and save them to `localStorage`. + +Use `let` to create a `currentTask` variable and set it to an empty object. This variable will be used to track the state when editing and discarding tasks. + +# --hints-- + +You should create a `taskData` constant set to an empty array. + +```js +assert.match(code, /const\s+taskData\s*=\s*\[\];?/) +``` + +You should use `let` to create an empty `currentTask` object. + +```js +assert.match(code, /let\s+currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md new file mode 100644 index 00000000000..7eba2896bf4 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md @@ -0,0 +1,307 @@ +--- +id: 64e4e78a7ea4a168de4e6a38 +title: Step 6 +challengeType: 0 +dashedName: step-6 +--- + +# --description-- + +Now, you will work on opening and closing the form modal. + +Add a `click` event listener to the `openTaskFormBtn` variable, and then use `classList` to toggle the `hidden` class on the `taskForm` element. + +Now you can click on the "Add new Task" button and see the form modal. + +# --hints-- + +You should call the `addEventListener()` method on your `openTaskFormBtn` variable. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax, then use `classList` property and it's `toggle` method on the `taskForm`, to toggle the class `hidden`. Don't use curly braces. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*taskForm\.classList\.toggle\(\1hidden\1\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md new file mode 100644 index 00000000000..6a29158134e --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md @@ -0,0 +1,309 @@ +--- +id: 64e4e7bbedb22d6939001ad3 +title: Step 7 +challengeType: 0 +dashedName: step-7 +--- + +# --description-- + +Add a `click` event listener to the `closeTaskFormBtn` variable, then use the `showModal()` method on your `confirmCloseDialog` variable. This will display a modal with the `Discard` and `Cancel` buttons. + +`showModal()` is a method associated with the HTML `dialog` element. It is used to display a modal dialog box on a web page. + +# --hints-- + +You should call the `addEventListener()` method on your `closeTaskFormBtn` variable. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `showModal()` method on `confirmCloseDialog`. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(["'`]click["'`],\s*\(.*\)\s*=>\s*{?\s*confirmCloseDialog\.showModal\(\);?\s*}?\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md new file mode 100644 index 00000000000..311984043a0 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md @@ -0,0 +1,316 @@ +--- +id: 64e4eaaa9070a66aecbfe603 +title: Step 8 +challengeType: 0 +dashedName: step-8 +--- + +# --description-- + +If the user clicks the `Cancel` button, you want to cancel the process (close the modal showing the two buttons) so the user can continue editing. + +Add a `click` event listener to the `cancelBtn` element, then use the `close()` method on the `confirmCloseDialog` variable. + +`close()` is a method of the window object you can use to close the current window, or a modal you create with the `dialog` element. + +# --hints-- + +You should call the `addEventListener()` method on your `cancelBtn` variable. + +```js +assert.match(code, /cancelBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `close()` method on `confirmCloseDialog`. Don't use curly braces. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*confirmCloseDialog\.close\(\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- + +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md new file mode 100644 index 00000000000..66a40c9771b --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md @@ -0,0 +1,327 @@ +--- +id: 64e4ebc7eabc5a6babd479cd +title: Step 9 +challengeType: 0 +dashedName: step-9 +--- + +# --description-- + +If the user clicks the `Discard` button, you want to close the modal showing the `Cancel` and `Discard` buttons, then hide the form modal. + +Add a click event listener to `discardBtn`, then use the `close()` method on the `confirmCloseDialog` variable. Also, use `classList` to toggle the class `hidden` on `taskForm` so the form modal will close too. + +# --hints-- + +You should call the `addEventListener()` method on your `discardBtn` variable. + +```js +assert.match(code, /discardBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{/) +``` + +Your event listener should use the `close()` method on `confirmCloseDialog`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?/) +``` + +Your event listener should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?\s*taskForm\.classList\.toggle\(\1hidden\1\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md new file mode 100644 index 00000000000..f4002dc0937 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md @@ -0,0 +1,326 @@ +--- +id: 64e4ecd7735a566c9266a338 +title: Step 10 +challengeType: 0 +dashedName: step-10 +--- + +# --description-- + +Now that you've worked on opening and closing the modal, it's time to get the values from the input fields, save them into the `taskData` array, and display them on the page. + +To start, add a `submit` event listener to your `taskForm` element and pass in `e` as the parameter of your arrow function. Inside the curly braces, use the `preventDefault()` method to stop the browser from refreshing the page after submitting the form. + +# --hints-- + +You should call the `addEventListener()` method on your `taskForm` variable. + +```js +assert.match(code, /taskForm\.addEventListener\(/) +``` + +Your event listener should listen for a `submit` event. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces with `e` as the parameter. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{/) +``` + +You should use the `e.preventDefault()` method to stop the browser from reloading the page. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{\s*e\.preventDefault\(\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md new file mode 100644 index 00000000000..148e52f3e93 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md @@ -0,0 +1,337 @@ +--- +id: 64e4eec13546c06d61a63d59 +title: Step 11 +challengeType: 0 +dashedName: step-11 +--- + +# --description-- + +You need to determine whether the task being added already exists or not. If it doesn't exist, you will add it, and if it already exists, you will set it up for editing. You can use the `findIndex()` method to accomplish this. + +`findIndex` is an array method that lets find the index of the first element in an array that satisfies a given testing function. + +Here's an example: + +```js +const numbers = [3, 1, 5, 6, 10, 9, 8]; +const firstEvenNumIndex = numbers.findIndex((num) => num % 2 === 0); + +console.log(firstEvenNumIndex); // Output: 3 – because the first even number (6) is at index 3 +``` + +Declare a `dataArrIndex` variable using `const` and set it to the result of the `findIndex()` method applied to the `taskData` array. Utilize arrow syntax to provide a callback function with `item` as the parameter, and within the callback, check if the `id` property of `item` is equal to the `id` property of `currentTask`. + +If the task exists, this returns the index, and if it doesn't exist, it returns `-1`. + +# --hints-- + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === currentTask.id`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?\s*=>\s*item.id\s*===\s*currentTask.id\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md new file mode 100644 index 00000000000..10826de6f17 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md @@ -0,0 +1,340 @@ +--- +id: 64e4f01d6c72086e016a8626 +title: Step 12 +challengeType: 0 +dashedName: step-12 +--- + +# --description-- + +Next, retrieve the values from the input fields and store them in a `taskObj` object. Each task should also have a unique `id`. + +Create a `taskObj` object with an `id` property as the first property. For the value of the `id` property, retrieve the value of the `titleInput` field, convert it to lowercase, and then use the `split()` and `join()` methods to hyphenate it. + +Make sure all of those are in template literals because you need the `id` property value as a string. + +# --hints-- + +You should use `const` to create a `taskObj` object. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*/) +``` + +Your `taskObj` object should have a key of `id`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id/) +``` + +You should use template strings to get `titleInput.value` as the value of your `id` key and convert it to lowercase. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)/) +``` + +You should use `.split(' ')` on `titleInput.value.toLowerCase()`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)/) +``` + +You should use `.join('-')` on `titleInput.value.toLowerCase().split(' ')`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md new file mode 100644 index 00000000000..764de1b0918 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md @@ -0,0 +1,327 @@ +--- +id: 64ec89ee549ecf802de2b3e2 +title: Step 14 +challengeType: 0 +dashedName: step-14 +--- + +# --description-- + +Retrieve the values from the `titleInput`, `dateInput`, and `descriptionInput` fields, and then save them in the properties `title`, `date`, and `description` of the `taskObj` object. + +# --hints-- + +You should get the value of `titleInput` and save it in a `title` key. + +```js +assert.match(code, /title:\s*titleInput\.value,/) +``` + +You should get the value of `dateInput` and save it in a `date` key. + +```js +assert.match(code, /date:\s*dateInput\.value,/) +``` + +You should get the value of `descriptionInput` and save it in a `description` key. + +```js +assert.match(code, /description:\s*descriptionInput\.value,?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + const taskObj = { + id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`, + --fcc-editable-region-- + + --fcc-editable-region-- + }; +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md new file mode 100644 index 00000000000..3d009188d81 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md @@ -0,0 +1,332 @@ +--- +id: 64ec8f717b261e824d82d6a5 +title: Step 15 +challengeType: 0 +dashedName: step-15 +--- + +# --description-- + +Now that you have obtained the values from the input fields and generated an `id`, you want to add them to your `taskData` array to keep track of each task. However, you should only do this if the task is new. If the task already exists, you will set it up for editing. This is why you have the `dataArrIndex` variable, which provides the index of each task. + +Create an `if` statement with the condition `dataArrIndex === -1`. Within the `if` statement, use the `unshift()` method to add the `taskObj` object to the beginning of the `taskData` array. + +`unshift()` is an array method that is used to add one or more elements to the beginning of an array. + +**N.B:** Try adding a task and log `taskData` to the console to see what it looks like. + +# --hints-- + +You should create an `if` statement with the condition `dataArrIndex === -1`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{/) +``` + +Your `if` statement should have `taskData.unshift(taskObj)` in it's body. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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, + }; + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md new file mode 100644 index 00000000000..f1202bb93fe --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md @@ -0,0 +1,331 @@ +--- +id: 64ec9145e424d8835a4e0f28 +title: Step 16 +challengeType: 0 +dashedName: step-16 +--- + +# --description-- + +Now that you have saved the task in the `taskData` array, you should display the task on the page by looping through it. + +Use `forEach()` on `taskData`, then destructure `id`, `title`, `date`, `description` as the parameters. Don't return anything yet. + +# --hints-- + +You should use `forEach()` on `taskData`. + +```js +assert.match(code, /taskData\.forEach\(\s*/) +``` + +You should destructure `id`, `title`, `date`, `description` as the parameters of the callback of your `forEach()`. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md new file mode 100644 index 00000000000..06bdd7c6ae5 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md @@ -0,0 +1,336 @@ +--- +id: 64ec9282cd547785258cecf2 +title: Step 17 +challengeType: 0 +dashedName: step-17 +--- + +# --description-- + +Using arrow syntax, create an implicit return and use addition assignment to set the `innerHTML` of `tasksContainer` to empty backticks. + +# --hints-- + +You should not alter the existing `taskData.forEach()` and the values you destructured. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +You should use arrow syntax and attach `innerHTML` to `tasksContainer`. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\(\s*tasksContainer\.innerHTML\s*\))?/) +``` + +You should use addition assignment to set the `innerHTML` of `tasksContainer` to an empty pair of backticks. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\s*\(?tasksContainer\.innerHTML\s*\+=\s*`\s*`\)?)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + taskData.forEach(({id, title, date, description})); + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md new file mode 100644 index 00000000000..b5115f99a51 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md @@ -0,0 +1,333 @@ +--- +id: 64ec9343769e8f85c1e17e05 +title: Step 18 +challengeType: 0 +dashedName: step-18 +--- + +# --description-- + +Create a `div` element with the class of `task`. Utilize template strings to set the `id` attribute of the `div` to the `id` you destructured from the task data. + +# --hints-- + +You should create a `div` element with the class `task`. + +```js +assert.match(code, /\s*<\/div>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` + --fcc-editable-region-- + + --fcc-editable-region-- + `) + ); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md new file mode 100644 index 00000000000..81dd3530420 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md @@ -0,0 +1,348 @@ +--- +id: 64ec94f0de20c086e09b0fc3 +title: Step 19 +challengeType: 0 +dashedName: step-19 +--- + +# --description-- + +Create a `p` element and use template strings to set its content to the `title` you destructured. Right before the content of the `p` element, create a `strong` element with the text `Title:`. + +# --hints-- + +You should create a `p` element. + +```js +assert.match(code, /

/) +``` + +You should interpolate `${title}` as the text of your `p` element. + +```js +assert.match(code, /

.*\$\{title\}<\/p>/) +``` + +You should create a `strong` element after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)/) +``` + +Your `strong` element should have the text `Title:`. + +```js +assert.match(code, /(?<=

)Title:\s*<\/strong>\s*/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md new file mode 100644 index 00000000000..4820d100d4d --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md @@ -0,0 +1,336 @@ +--- +id: 64ec959a76336c8767f5cd4d +title: Step 20 +challengeType: 0 +dashedName: step-20 +--- + +# --description-- + +Similarly to the previous step, create another `p` element, and interpolate the `date` you destructured as the text content. Inside this paragraph, create a `strong` element with the text `Date:`. + +# --hints-- + +You should create a `p` element and interpolate `${date}` as the text. + +```js +assert.match(code, /

.*\$\{date\}<\/p>/) +``` + +You should create a `strong` element with the text `Date:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Date:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md new file mode 100644 index 00000000000..4525b0e154c --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md @@ -0,0 +1,340 @@ +--- +id: 64ec96761156a187ed32b274 +title: Step 22 +challengeType: 0 +dashedName: step-22 +--- + +# --description-- + +To allow for task management, you need to include both a delete and an edit button for each task. + +Create two `button` elements with the `type` attribute set to `button` and the `class` attribute set to `btn`. Set the text of the first button to `Edit` and the text of the second button to `Delete`. + +# --hints-- + +You should create a `button` element of type `button`, a class `btn` and `Edit` as the text, in that order. + +```js +assert.match(code, /Edit<\/button/) +``` + +You should create a `button` element of type `button` a class `btn` and `Delete` as the text, in that order. + +```js +assert.match(code, /Delete<\/button/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md new file mode 100644 index 00000000000..0081fa88e1a --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md @@ -0,0 +1,336 @@ +--- +id: 64ec9b10356c2d8aa05d9ce1 +title: Step 23 +challengeType: 0 +dashedName: step-23 +--- + +# --description-- + +After adding the task to the page, you should close the form modal to view the task. To do this, utilize `classList` to toggle the `hidden` class on the `taskForm` element. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +const splitter = code.split("taskData.forEach") +assert.match(splitter[1], /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md new file mode 100644 index 00000000000..6ae1e4dff48 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md @@ -0,0 +1,347 @@ +--- +id: 64ec9c55fdeef78bacd2fc3b +title: Step 24 +challengeType: 0 +dashedName: step-24 +--- + +# --description-- + +If you attempt to add another task now, you'll notice that the input fields retain the values you entered for the previous task. To resolve this, you need to clear the input fields after adding a task. + +Instead of clearing the input fields one by one, it's a good practice to create a function that handles clearing those fields. You can then call this function whenever you need to clear the input fields again. + +Use arrow syntax to create a `reset` function and set it to a pair of curly braces. + +# --hints-- + +You should use `const` and arrow syntax to create a `reset` function. + +```js +assert.match(code, /const\s+reset\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +Your `reset` function should be empty. + +```js +assert.match(reset.toString(), /\(\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md new file mode 100644 index 00000000000..e12fb12ab81 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md @@ -0,0 +1,366 @@ +--- +id: 64fac365aeb8ad70b69b366f +title: Step 25 +challengeType: 0 +dashedName: step-25 +--- + +# --description-- + +Inside the `reset` function, set each value of `titleInput`, `dateInput`, `descriptionInput` to an empty string. + +Also, use `classList` to toggle the class `hidden` on the `taskForm` and set `currentTask` to an empty object. That's because at this point, `currentTask` will be filled with the task the user might have added. + +# --hints-- + +You should set `titleInput.value` to an empty string. + +```js +assert.match(reset.toString(), /titleInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `dateInput.value` to an empty string. + +```js +assert.match(reset.toString(), /dateInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `descriptionInput.value` to an empty string. + +```js +assert.match(reset.toString(), /descriptionInput\.value\s*=\s*('|")\1;?/) +``` + +You should use `classList` to toggle the class `hidden` on `taskForm` + +```js +assert.match(reset.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +You should set `currentTask` to an empty object. + +```js +assert.match(reset.toString(), /currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- +const reset = () => { + +} +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md new file mode 100644 index 00000000000..5f281d0de16 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md @@ -0,0 +1,351 @@ +--- +id: 64fac4d1773e7a719b1254de +title: Step 26 +challengeType: 0 +dashedName: step-26 +--- + +# --description-- + +Remove the existing code toggling the class of `hidden` on `taskForm` and call the `reset` function instead. This would clear the input fields and also hide the form modal for the user to see the added task. + +# --hints-- + +Yous should remove the code toggling the `hidden` class on `taskForm`. + +```js +const splitter = code.split('') +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /reset\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + +--fcc-editable-region-- + taskForm.classList.toggle("hidden"); +--fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md new file mode 100644 index 00000000000..c1a7b0f8965 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md @@ -0,0 +1,351 @@ +--- +id: 64fac6a497811572b338e5e5 +title: Step 27 +challengeType: 0 +dashedName: step-27 +--- + +# --description-- + +Also, remove the existing code toggling the class `hidden` on `taskForm` inside the `discardBtn` event listener and call the `reset` function instead. That's because when you click the `Discard` button, everything in the input fields should go away. + +# --hints-- + +Yous should remove the code toggling the class `hidden` on `taskForm`. + +```js +const splitter = code.split("confirmCloseDialog.close();") +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /confirmCloseDialog\.close\(\);?\s*reset\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); +--fcc-editable-region-- + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md new file mode 100644 index 00000000000..84005e1b01a --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md @@ -0,0 +1,346 @@ +--- +id: 64faca774fd9fd74bc084cc9 +title: Step 28 +challengeType: 0 +dashedName: step-28 +--- + +# --description-- + +You should display the `Cancel` and `Discard` buttons to the user only if there is some text present in the input fields. + +To begin, within the `closeTaskFormBtn` event listener, create a `formInputsContainValues` variable to check if there is a value in the `titleInput` field **or** the `dateInput` field **or** the `descriptionInput` field. + +# --hints-- + +You should use `const` to create a variable `formInputsContainValues` with the value `titleInput.value || dateInput.value || descriptionInput.value;` + +```js +assert.match(code, /const\s*formInputsContainValues\s*=\s*titleInput\.value\s*\|\|\s*dateInput\.value\s*\|\|\s*descriptionInput\.value;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); + +}); +--fcc-editable-region-- + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md new file mode 100644 index 00000000000..07482464ebf --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md @@ -0,0 +1,362 @@ +--- +id: 64facf6180824876f70a2e86 +title: Step 29 +challengeType: 0 +dashedName: step-29 +--- + +# --description-- + +Create an `if` statement to check if `formInputsContainValues` is true. If `formInputsContainValues` is true, indicating that there are changes, use the `showModal()` method on `confirmCloseDialog`. Otherwise, if there are no changes, call the `reset()` function to clear the input fields and hide the form modal. + +# --hints-- + +You should create an `if` statement with the condition `formInputsContainValues`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{/) +``` + +The `if` block of your `if` statement should contain `confirmCloseDialog.showModal();`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?/) +``` + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{/) +``` + +You should call the `reset()` function in the `else` block of your `if` statement. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{\s*reset\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 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; +--fcc-editable-region-- + confirmCloseDialog.showModal(); +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md new file mode 100644 index 00000000000..95b71fba69f --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md @@ -0,0 +1,372 @@ +--- +id: 64fad07f43a101779cb8692a +title: Step 30 +challengeType: 0 +dashedName: step-30 +--- + +# --description-- + +You can enhance code readability and maintainability by refactoring the `submit` event listener into two separate functions. The first function can be used to add the input values to `taskData`, while the second function can be responsible for adding the tasks to the DOM. + +Use arrow syntax to create an `addOrUpdateTask` function. Then move the `dataArrIndex` variable, the `taskObj` object, and the `if` statement into the `addOrUpdateTask` function. + +# --hints-- + +You should use `const` and arrow syntax to create an `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +You should move the `dataArrIndex` variable into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?/) +``` + +You should move the `taskObj` object into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?/) +``` + +You should move the `if` statement with the condition `dataArrIndex === 1` into your `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?\s*if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md new file mode 100644 index 00000000000..8192699bb61 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md @@ -0,0 +1,358 @@ +--- +id: 64fad9cd2eeb1e7ca2ca8c8b +title: Step 31 +challengeType: 0 +dashedName: step-31 +--- + +# --description-- + +Use arrow syntax to create an `updateTaskContainer` function. Then move the `taskData.forEach()` and its content into it. + +# --hints-- + +You should use `const` and arrow syntax to create a `updateTaskContainer` function. + +```js +assert.match(code, /const\s*updateTaskContainer\s*=\s*\(\)\s*=>\s*\{/) +``` + +You should move `taskData.forEach()` and its content into the `updateTaskContainer()` function. + +```js +assert.match(code, /const\s+updateTaskContainer\s+=\s*\(\)\s*=>\s*\{\s*taskData\.forEach\(\s*\(\{\s*id,\s*title,\s*date,\s*description\s*\}\)\s*=>\s*\(tasksContainer\.innerHTML\s*\+=\s*`\s*\s*

Title:<\/strong>\s*\$\{title\}<\/p>\s*

Date:<\/strong>\s*\$\{date\}<\/p>\s*

Description:<\/strong>\s*\$\{description\}<\/p>\s*Edit<\/button>\s*Delete<\/button>\s*<\/div>\s*`\)\s*\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md new file mode 100644 index 00000000000..18b2a462de4 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md @@ -0,0 +1,360 @@ +--- +id: 64fadae4f2d51b7d5d8b98d8 +title: Step 32 +challengeType: 0 +dashedName: step-32 +--- + +# --description-- + +Inside the `addOrUpdateTask` function, call the `updateTaskContainer` and `reset` functions. + +# --hints-- + +You should call the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\)\s*/) +``` + +You should call the `reset` function after calling the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\);?\s*reset\(\);?\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + reset() +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md new file mode 100644 index 00000000000..657bb4aa05b --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md @@ -0,0 +1,355 @@ +--- +id: 64fadff23375f27ff06c6d40 +title: Step 33 +challengeType: 0 +dashedName: step-33 +--- + +# --description-- + +Now remove the `reset()` call inside the `taskForm` submit event listener and call the `addOrUpdateTask` function instead. + +# --hints-- + +You should remove `reset()` and call `addOrUpdateTask()`. + +```js +assert.match(code, /addOrUpdateTask\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + --fcc-editable-region-- + reset() + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md new file mode 100644 index 00000000000..bd759fb7e5f --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md @@ -0,0 +1,364 @@ +--- +id: 64fae068bcdc9c805bd8399e +title: Step 35 +challengeType: 0 +dashedName: step-35 +--- + +# --description-- + +To enable editing and deleting for each task, add an `onclick` attribute to both buttons. Set the value of the `onclick` attribute to `editTask(this)` for the `Edit` button and `deleteTask(this)` for the `Delete` button. The `editTask(this)` function will handle editing, while the `deleteTask(this)` function will handle deletion. + +`this` is a keyword that refers to the current context. In this case, `this` points to the element that triggers the event – the buttons. + +# --hints-- + +You should add `onclick="editTask(this)"` as the first attribute of the edit button. + +```js +assert.match(code, /Edit<\/button>/) +``` + +You should add `onclick="deleteTask(this)"` as the first attribute of the delete button. + +```js +assert.match(code, /Delete<\/button>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + + --fcc-editable-region-- +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md new file mode 100644 index 00000000000..c6fac350818 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md @@ -0,0 +1,371 @@ +--- +id: 64faedcd16a1e985c4c2dc94 +title: Step 36 +challengeType: 0 +dashedName: step-36 +--- + +# --description-- + +Create a `deleteTask` function using arrow syntax. Pass `buttonEl` as the parameter and define an empty set of curly braces for the function body. + +# --hints-- + +You should use `const` and arrow syntax to create a `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `deleteTask` function should take a `buttonEl` parameter. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `deleteTask` function should be empty. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md new file mode 100644 index 00000000000..27631c77f2f --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md @@ -0,0 +1,359 @@ +--- +id: 64faf0418e828c0114a558a7 +title: Step 34 +challengeType: 0 +dashedName: step-34 +--- + +# --description-- + +There's a problem. If you add a task, and then add another, the previous task gets duplicated. This means you need to clear out the existing contents of `tasksContainer` before adding a new task. + +Set the `innerHTML` of `taskContainer` back to an empty string. + + +# --hints-- + +You should set the `innerHTML` of `tasksContainer` to an empty string. + +```js +assert.match(code, /tasksContainer\.innerHTML\s*=\s*("|')\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + --fcc-editable-region-- + + --fcc-editable-region-- + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md new file mode 100644 index 00000000000..9cdbd7860ca --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md @@ -0,0 +1,382 @@ +--- +id: 64faf65b22ad8d07df9be14d +title: Step 37 +challengeType: 0 +dashedName: step-37 +--- + +# --description-- + +You need to find the index of the task you want to delete first. + +Create a `dataArrIndex` variable and set its value using the `findIndex()` method on the `taskData` array. Pass `item` as the parameter for the arrow callback function, and within the callback, check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. Don't use curly braces. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?\s*item\s*\)?\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\s*\);?\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md new file mode 100644 index 00000000000..0e54adadf06 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md @@ -0,0 +1,398 @@ +--- +id: 64faf874364ec308f875f636 +title: Step 38 +challengeType: 0 +dashedName: step-38 +--- + +# --description-- + +You need to remove the task from the DOM using `remove()` and from the `taskData` array using `splice()`. + +`splice()` is an array method that modifies arrays by removing, replacing, or adding elements at a specified index, while also returning the removed elements. It can take up to three parameters: the first one is the mandatory index at which to start, the second is the number of items to remove, and the third is an optional replacement element. Here's an example: + +```js +const fruits = ["mango", "date", "cherry", "banana", "apple"]; + +// Remove date and cherry from the array starting at index 1 +const removedFruits = fruits.splice(1, 2); + +console.log(fruits); // [ 'mango', 'banana', 'apple' ] +console.log(removedFruits); // [ 'date', 'cherry' ] +``` + +Use the `remove()` method to remove the `parentElement` of the `buttonEl` from the DOM. Then use `splice()` to remove the task from the `taskData` array. Pass in `dataArrIndex` and `1` as the parameters of your `splice()`. + +`dataArrIndex` is the index to start and `1` is the number of items to remove. + +# --hints-- + +You should use the `remove()` method to remove the parent element of `buttonEl`. + +```js +assert.match(deleteTask.toString(), /buttonEl\.parentElement\.remove\(\);?/) +``` + +You should use `splice()` on the `taskData` array. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(/) +``` + +The first parameter of your `splice()` method should be `dataArrIndex`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex/) +``` + +The second parameter of your `splice()` method should be `1`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex,\s*1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md new file mode 100644 index 00000000000..3f379d51764 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md @@ -0,0 +1,381 @@ +--- +id: 64fafac95328110a69bcb75f +title: Step 39 +challengeType: 0 +dashedName: step-39 +--- + +# --description-- + +Use arrow syntax to create an `editTask` function. Pass in `buttonEl` as the parameter and add empty curly braces for the body. + +# --hints-- + +You should use `const` and arrow syntax to create an `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `editTask` function should take a `buttonEl` parameter. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `editTask` function should be empty. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md new file mode 100644 index 00000000000..bc0e2a84503 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md @@ -0,0 +1,391 @@ +--- +id: 64fb0fa0968f2b113b2d90e9 +title: Step 40 +challengeType: 0 +dashedName: step-40 +--- + +# --description-- + +As you did in the `deleteTask` function, you need to find the index of the task to be edited. + +Create a `dataArrIndex` variable. For its value, utilize the `findIndex()` method on `taskData`. Pass `item` as the parameter to its callback function and check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md new file mode 100644 index 00000000000..14d527ecf40 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md @@ -0,0 +1,373 @@ +--- +id: 64fb1061ca838611ed6a7d6b +title: Step 41 +challengeType: 0 +dashedName: step-41 +--- + +# --description-- + +Use square bracket notation to retrieve the task to be edited from the `taskData` array using the `dataArrIndex`. Then, assign it to the `currentTask` object to keep track of it. + +# --hints-- + +You should assign `taskData[dataArrIndex]` to `currentTask`. + +```js +assert.match(code, /currentTask\s*=\s*taskData\[dataArrIndex\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex((item) => item.id === buttonEl.parentElement.id); + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md new file mode 100644 index 00000000000..73ef19befcc --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md @@ -0,0 +1,389 @@ +--- +id: 64fb1321e189a6136d200f77 +title: Step 42 +challengeType: 0 +dashedName: step-42 +--- + +# --description-- + +The task to be edited is now in the `currentTask` object. Stage it for editing inside the input fields by setting the value of `titleInput` to `currentTask.title`, `dateInput` to `currentTask.date`, and `descriptionInput` to `currentTask.description`. + +# --hints-- + +You should set `titleInput.value` to `currentTask.title`. + +```js +assert.match(editTask.toString(), /titleInput\.value\s*=\s*currentTask\.title;?/) +``` + +You should set `dateInput.value` to `currentTask.date`. + +```js +assert.match(editTask.toString(), /dateInput\.value\s*=\s*currentTask\.date;?/) +``` + +You should set `descriptionInput.value` to `currentTask.description`. + +```js +assert.match(editTask.toString(), /descriptionInput\.value\s*=\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + currentTask = taskData[dataArrIndex]; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md new file mode 100644 index 00000000000..c6312bf6bd0 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md @@ -0,0 +1,386 @@ +--- +id: 64fb1436adef3e145b4c3501 +title: Step 43 +challengeType: 0 +dashedName: step-43 +--- + +# --description-- + +Set the `innerText` of the `addOrUpdateTaskBtn` button to `Update Task` and its `aria-label` to `Update Task` as well. + +# --hints-- + +You should set the inner text of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.innerText\s*=\s*("|')Update Task\1;?/) +``` + +You should set the `ariaLabel` of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.ariaLabel\s*=\s*("|')Update Task\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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; + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md new file mode 100644 index 00000000000..101870d098a --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md @@ -0,0 +1,384 @@ +--- +id: 64fb14d890415c14f93069ce +title: Step 44 +challengeType: 0 +dashedName: step-44 +--- + +# --description-- + +Finally, display the `form` modal with the values of the input fields by using `classList` to toggle the `hidden` class on `taskForm`. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(editTask.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md new file mode 100644 index 00000000000..7ad62060776 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md @@ -0,0 +1,391 @@ +--- +id: 64fb154a7c48cd159924bb18 +title: Step 45 +challengeType: 0 +dashedName: step-45 +--- + +# --description-- + +At this point, editing a task won't reflect when you submit the task. To make the editing functional, go back to the `if` statement inside the `addOrUpdateTask` function. Create an `else` block and set `taskData[dataArrIndex]` to `taskObj`. + +# --hints-- + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*/) +``` + +Your `else` block should have the code `taskData[dataArrIndex] = taskObj`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*taskData\[dataArrIndex\]\s*=\s*taskObj;?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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, + }; + + --fcc-editable-region-- + if (dataArrIndex === -1) { + taskData.unshift(taskObj); + } + + --fcc-editable-region-- + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md new file mode 100644 index 00000000000..4ec33172878 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md @@ -0,0 +1,401 @@ +--- +id: 64fb1c4dc0feb219149a7c7d +title: Step 46 +challengeType: 0 +dashedName: step-46 +--- + +# --description-- + +If the user attempts to edit a task but decides not to make any changes before closing the form, there is no need to display the modal with the `Cancel` and `Discard` buttons. + +Inside the `closeTaskFormBtn` event listener, use `const` to create another variable named `formInputValuesUpdated`. Check if the user made changes while trying to edit a task by verifying that the `titleInput` value **is not equal to** `currentTask.title`, the `dateInput` value **is not equal to** `currentTask.date`, and the `descriptionInput` value **is not equal to** `currentTask.description`. + +# --hints-- + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*/) +``` + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title` or `dateInput.value` is not equal to `currentTask.date`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date/) +``` + +Your `formInputValuesUpdated` variable should have the value `titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date\s*\|\|\s*descriptionInput\.value\s*!==\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + --fcc-editable-region-- + + --fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md new file mode 100644 index 00000000000..bb8a87acf60 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md @@ -0,0 +1,390 @@ +--- +id: 64fb285637fa1e1c222033e3 +title: Step 47 +challengeType: 0 +dashedName: step-47 +--- + +# --description-- + +Now add `formInputValuesUpdated` as the second mandatory condition in the `if` statement using the `AND` operator. + +This way, the `Cancel` and `Discard` buttons in the modal won't be displayed to the user if they haven't made any changes to the input fields while attempting to edit a task. + +# --hints-- + +Your `if` should have the condition `formInputsContainValues && formInputValuesUpdated`. + +```js +assert.match(code.split("if (formInputsContainValues"), /\s*&&\s*formInputValuesUpdated/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description; + +--fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md new file mode 100644 index 00000000000..94fdc40185a --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md @@ -0,0 +1,414 @@ +--- +id: 64fb29348a60361ccd45c1e2 +title: Step 48 +challengeType: 0 +dashedName: step-48 +--- + +# --description-- + +`localStorage` offers methods for saving, retrieving, and deleting items. The items you save can be of any JavaScript data type. + +For instance, the `setItem()` method is used to save an item, and the `getItem()` method retrieves the item. To delete a specific item, you can utilize the `removeItem()` method, or if you want to delete all items in the storage, you can use `clear()`. + +Here's how you can save an item: + +```js +localStorage.setItem("key", value); // value could be string, number, or any other data type +``` + +A `myTaskArr` array has been provided for you. Use the `setItem()` method to save it with a key of `data`. + +After that, open your browser console and go to the `Applications` tab, select `Local Storage`, and the freeCodeCamp domain you see. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +Your `localStorage.setItem()` should have a key of `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*myTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md new file mode 100644 index 00000000000..2ce0b111937 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md @@ -0,0 +1,404 @@ +--- +id: 64fefebad99209211ec30537 +title: Step 49 +challengeType: 0 +dashedName: step-49 +--- + +# --description-- + +If you check the `Application` tab of your browser console, you'll notice a series of `[object Object]`. This is because everything you save in `localStorage` needs to be in string format. + +To resolve the issue, wrap the data you're saving in the `JSON.stringify()` method. Then, check local storage again to observe the results. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +You should wrap `JSON.stringify()` around `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*JSON\.stringify\(myTaskArr\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- +localStorage.setItem("data", myTaskArr); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md new file mode 100644 index 00000000000..1cb0c0c5930 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md @@ -0,0 +1,406 @@ +--- +id: 64ff0313700dad264d19dfe4 +title: Step 50 +challengeType: 0 +dashedName: step-50 +--- + +# --description-- + +Now that you have the `myTaskArr` array saved in `localStorage` correctly, you can retrieve it with `getItem()` by specifying the key you used to save the item. + +Use the `getItem()` method to retrieve the `myTaskArr` array and assign it to the variable `getTaskArr`. Then, log the `getTaskArr` variable to the console to see the result. + +# --hints-- + +You should use `const` to create a `getTaskArr` variable and assign `localStorage.getItem("data")` to it. + +```js +assert.match(code, /const\s*getTaskArr\s*=\s*localStorage\.getItem\(('|")data\1\);?/) +``` + +You should log the `getTaskArr` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md new file mode 100644 index 00000000000..608713897b5 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md @@ -0,0 +1,411 @@ +--- +id: 64ff04cc33779427a6412449 +title: Step 51 +challengeType: 0 +dashedName: step-51 +--- + +# --description-- + +The item you retrieve is a string, as you saved it with `JSON.stringify()`. To view it in its original form before saving, you need to use `JSON.parse()`. + +Use `getItem()` to retrieve the `myTaskArr` array again. This time, wrap it inside `JSON.parse()`, assign it to the variable `getTaskArrObj` and log the `getTaskArrObj` to the console. + +Check the console to see the difference between `getTaskArr` and `getTaskObj`. + +# --hints-- + +You should use `const` to create a `getTaskArrObj` variable and assign it to `JSON.parse(localStorage.getItem('data'));`. + +```js +assert.match(code, /const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\(('|")data\1\)\);?/) +``` + +You should log the `getTaskArrObj` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md new file mode 100644 index 00000000000..09bad9433fa --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md @@ -0,0 +1,406 @@ +--- +id: 64ff068e0426eb288874ed79 +title: Step 52 +challengeType: 0 +dashedName: step-52 +--- + +# --description-- + +You can use `localStorage.removeItem()` to remove a specific item and `localStorage.clear()` to clear all items in the local storage. + +Remove the `data` item from local storage and open the console to observe the result. You should see `null`. + +# --hints-- + +You should use `localStorage.removeItem()` to remove the `data` item from the browser's local storage. + +```js +assert.match(code, /localStorage\.removeItem\(('|")data\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md new file mode 100644 index 00000000000..5dc3bd6e1d1 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md @@ -0,0 +1,413 @@ +--- +id: 64ff23daf176a92de95f24dc +title: Step 53 +challengeType: 0 +dashedName: step-53 +--- + +# --description-- + +Using `localStorage.clear()` won't just delete a single item from local storage but will remove all items. + +Remove `localStorage.removeItem()` and use `localStorage.clear()` instead. You don't need to pass in anything. You should also see `null` in the console. + +# --hints-- + +You should remove `localStorage.removeItem("data")`. + +```js +assert.notMatch(code, /localStorage\.removeItem\(('|")data\1\);/) +``` + +You should remove everything from the browser `local storage` with `localStorage.clear()`. + +```js +assert.match(code, /localStorage\.clear\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- +localStorage.removeItem("data"); + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md new file mode 100644 index 00000000000..0d6c42c989e --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md @@ -0,0 +1,404 @@ +--- +id: 64ff24b80431f62ec6b93f65 +title: Step 54 +challengeType: 0 +dashedName: step-54 +--- + +# --description-- + +Remove the `myTaskArr` array and all of the code for `localStorage` because you don't need them anymore. + +# --hints-- + +You should remove `myTaskArr` and all the code related to `localStorage` that you've just learned. + +```js +assert.notMatch(code, /const\s*myTaskArr\s*=\s*\[\s*\{\s*task:\s"Walk\s*the\s*Dog",\s*date:\s*"22-04-2022"\s*\},\s*\{\s*task:\s"Read\s*some\s*books",\s*date:\s*"02-11-2023"\s*\},\s*\{\s*task:\s"Watch\s*football",\s*date:\s*"10-08-2021"\s*\},\s*\];?\s*localStorage\.setItem\("data", JSON\.stringify\(myTaskArr\)\);\s*localStorage\.clear\(\);?\s*const\s*getTaskArr\s*=\s*localStorage\.getItem\("data"\)\s*console\.log\(getTaskArr\)\s*const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\("data"\)\);?\s*console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +--fcc-editable-region-- +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +localStorage.clear(); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md new file mode 100644 index 00000000000..cbbcd4df6e1 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md @@ -0,0 +1,404 @@ +--- +id: 65003986d17d1e1865b269c0 +title: Step 55 +challengeType: 0 +dashedName: step-55 +--- + +# --description-- + +Now you should save the task items to local storage when the user adds, updates, or removes a task. + +Inside the `addOrUpdateTask` function, use `setItem()` to save the tasks with a key of `data`, then pass the `taskData` array as its parameter. Ensure that you stringify the `taskData`. + +This would persist data once the user adds or updates tasks. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +assert.match(code, /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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; + } + --fcc-editable-region-- + + --fcc-editable-region-- + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md new file mode 100644 index 00000000000..ec3a700096a --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md @@ -0,0 +1,407 @@ +--- +id: 650046832f92c01a35834bca +title: Step 56 +challengeType: 0 +dashedName: step-56 +--- + +# --description-- + +You also want a deleted task to be removed from local storage. For this, you don't need the `removeItem()` or `clear()` methods. Since you already use `splice()` to remove the deleted task from `taskData`, all you need to do now is save `taskData` to local storage again. + +Use `setItem()` to save the `taskData` array again. Pass in `data` as the key and ensure that `taskData` is stringified before saving. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); + --fcc-editable-region-- + + --fcc-editable-region-- +} + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md new file mode 100644 index 00000000000..40cf6384c4e --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md @@ -0,0 +1,392 @@ +--- +id: 650048b0764f9c1b798200e2 +title: Step 57 +challengeType: 0 +dashedName: step-57 +--- + +# --description-- + +If you add, update, or remove a task, it should reflect in the UI. However, that's not happening now because you have yet to retrieve the tasks. To do this, you need to modify your initial `taskData` to be an empty array. + +Set `taskData` to the retrieval of `data` from local storage **or** an empty array. Make sure you parse the data coming with `JSON.parse()` because you saved it as a string. + +# --hints-- + +You should set the `taskData` variable to `JSON.parse(localStorage.getItem("data")) || [];`. + +```js +assert.match(code, /const\s*taskData\s*=\s*JSON.parse\(localStorage\.getItem\(('|")data\1\)\)\s*\|\|\s*\[\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- +const taskData = []; +--fcc-editable-region-- +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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md new file mode 100644 index 00000000000..08ced41b63f --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md @@ -0,0 +1,776 @@ +--- +id: 65004ba581d03d1d5628b41c +title: Step 58 +challengeType: 0 +dashedName: step-58 +--- + +# --description-- + +You've retrieved the task item(s) now, but they still don't reflect in the UI when the page loads. However, they appear when you add a new task. + +To fix this issue, you can check if there's a task inside `taskData`, and then call the `updateTaskContainer()`. + +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 the condition `taskData.length`. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*/) +``` + +You should call the `updateTaskContainer` function in your `if` statement. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*updateTaskContainer\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + taskForm.classList.toggle("hidden"); +} + +const reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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 + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md new file mode 100644 index 00000000000..6b21e6888ce --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md @@ -0,0 +1,314 @@ +--- +id: 650300a25b6f72964ab8aca6 +title: Step 13 +challengeType: 0 +dashedName: step-13 +--- + +# --description-- + +To make the `id` more unique, add another hyphen and use `Date.now()`. + +# --hints-- + +You should attach `-${Date.now()}` to the existing value of the `id` key. Don't forget it needs to be inside the template string. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + const taskObj = { + id: `${titleInput.value.toLowerCase().split(' ').join('-')}`, + }; + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md new file mode 100644 index 00000000000..0c9fbc152b7 --- /dev/null +++ b/curriculum/challenges/chinese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md @@ -0,0 +1,337 @@ +--- +id: 65099dbd8f137d58e5c0ff16 +title: Step 21 +challengeType: 0 +dashedName: step-21 +--- + +# --description-- + +Create one more `p` element and interpolate the `description` you destructured as the text. Also, create a `strong` element inside the paragraph with the text `Description:`. + +# --hints-- + +You should create a `p` element with `${description}` as the text. + +```js +assert.match(code, /

.*\$\{description\}<\/p>/) +``` + +You should create a `strong` element with the text `Description:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Description:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/chinese/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md b/curriculum/challenges/chinese/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md index 5d03eecd633..4c2eca20050 100644 --- a/curriculum/challenges/chinese/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md +++ b/curriculum/challenges/chinese/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md @@ -12,7 +12,7 @@ dashedName: simple-and-compound-interest 这是和这个视频配套的 Colab 笔记本。 -这里有一个额外的 Colab 笔记本,它向你展示了将许多这些利息和付款公式图放入 Python 函数的一种方法。同时你将看到一个使用一些公式输出结果的例子,注意一个趋势,并学习其他公式来分析模式。 +Here is an additional Colab notebook that shows you one way to put many of these interest and payment formulas into Python functions. Also you will see an example of using some formulas to output results, notice a trend, and follow up with other formulas to analyze patterns. # --question-- diff --git a/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md b/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md index 39ab831dad7..144796de1b0 100644 --- a/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md +++ b/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md @@ -71,8 +71,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md b/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md index 53ae7b50350..871e1408079 100644 --- a/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md +++ b/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md @@ -73,7 +73,7 @@ substringDivisibility(5); ```js function substringDivisibility(n) { - function isSubDivisable(digits) { + function isSubDivisible(digits) { const factors = [2, 3, 5, 7, 11, 13, 17]; for (let i = 1; i < digits.length - 2; i++) { @@ -108,17 +108,17 @@ function substringDivisibility(n) { } const allowedDigits = [...new Array(n + 1).keys()]; - const divisablePandigitals = []; + const divisiblePandigitals = []; heapsPermutations( allowedDigits.length, allowedDigits, - isSubDivisable, - divisablePandigitals + isSubDivisible, + divisiblePandigitals ); let sum = 0; - for (let i = 0; i < divisablePandigitals.length; i++) { - sum += divisablePandigitals[i]; + for (let i = 0; i < divisiblePandigitals.length; i++) { + sum += divisiblePandigitals[i]; } return sum; diff --git a/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md b/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md index 7cd5bc67b14..2d583a52355 100644 --- a/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md +++ b/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md @@ -67,8 +67,8 @@ class PrimeSeive { const prime = 2 * i + 3; primes.push(prime); // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } @@ -101,10 +101,10 @@ class PrimeSeive { }; function consecutivePrimeSum(limit) { - // Initalize seive + // Initialize seive const primeSeive = new PrimeSeive(limit); - // Initalize for longest sum < 100 + // Initialize for longest sum < 100 let bestPrime = 41; let bestI = 0; let bestJ = 5; diff --git a/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md b/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md index cdbfc3e568b..5b55ddd8079 100644 --- a/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md +++ b/curriculum/challenges/chinese/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/chinese/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md b/curriculum/challenges/chinese/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md index 78c29aa7952..4fa15980a0d 100644 --- a/curriculum/challenges/chinese/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md +++ b/curriculum/challenges/chinese/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md @@ -31,10 +31,10 @@ For $1 ≤ k ≤ 200$, find $\sum{m(k)}$. # --hints-- -`efficientExponentation()` should return `1582`. +`efficientExponentiation()` should return `1582`. ```js -assert.strictEqual(efficientExponentation(), 1582); +assert.strictEqual(efficientExponentiation(), 1582); ``` # --seed-- @@ -42,12 +42,12 @@ assert.strictEqual(efficientExponentation(), 1582); ## --seed-contents-- ```js -function efficientExponentation() { +function efficientExponentiation() { return true; } -efficientExponentation(); +efficientExponentiation(); ``` # --solutions-- diff --git a/curriculum/challenges/chinese/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md b/curriculum/challenges/chinese/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md index 49d59948762..e402ff347c5 100644 --- a/curriculum/challenges/chinese/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md +++ b/curriculum/challenges/chinese/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/chinese/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md b/curriculum/challenges/chinese/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md index 596f703da28..b843f6f2be2 100644 --- a/curriculum/challenges/chinese/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md +++ b/curriculum/challenges/chinese/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md @@ -35,7 +35,7 @@ concealedSquare(); ```js // Check if n**2 matches the pattern -function squareMatchs(n) { +function squareMatches(n) { // Need BigInt due to size of values let nSquared = (BigInt(n) * BigInt(n)).toString(); @@ -55,8 +55,8 @@ function concealedSquare() { for (let x = maxSquareRoot; x >= minSquareRoot; x -= 10) { // Note: 3*3 = 9 and 7*7 = 49 are only trailing digits // that can produce 9 as trailing digit in square - if (squareMatchs(x + 3)) return (x + 3)*10; - if (squareMatchs(x + 7)) return (x + 7)*10; + if (squareMatches(x + 3)) return (x + 3)*10; + if (squareMatches(x + 7)) return (x + 7)*10; } return -1; } diff --git a/curriculum/challenges/espanol/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md b/curriculum/challenges/espanol/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md index f84d9c20e2d..0b95ccac3a2 100644 --- a/curriculum/challenges/espanol/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md +++ b/curriculum/challenges/espanol/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md @@ -54,7 +54,24 @@ assert.match(code, /myStorage\.car\.inside/); `gloveBoxContents` debe ser declarado como `const`. ```js -assert.match(code, /const\s+gloveBoxContents\s*=\s*myStorage\.car\.inside\[\s*("|')glove box\1\s*\]|const\s*{\s*('|")glove box\2:\s*gloveBoxContents\s*}\s*=\s*myStorage\.car\.inside;/); +assert.match(code, /const\s+gloveBoxContents\s*=/); +``` + +You should not change the `myStorage` object. + +```js +const expectedMyStorage = { + "car":{ + "inside":{ + "glove box":"maps", + "passenger seat":"crumbs" + }, + "outside":{ + "trunk":"jack" + } + } +}; +assert.deepStrictEqual(myStorage, expectedMyStorage); ``` # --seed-- diff --git a/curriculum/challenges/espanol/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md b/curriculum/challenges/espanol/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md index f877b9259d4..f8f38eec4ce 100644 --- a/curriculum/challenges/espanol/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md +++ b/curriculum/challenges/espanol/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md @@ -10,7 +10,7 @@ dashedName: catch-misspelled-variable-and-function-names Los métodos `console.log()` y `typeof` son las dos formas principales de comprobar los valores intermedios y los tipos de salida de un programa. Ahora es el momento de entrar en las formas comúnes que adoptan los errores (bugs). Un problema a nivel de sintaxis con el que las personas que escriben rápido pueden simpatizar es el humilde error ortográfico. -Los caracteres transpuestos, omitidos o mal escritos en el nombre de una variable o función harán que el navegador busque un objeto que no existe, y se queje en forma de error de referencia. Los nombres de variables y funciones de JavaScript distinguen entre mayúsculas y minúsculas. +Transposed, missing, or miscapitalized characters in a variable or function name will have the browser looking for an object that doesn't exist - and complain in the form of a reference error. Los nombres de variables y funciones de JavaScript distinguen entre mayúsculas y minúsculas. # --instructions-- diff --git a/curriculum/challenges/espanol/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md b/curriculum/challenges/espanol/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md index ebc50c90d83..7fd4cc16a51 100644 --- a/curriculum/challenges/espanol/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md +++ b/curriculum/challenges/espanol/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md @@ -58,8 +58,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/espanol/06-quality-assurance/quality-assurance-projects/issue-tracker.md b/curriculum/challenges/espanol/06-quality-assurance/quality-assurance-projects/issue-tracker.md index 5d0dc632291..e273fb11a19 100644 --- a/curriculum/challenges/espanol/06-quality-assurance/quality-assurance-projects/issue-tracker.md +++ b/curriculum/challenges/espanol/06-quality-assurance/quality-assurance-projects/issue-tracker.md @@ -231,13 +231,13 @@ async (getUserInput) => { }; const url = getUserInput('url') + '/api/issues/fcc-project'; const itemToUpdate = await $.post(url, initialData); - const updateSucccess = await $.ajax({ + const updateSuccess = await $.ajax({ url: url, type: 'PUT', data: { _id: itemToUpdate._id, issue_text: 'New Issue Text' } }); - assert.isObject(updateSucccess); - assert.deepEqual(updateSucccess, { + assert.isObject(updateSuccess); + assert.deepEqual(updateSuccess, { result: 'successfully updated', _id: itemToUpdate._id }); diff --git a/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/24-game.md b/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/24-game.md index ac16df78509..88cdeed88e7 100644 --- a/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/24-game.md +++ b/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/24-game.md @@ -82,7 +82,7 @@ const OPERATORS_ = { "/": (a, b) => a / b, } -const PRECIDENCE_ = { +const PRECEDENCE_ = { "+": 1, "-": 1, "*": 2, @@ -114,7 +114,7 @@ function evaluate_(expression) { case "*": case "/": while (stack.length && - PRECIDENCE_[c] <= PRECIDENCE_[stack[stack.length-1]]) { + PRECEDENCE_[c] <= PRECEDENCE_[stack[stack.length-1]]) { postfix += stack.pop(); } stack.push(c); diff --git a/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md b/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md index 6eca8fa1bd6..36f8c4da04a 100644 --- a/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md +++ b/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md @@ -175,7 +175,7 @@ function dealFreeCell(seed) { const rng = FreeCellRNG(seed); const deck = getDeck(); - const deltCards = [[], [], [], [], [], [], []]; + const dealtCards = [[], [], [], [], [], [], []]; let currentColumn = 0; let currentRow = 0; @@ -195,7 +195,7 @@ function dealFreeCell(seed) { card = deck.pop(); // Deal this card - deltCards[currentRow].push(card); + dealtCards[currentRow].push(card); currentColumn += 1; if (currentColumn === 8) { currentColumn = 0; @@ -203,6 +203,6 @@ function dealFreeCell(seed) { } } - return deltCards; + return dealtCards; } ``` diff --git a/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/department-numbers.md b/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/department-numbers.md index 1117963ac08..5414cdcf18d 100644 --- a/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/department-numbers.md +++ b/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/department-numbers.md @@ -101,7 +101,7 @@ function combinations(possibleNumbers, total) { function combinations(possibleNumbers, total) { let firstNumber; let secondNumber; - let thridNumber; + let thirdNumber; const allCombinations = []; for (let i = 0; i < possibleNumbers.length; i += 1) { @@ -112,10 +112,10 @@ function combinations(possibleNumbers, total) { secondNumber = possibleNumbers[j]; if (j !== i && firstNumber + secondNumber <= total) { - thridNumber = total - firstNumber - secondNumber; + thirdNumber = total - firstNumber - secondNumber; - if (thridNumber !== firstNumber && thridNumber !== secondNumber && possibleNumbers.includes(thridNumber)) { - allCombinations.push([firstNumber, secondNumber, thridNumber]); + if (thirdNumber !== firstNumber && thirdNumber !== secondNumber && possibleNumbers.includes(thirdNumber)) { + allCombinations.push([firstNumber, secondNumber, thirdNumber]); } } } diff --git a/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/lu-decomposition.md b/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/lu-decomposition.md index 441680f24fb..a15b238b3be 100644 --- a/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/lu-decomposition.md +++ b/curriculum/challenges/espanol/10-coding-interview-prep/rosetta-code/lu-decomposition.md @@ -80,7 +80,7 @@ $PA = LU$ # --instructions-- -The task is to implement a routine which will take a square nxn matrix $A$ and return a lower triangular matrix $L$, a upper triangular matrix $U$ and a permutation matrix $P$, so that the above equation is fullfilled. The returned value should be in the form `[L, U, P]`. +The task is to implement a routine which will take a square nxn matrix $A$ and return a lower triangular matrix $L$, a upper triangular matrix $U$ and a permutation matrix $P$, so that the above equation is fulfilled. The returned value should be in the form `[L, U, P]`. # --hints-- diff --git a/curriculum/challenges/espanol/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md b/curriculum/challenges/espanol/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md index e9a70cffc9d..72eafa4111f 100644 --- a/curriculum/challenges/espanol/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md +++ b/curriculum/challenges/espanol/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md @@ -9,7 +9,7 @@ dashedName: step-82 El pico y las patas del pingüino comparten el mismo `color`. -Cree una nueva variable CSS personalizada llamada `--penguin-picorna` y reemplace todos los valores de propiedad relevantes con ella. +Create a new custom CSS variable named `--penguin-picorna`, and replace all relevant property values with it. # --hints-- diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md index 6c91e47a3b4..ff36d4c4254 100644 --- a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md @@ -11,7 +11,7 @@ Inside `pick`, use `let` to initialize a variable named `numbers` and set it to # --hints-- -Your `pick` function should initalize `numbers` to an empty array `[]`. +Your `pick` function should initialize `numbers` to an empty array `[]`. ```js assert.match(pick.toString(), /numbers\s*=\s*\[\]/); diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md new file mode 100644 index 00000000000..4b042aa87ba --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md @@ -0,0 +1,310 @@ +--- +id: 64e4e4c4ec263b62ae7bf54d +title: Step 1 +challengeType: 0 +dashedName: step-1 +--- + +# --description-- + +In this project, you will learn how `localStorage` works in JavaScript by building a Todo app. LocalStorage is a web storage feature of JavaScript that lets you persist data by storing the data as a **key:value** pair. + +The HTML and CSS for this project have been provided for you. Take a look at the files to get yourself familiarized with them. + +Begin by accessing the `task-form`, `confirm-close-dialog`, and `open-task-form-btn` elements with the `getElementById()` method. Save them in the variables `taskForm`, `confirmCloseDialog`, and `openTaskFormBtn`. + +# --hints-- + +You should use `getElementById()` to access the `task-form` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should assign the `task-form` element to the variable `taskForm`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+taskForm\s*=\s*document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should use `getElementById()` to access the `confirm-close-dialog` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should assign the `confirm-close-dialog` element to the variable `confirmCloseDialog`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+confirmCloseDialog\s*=\s*document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should use `getElementById()` to access the `open-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +You should assign the `open-task-form-btn` element to the variable `openTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+openTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md new file mode 100644 index 00000000000..32310409728 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md @@ -0,0 +1,310 @@ +--- +id: 64e4e6c86954de67a3e44ee3 +title: Step 2 +challengeType: 0 +dashedName: step-2 +--- + +# --description-- + +You need to access more elements with the `getElementById()` method. This time you need the `close-task-form-btn`, `add-or-update-task-btn`, and `cancel-btn` elements. Save them in the variables `closeTaskFormBtn`, `addOrUpdateTaskBtn`, and `cancelBtn`. + +# --hints-- + +You should use `getElementById()` to access the `close-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should assign the `close-task-form-btn` element to the variable `closeTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+closeTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `add-or-update-task-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should assign the `add-or-update-task-btn` element to the variable `addOrUpdateTaskBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+addOrUpdateTaskBtn\s*=\s*document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `cancel-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +You should assign the `cancel-btn` element to the variable `cancelBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+cancelBtn\s*=\s*document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md new file mode 100644 index 00000000000..17fc6b76517 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md @@ -0,0 +1,313 @@ +--- +id: 64e4e6fe78b5aa67ef2fc3e7 +title: Step 3 +challengeType: 0 +dashedName: step-3 +--- + +# --description-- + +Next, access the `discard-btn`, `tasks-container`, and `title-input` elements using the `getElementById()` method. Save them in variables named `discardBtn`, `tasksContainer`, and `titleInput`, respectively. + +# --hints-- + +You should use `getElementById()` to access the `discard-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should assign the `discard-btn` element to the variable `discardBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+discardBtn\s*=\s*document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `tasks-container` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should assign the `tasks-container` element to the variable `tasksContainer`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+tasksContainer\s*=\s*document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should use `getElementById()` to access the `title-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +You should assign the `title-input` element to the variable `titleInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+titleInput\s*=\s*document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md new file mode 100644 index 00000000000..6e543d36acc --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md @@ -0,0 +1,304 @@ +--- +id: 64e4e7241f52bb682eeb8211 +title: Step 4 +challengeType: 0 +dashedName: step-4 +--- + +# --description-- + +The last set of elements you need to get from the HTML file are the `date-input` and `description-input` elements. Save them in the variables `dateInput` and `descriptionInput` respectively. + +# --hints-- + +You should use `getElementById()` to access the `date-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should assign the `date-input` element to the variable `dateInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+dateInput\s*=\s*document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should use `getElementById()` to access the `description-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +You should assign the `description-input` element to the variable `descriptionInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+descriptionInput\s*=\s*document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md new file mode 100644 index 00000000000..6f0cd6e9fc4 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md @@ -0,0 +1,296 @@ +--- +id: 64e4e74d0fb4f0687bf4145d +title: Step 5 +challengeType: 0 +dashedName: step-5 +--- + +# --description-- + +Create a `taskData` constant and set it to an empty array. This array will store all the tasks along with their associated data, including title, due date, and description. This storage will enable you to keep track of tasks, display them on the page, and save them to `localStorage`. + +Use `let` to create a `currentTask` variable and set it to an empty object. This variable will be used to track the state when editing and discarding tasks. + +# --hints-- + +You should create a `taskData` constant set to an empty array. + +```js +assert.match(code, /const\s+taskData\s*=\s*\[\];?/) +``` + +You should use `let` to create an empty `currentTask` object. + +```js +assert.match(code, /let\s+currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md new file mode 100644 index 00000000000..7eba2896bf4 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md @@ -0,0 +1,307 @@ +--- +id: 64e4e78a7ea4a168de4e6a38 +title: Step 6 +challengeType: 0 +dashedName: step-6 +--- + +# --description-- + +Now, you will work on opening and closing the form modal. + +Add a `click` event listener to the `openTaskFormBtn` variable, and then use `classList` to toggle the `hidden` class on the `taskForm` element. + +Now you can click on the "Add new Task" button and see the form modal. + +# --hints-- + +You should call the `addEventListener()` method on your `openTaskFormBtn` variable. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax, then use `classList` property and it's `toggle` method on the `taskForm`, to toggle the class `hidden`. Don't use curly braces. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*taskForm\.classList\.toggle\(\1hidden\1\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md new file mode 100644 index 00000000000..6a29158134e --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md @@ -0,0 +1,309 @@ +--- +id: 64e4e7bbedb22d6939001ad3 +title: Step 7 +challengeType: 0 +dashedName: step-7 +--- + +# --description-- + +Add a `click` event listener to the `closeTaskFormBtn` variable, then use the `showModal()` method on your `confirmCloseDialog` variable. This will display a modal with the `Discard` and `Cancel` buttons. + +`showModal()` is a method associated with the HTML `dialog` element. It is used to display a modal dialog box on a web page. + +# --hints-- + +You should call the `addEventListener()` method on your `closeTaskFormBtn` variable. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `showModal()` method on `confirmCloseDialog`. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(["'`]click["'`],\s*\(.*\)\s*=>\s*{?\s*confirmCloseDialog\.showModal\(\);?\s*}?\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md new file mode 100644 index 00000000000..311984043a0 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md @@ -0,0 +1,316 @@ +--- +id: 64e4eaaa9070a66aecbfe603 +title: Step 8 +challengeType: 0 +dashedName: step-8 +--- + +# --description-- + +If the user clicks the `Cancel` button, you want to cancel the process (close the modal showing the two buttons) so the user can continue editing. + +Add a `click` event listener to the `cancelBtn` element, then use the `close()` method on the `confirmCloseDialog` variable. + +`close()` is a method of the window object you can use to close the current window, or a modal you create with the `dialog` element. + +# --hints-- + +You should call the `addEventListener()` method on your `cancelBtn` variable. + +```js +assert.match(code, /cancelBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `close()` method on `confirmCloseDialog`. Don't use curly braces. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*confirmCloseDialog\.close\(\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- + +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md new file mode 100644 index 00000000000..66a40c9771b --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md @@ -0,0 +1,327 @@ +--- +id: 64e4ebc7eabc5a6babd479cd +title: Step 9 +challengeType: 0 +dashedName: step-9 +--- + +# --description-- + +If the user clicks the `Discard` button, you want to close the modal showing the `Cancel` and `Discard` buttons, then hide the form modal. + +Add a click event listener to `discardBtn`, then use the `close()` method on the `confirmCloseDialog` variable. Also, use `classList` to toggle the class `hidden` on `taskForm` so the form modal will close too. + +# --hints-- + +You should call the `addEventListener()` method on your `discardBtn` variable. + +```js +assert.match(code, /discardBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{/) +``` + +Your event listener should use the `close()` method on `confirmCloseDialog`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?/) +``` + +Your event listener should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?\s*taskForm\.classList\.toggle\(\1hidden\1\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md new file mode 100644 index 00000000000..f4002dc0937 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md @@ -0,0 +1,326 @@ +--- +id: 64e4ecd7735a566c9266a338 +title: Step 10 +challengeType: 0 +dashedName: step-10 +--- + +# --description-- + +Now that you've worked on opening and closing the modal, it's time to get the values from the input fields, save them into the `taskData` array, and display them on the page. + +To start, add a `submit` event listener to your `taskForm` element and pass in `e` as the parameter of your arrow function. Inside the curly braces, use the `preventDefault()` method to stop the browser from refreshing the page after submitting the form. + +# --hints-- + +You should call the `addEventListener()` method on your `taskForm` variable. + +```js +assert.match(code, /taskForm\.addEventListener\(/) +``` + +Your event listener should listen for a `submit` event. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces with `e` as the parameter. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{/) +``` + +You should use the `e.preventDefault()` method to stop the browser from reloading the page. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{\s*e\.preventDefault\(\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md new file mode 100644 index 00000000000..148e52f3e93 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md @@ -0,0 +1,337 @@ +--- +id: 64e4eec13546c06d61a63d59 +title: Step 11 +challengeType: 0 +dashedName: step-11 +--- + +# --description-- + +You need to determine whether the task being added already exists or not. If it doesn't exist, you will add it, and if it already exists, you will set it up for editing. You can use the `findIndex()` method to accomplish this. + +`findIndex` is an array method that lets find the index of the first element in an array that satisfies a given testing function. + +Here's an example: + +```js +const numbers = [3, 1, 5, 6, 10, 9, 8]; +const firstEvenNumIndex = numbers.findIndex((num) => num % 2 === 0); + +console.log(firstEvenNumIndex); // Output: 3 – because the first even number (6) is at index 3 +``` + +Declare a `dataArrIndex` variable using `const` and set it to the result of the `findIndex()` method applied to the `taskData` array. Utilize arrow syntax to provide a callback function with `item` as the parameter, and within the callback, check if the `id` property of `item` is equal to the `id` property of `currentTask`. + +If the task exists, this returns the index, and if it doesn't exist, it returns `-1`. + +# --hints-- + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === currentTask.id`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?\s*=>\s*item.id\s*===\s*currentTask.id\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md new file mode 100644 index 00000000000..10826de6f17 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md @@ -0,0 +1,340 @@ +--- +id: 64e4f01d6c72086e016a8626 +title: Step 12 +challengeType: 0 +dashedName: step-12 +--- + +# --description-- + +Next, retrieve the values from the input fields and store them in a `taskObj` object. Each task should also have a unique `id`. + +Create a `taskObj` object with an `id` property as the first property. For the value of the `id` property, retrieve the value of the `titleInput` field, convert it to lowercase, and then use the `split()` and `join()` methods to hyphenate it. + +Make sure all of those are in template literals because you need the `id` property value as a string. + +# --hints-- + +You should use `const` to create a `taskObj` object. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*/) +``` + +Your `taskObj` object should have a key of `id`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id/) +``` + +You should use template strings to get `titleInput.value` as the value of your `id` key and convert it to lowercase. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)/) +``` + +You should use `.split(' ')` on `titleInput.value.toLowerCase()`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)/) +``` + +You should use `.join('-')` on `titleInput.value.toLowerCase().split(' ')`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md new file mode 100644 index 00000000000..764de1b0918 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md @@ -0,0 +1,327 @@ +--- +id: 64ec89ee549ecf802de2b3e2 +title: Step 14 +challengeType: 0 +dashedName: step-14 +--- + +# --description-- + +Retrieve the values from the `titleInput`, `dateInput`, and `descriptionInput` fields, and then save them in the properties `title`, `date`, and `description` of the `taskObj` object. + +# --hints-- + +You should get the value of `titleInput` and save it in a `title` key. + +```js +assert.match(code, /title:\s*titleInput\.value,/) +``` + +You should get the value of `dateInput` and save it in a `date` key. + +```js +assert.match(code, /date:\s*dateInput\.value,/) +``` + +You should get the value of `descriptionInput` and save it in a `description` key. + +```js +assert.match(code, /description:\s*descriptionInput\.value,?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + const taskObj = { + id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`, + --fcc-editable-region-- + + --fcc-editable-region-- + }; +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md new file mode 100644 index 00000000000..3d009188d81 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md @@ -0,0 +1,332 @@ +--- +id: 64ec8f717b261e824d82d6a5 +title: Step 15 +challengeType: 0 +dashedName: step-15 +--- + +# --description-- + +Now that you have obtained the values from the input fields and generated an `id`, you want to add them to your `taskData` array to keep track of each task. However, you should only do this if the task is new. If the task already exists, you will set it up for editing. This is why you have the `dataArrIndex` variable, which provides the index of each task. + +Create an `if` statement with the condition `dataArrIndex === -1`. Within the `if` statement, use the `unshift()` method to add the `taskObj` object to the beginning of the `taskData` array. + +`unshift()` is an array method that is used to add one or more elements to the beginning of an array. + +**N.B:** Try adding a task and log `taskData` to the console to see what it looks like. + +# --hints-- + +You should create an `if` statement with the condition `dataArrIndex === -1`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{/) +``` + +Your `if` statement should have `taskData.unshift(taskObj)` in it's body. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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, + }; + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md new file mode 100644 index 00000000000..f1202bb93fe --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md @@ -0,0 +1,331 @@ +--- +id: 64ec9145e424d8835a4e0f28 +title: Step 16 +challengeType: 0 +dashedName: step-16 +--- + +# --description-- + +Now that you have saved the task in the `taskData` array, you should display the task on the page by looping through it. + +Use `forEach()` on `taskData`, then destructure `id`, `title`, `date`, `description` as the parameters. Don't return anything yet. + +# --hints-- + +You should use `forEach()` on `taskData`. + +```js +assert.match(code, /taskData\.forEach\(\s*/) +``` + +You should destructure `id`, `title`, `date`, `description` as the parameters of the callback of your `forEach()`. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md new file mode 100644 index 00000000000..06bdd7c6ae5 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md @@ -0,0 +1,336 @@ +--- +id: 64ec9282cd547785258cecf2 +title: Step 17 +challengeType: 0 +dashedName: step-17 +--- + +# --description-- + +Using arrow syntax, create an implicit return and use addition assignment to set the `innerHTML` of `tasksContainer` to empty backticks. + +# --hints-- + +You should not alter the existing `taskData.forEach()` and the values you destructured. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +You should use arrow syntax and attach `innerHTML` to `tasksContainer`. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\(\s*tasksContainer\.innerHTML\s*\))?/) +``` + +You should use addition assignment to set the `innerHTML` of `tasksContainer` to an empty pair of backticks. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\s*\(?tasksContainer\.innerHTML\s*\+=\s*`\s*`\)?)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + taskData.forEach(({id, title, date, description})); + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md new file mode 100644 index 00000000000..b5115f99a51 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md @@ -0,0 +1,333 @@ +--- +id: 64ec9343769e8f85c1e17e05 +title: Step 18 +challengeType: 0 +dashedName: step-18 +--- + +# --description-- + +Create a `div` element with the class of `task`. Utilize template strings to set the `id` attribute of the `div` to the `id` you destructured from the task data. + +# --hints-- + +You should create a `div` element with the class `task`. + +```js +assert.match(code, /\s*<\/div>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` + --fcc-editable-region-- + + --fcc-editable-region-- + `) + ); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md new file mode 100644 index 00000000000..81dd3530420 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md @@ -0,0 +1,348 @@ +--- +id: 64ec94f0de20c086e09b0fc3 +title: Step 19 +challengeType: 0 +dashedName: step-19 +--- + +# --description-- + +Create a `p` element and use template strings to set its content to the `title` you destructured. Right before the content of the `p` element, create a `strong` element with the text `Title:`. + +# --hints-- + +You should create a `p` element. + +```js +assert.match(code, /

/) +``` + +You should interpolate `${title}` as the text of your `p` element. + +```js +assert.match(code, /

.*\$\{title\}<\/p>/) +``` + +You should create a `strong` element after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)/) +``` + +Your `strong` element should have the text `Title:`. + +```js +assert.match(code, /(?<=

)Title:\s*<\/strong>\s*/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md new file mode 100644 index 00000000000..4820d100d4d --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md @@ -0,0 +1,336 @@ +--- +id: 64ec959a76336c8767f5cd4d +title: Step 20 +challengeType: 0 +dashedName: step-20 +--- + +# --description-- + +Similarly to the previous step, create another `p` element, and interpolate the `date` you destructured as the text content. Inside this paragraph, create a `strong` element with the text `Date:`. + +# --hints-- + +You should create a `p` element and interpolate `${date}` as the text. + +```js +assert.match(code, /

.*\$\{date\}<\/p>/) +``` + +You should create a `strong` element with the text `Date:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Date:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md new file mode 100644 index 00000000000..4525b0e154c --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md @@ -0,0 +1,340 @@ +--- +id: 64ec96761156a187ed32b274 +title: Step 22 +challengeType: 0 +dashedName: step-22 +--- + +# --description-- + +To allow for task management, you need to include both a delete and an edit button for each task. + +Create two `button` elements with the `type` attribute set to `button` and the `class` attribute set to `btn`. Set the text of the first button to `Edit` and the text of the second button to `Delete`. + +# --hints-- + +You should create a `button` element of type `button`, a class `btn` and `Edit` as the text, in that order. + +```js +assert.match(code, /Edit<\/button/) +``` + +You should create a `button` element of type `button` a class `btn` and `Delete` as the text, in that order. + +```js +assert.match(code, /Delete<\/button/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md new file mode 100644 index 00000000000..0081fa88e1a --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md @@ -0,0 +1,336 @@ +--- +id: 64ec9b10356c2d8aa05d9ce1 +title: Step 23 +challengeType: 0 +dashedName: step-23 +--- + +# --description-- + +After adding the task to the page, you should close the form modal to view the task. To do this, utilize `classList` to toggle the `hidden` class on the `taskForm` element. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +const splitter = code.split("taskData.forEach") +assert.match(splitter[1], /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md new file mode 100644 index 00000000000..6ae1e4dff48 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md @@ -0,0 +1,347 @@ +--- +id: 64ec9c55fdeef78bacd2fc3b +title: Step 24 +challengeType: 0 +dashedName: step-24 +--- + +# --description-- + +If you attempt to add another task now, you'll notice that the input fields retain the values you entered for the previous task. To resolve this, you need to clear the input fields after adding a task. + +Instead of clearing the input fields one by one, it's a good practice to create a function that handles clearing those fields. You can then call this function whenever you need to clear the input fields again. + +Use arrow syntax to create a `reset` function and set it to a pair of curly braces. + +# --hints-- + +You should use `const` and arrow syntax to create a `reset` function. + +```js +assert.match(code, /const\s+reset\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +Your `reset` function should be empty. + +```js +assert.match(reset.toString(), /\(\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md new file mode 100644 index 00000000000..e12fb12ab81 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md @@ -0,0 +1,366 @@ +--- +id: 64fac365aeb8ad70b69b366f +title: Step 25 +challengeType: 0 +dashedName: step-25 +--- + +# --description-- + +Inside the `reset` function, set each value of `titleInput`, `dateInput`, `descriptionInput` to an empty string. + +Also, use `classList` to toggle the class `hidden` on the `taskForm` and set `currentTask` to an empty object. That's because at this point, `currentTask` will be filled with the task the user might have added. + +# --hints-- + +You should set `titleInput.value` to an empty string. + +```js +assert.match(reset.toString(), /titleInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `dateInput.value` to an empty string. + +```js +assert.match(reset.toString(), /dateInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `descriptionInput.value` to an empty string. + +```js +assert.match(reset.toString(), /descriptionInput\.value\s*=\s*('|")\1;?/) +``` + +You should use `classList` to toggle the class `hidden` on `taskForm` + +```js +assert.match(reset.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +You should set `currentTask` to an empty object. + +```js +assert.match(reset.toString(), /currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- +const reset = () => { + +} +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md new file mode 100644 index 00000000000..5f281d0de16 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md @@ -0,0 +1,351 @@ +--- +id: 64fac4d1773e7a719b1254de +title: Step 26 +challengeType: 0 +dashedName: step-26 +--- + +# --description-- + +Remove the existing code toggling the class of `hidden` on `taskForm` and call the `reset` function instead. This would clear the input fields and also hide the form modal for the user to see the added task. + +# --hints-- + +Yous should remove the code toggling the `hidden` class on `taskForm`. + +```js +const splitter = code.split('') +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /reset\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + +--fcc-editable-region-- + taskForm.classList.toggle("hidden"); +--fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md new file mode 100644 index 00000000000..c1a7b0f8965 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md @@ -0,0 +1,351 @@ +--- +id: 64fac6a497811572b338e5e5 +title: Step 27 +challengeType: 0 +dashedName: step-27 +--- + +# --description-- + +Also, remove the existing code toggling the class `hidden` on `taskForm` inside the `discardBtn` event listener and call the `reset` function instead. That's because when you click the `Discard` button, everything in the input fields should go away. + +# --hints-- + +Yous should remove the code toggling the class `hidden` on `taskForm`. + +```js +const splitter = code.split("confirmCloseDialog.close();") +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /confirmCloseDialog\.close\(\);?\s*reset\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); +--fcc-editable-region-- + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md new file mode 100644 index 00000000000..84005e1b01a --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md @@ -0,0 +1,346 @@ +--- +id: 64faca774fd9fd74bc084cc9 +title: Step 28 +challengeType: 0 +dashedName: step-28 +--- + +# --description-- + +You should display the `Cancel` and `Discard` buttons to the user only if there is some text present in the input fields. + +To begin, within the `closeTaskFormBtn` event listener, create a `formInputsContainValues` variable to check if there is a value in the `titleInput` field **or** the `dateInput` field **or** the `descriptionInput` field. + +# --hints-- + +You should use `const` to create a variable `formInputsContainValues` with the value `titleInput.value || dateInput.value || descriptionInput.value;` + +```js +assert.match(code, /const\s*formInputsContainValues\s*=\s*titleInput\.value\s*\|\|\s*dateInput\.value\s*\|\|\s*descriptionInput\.value;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); + +}); +--fcc-editable-region-- + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md new file mode 100644 index 00000000000..07482464ebf --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md @@ -0,0 +1,362 @@ +--- +id: 64facf6180824876f70a2e86 +title: Step 29 +challengeType: 0 +dashedName: step-29 +--- + +# --description-- + +Create an `if` statement to check if `formInputsContainValues` is true. If `formInputsContainValues` is true, indicating that there are changes, use the `showModal()` method on `confirmCloseDialog`. Otherwise, if there are no changes, call the `reset()` function to clear the input fields and hide the form modal. + +# --hints-- + +You should create an `if` statement with the condition `formInputsContainValues`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{/) +``` + +The `if` block of your `if` statement should contain `confirmCloseDialog.showModal();`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?/) +``` + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{/) +``` + +You should call the `reset()` function in the `else` block of your `if` statement. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{\s*reset\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 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; +--fcc-editable-region-- + confirmCloseDialog.showModal(); +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md new file mode 100644 index 00000000000..95b71fba69f --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md @@ -0,0 +1,372 @@ +--- +id: 64fad07f43a101779cb8692a +title: Step 30 +challengeType: 0 +dashedName: step-30 +--- + +# --description-- + +You can enhance code readability and maintainability by refactoring the `submit` event listener into two separate functions. The first function can be used to add the input values to `taskData`, while the second function can be responsible for adding the tasks to the DOM. + +Use arrow syntax to create an `addOrUpdateTask` function. Then move the `dataArrIndex` variable, the `taskObj` object, and the `if` statement into the `addOrUpdateTask` function. + +# --hints-- + +You should use `const` and arrow syntax to create an `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +You should move the `dataArrIndex` variable into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?/) +``` + +You should move the `taskObj` object into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?/) +``` + +You should move the `if` statement with the condition `dataArrIndex === 1` into your `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?\s*if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md new file mode 100644 index 00000000000..8192699bb61 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md @@ -0,0 +1,358 @@ +--- +id: 64fad9cd2eeb1e7ca2ca8c8b +title: Step 31 +challengeType: 0 +dashedName: step-31 +--- + +# --description-- + +Use arrow syntax to create an `updateTaskContainer` function. Then move the `taskData.forEach()` and its content into it. + +# --hints-- + +You should use `const` and arrow syntax to create a `updateTaskContainer` function. + +```js +assert.match(code, /const\s*updateTaskContainer\s*=\s*\(\)\s*=>\s*\{/) +``` + +You should move `taskData.forEach()` and its content into the `updateTaskContainer()` function. + +```js +assert.match(code, /const\s+updateTaskContainer\s+=\s*\(\)\s*=>\s*\{\s*taskData\.forEach\(\s*\(\{\s*id,\s*title,\s*date,\s*description\s*\}\)\s*=>\s*\(tasksContainer\.innerHTML\s*\+=\s*`\s*\s*

Title:<\/strong>\s*\$\{title\}<\/p>\s*

Date:<\/strong>\s*\$\{date\}<\/p>\s*

Description:<\/strong>\s*\$\{description\}<\/p>\s*Edit<\/button>\s*Delete<\/button>\s*<\/div>\s*`\)\s*\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md new file mode 100644 index 00000000000..18b2a462de4 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md @@ -0,0 +1,360 @@ +--- +id: 64fadae4f2d51b7d5d8b98d8 +title: Step 32 +challengeType: 0 +dashedName: step-32 +--- + +# --description-- + +Inside the `addOrUpdateTask` function, call the `updateTaskContainer` and `reset` functions. + +# --hints-- + +You should call the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\)\s*/) +``` + +You should call the `reset` function after calling the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\);?\s*reset\(\);?\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + reset() +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md new file mode 100644 index 00000000000..657bb4aa05b --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md @@ -0,0 +1,355 @@ +--- +id: 64fadff23375f27ff06c6d40 +title: Step 33 +challengeType: 0 +dashedName: step-33 +--- + +# --description-- + +Now remove the `reset()` call inside the `taskForm` submit event listener and call the `addOrUpdateTask` function instead. + +# --hints-- + +You should remove `reset()` and call `addOrUpdateTask()`. + +```js +assert.match(code, /addOrUpdateTask\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + --fcc-editable-region-- + reset() + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md new file mode 100644 index 00000000000..bd759fb7e5f --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md @@ -0,0 +1,364 @@ +--- +id: 64fae068bcdc9c805bd8399e +title: Step 35 +challengeType: 0 +dashedName: step-35 +--- + +# --description-- + +To enable editing and deleting for each task, add an `onclick` attribute to both buttons. Set the value of the `onclick` attribute to `editTask(this)` for the `Edit` button and `deleteTask(this)` for the `Delete` button. The `editTask(this)` function will handle editing, while the `deleteTask(this)` function will handle deletion. + +`this` is a keyword that refers to the current context. In this case, `this` points to the element that triggers the event – the buttons. + +# --hints-- + +You should add `onclick="editTask(this)"` as the first attribute of the edit button. + +```js +assert.match(code, /Edit<\/button>/) +``` + +You should add `onclick="deleteTask(this)"` as the first attribute of the delete button. + +```js +assert.match(code, /Delete<\/button>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + + --fcc-editable-region-- +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md new file mode 100644 index 00000000000..c6fac350818 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md @@ -0,0 +1,371 @@ +--- +id: 64faedcd16a1e985c4c2dc94 +title: Step 36 +challengeType: 0 +dashedName: step-36 +--- + +# --description-- + +Create a `deleteTask` function using arrow syntax. Pass `buttonEl` as the parameter and define an empty set of curly braces for the function body. + +# --hints-- + +You should use `const` and arrow syntax to create a `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `deleteTask` function should take a `buttonEl` parameter. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `deleteTask` function should be empty. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md new file mode 100644 index 00000000000..27631c77f2f --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md @@ -0,0 +1,359 @@ +--- +id: 64faf0418e828c0114a558a7 +title: Step 34 +challengeType: 0 +dashedName: step-34 +--- + +# --description-- + +There's a problem. If you add a task, and then add another, the previous task gets duplicated. This means you need to clear out the existing contents of `tasksContainer` before adding a new task. + +Set the `innerHTML` of `taskContainer` back to an empty string. + + +# --hints-- + +You should set the `innerHTML` of `tasksContainer` to an empty string. + +```js +assert.match(code, /tasksContainer\.innerHTML\s*=\s*("|')\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + --fcc-editable-region-- + + --fcc-editable-region-- + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md new file mode 100644 index 00000000000..9cdbd7860ca --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md @@ -0,0 +1,382 @@ +--- +id: 64faf65b22ad8d07df9be14d +title: Step 37 +challengeType: 0 +dashedName: step-37 +--- + +# --description-- + +You need to find the index of the task you want to delete first. + +Create a `dataArrIndex` variable and set its value using the `findIndex()` method on the `taskData` array. Pass `item` as the parameter for the arrow callback function, and within the callback, check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. Don't use curly braces. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?\s*item\s*\)?\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\s*\);?\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md new file mode 100644 index 00000000000..0e54adadf06 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md @@ -0,0 +1,398 @@ +--- +id: 64faf874364ec308f875f636 +title: Step 38 +challengeType: 0 +dashedName: step-38 +--- + +# --description-- + +You need to remove the task from the DOM using `remove()` and from the `taskData` array using `splice()`. + +`splice()` is an array method that modifies arrays by removing, replacing, or adding elements at a specified index, while also returning the removed elements. It can take up to three parameters: the first one is the mandatory index at which to start, the second is the number of items to remove, and the third is an optional replacement element. Here's an example: + +```js +const fruits = ["mango", "date", "cherry", "banana", "apple"]; + +// Remove date and cherry from the array starting at index 1 +const removedFruits = fruits.splice(1, 2); + +console.log(fruits); // [ 'mango', 'banana', 'apple' ] +console.log(removedFruits); // [ 'date', 'cherry' ] +``` + +Use the `remove()` method to remove the `parentElement` of the `buttonEl` from the DOM. Then use `splice()` to remove the task from the `taskData` array. Pass in `dataArrIndex` and `1` as the parameters of your `splice()`. + +`dataArrIndex` is the index to start and `1` is the number of items to remove. + +# --hints-- + +You should use the `remove()` method to remove the parent element of `buttonEl`. + +```js +assert.match(deleteTask.toString(), /buttonEl\.parentElement\.remove\(\);?/) +``` + +You should use `splice()` on the `taskData` array. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(/) +``` + +The first parameter of your `splice()` method should be `dataArrIndex`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex/) +``` + +The second parameter of your `splice()` method should be `1`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex,\s*1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md new file mode 100644 index 00000000000..3f379d51764 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md @@ -0,0 +1,381 @@ +--- +id: 64fafac95328110a69bcb75f +title: Step 39 +challengeType: 0 +dashedName: step-39 +--- + +# --description-- + +Use arrow syntax to create an `editTask` function. Pass in `buttonEl` as the parameter and add empty curly braces for the body. + +# --hints-- + +You should use `const` and arrow syntax to create an `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `editTask` function should take a `buttonEl` parameter. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `editTask` function should be empty. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md new file mode 100644 index 00000000000..bc0e2a84503 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md @@ -0,0 +1,391 @@ +--- +id: 64fb0fa0968f2b113b2d90e9 +title: Step 40 +challengeType: 0 +dashedName: step-40 +--- + +# --description-- + +As you did in the `deleteTask` function, you need to find the index of the task to be edited. + +Create a `dataArrIndex` variable. For its value, utilize the `findIndex()` method on `taskData`. Pass `item` as the parameter to its callback function and check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md new file mode 100644 index 00000000000..14d527ecf40 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md @@ -0,0 +1,373 @@ +--- +id: 64fb1061ca838611ed6a7d6b +title: Step 41 +challengeType: 0 +dashedName: step-41 +--- + +# --description-- + +Use square bracket notation to retrieve the task to be edited from the `taskData` array using the `dataArrIndex`. Then, assign it to the `currentTask` object to keep track of it. + +# --hints-- + +You should assign `taskData[dataArrIndex]` to `currentTask`. + +```js +assert.match(code, /currentTask\s*=\s*taskData\[dataArrIndex\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex((item) => item.id === buttonEl.parentElement.id); + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md new file mode 100644 index 00000000000..73ef19befcc --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md @@ -0,0 +1,389 @@ +--- +id: 64fb1321e189a6136d200f77 +title: Step 42 +challengeType: 0 +dashedName: step-42 +--- + +# --description-- + +The task to be edited is now in the `currentTask` object. Stage it for editing inside the input fields by setting the value of `titleInput` to `currentTask.title`, `dateInput` to `currentTask.date`, and `descriptionInput` to `currentTask.description`. + +# --hints-- + +You should set `titleInput.value` to `currentTask.title`. + +```js +assert.match(editTask.toString(), /titleInput\.value\s*=\s*currentTask\.title;?/) +``` + +You should set `dateInput.value` to `currentTask.date`. + +```js +assert.match(editTask.toString(), /dateInput\.value\s*=\s*currentTask\.date;?/) +``` + +You should set `descriptionInput.value` to `currentTask.description`. + +```js +assert.match(editTask.toString(), /descriptionInput\.value\s*=\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + currentTask = taskData[dataArrIndex]; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md new file mode 100644 index 00000000000..c6312bf6bd0 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md @@ -0,0 +1,386 @@ +--- +id: 64fb1436adef3e145b4c3501 +title: Step 43 +challengeType: 0 +dashedName: step-43 +--- + +# --description-- + +Set the `innerText` of the `addOrUpdateTaskBtn` button to `Update Task` and its `aria-label` to `Update Task` as well. + +# --hints-- + +You should set the inner text of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.innerText\s*=\s*("|')Update Task\1;?/) +``` + +You should set the `ariaLabel` of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.ariaLabel\s*=\s*("|')Update Task\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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; + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md new file mode 100644 index 00000000000..101870d098a --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md @@ -0,0 +1,384 @@ +--- +id: 64fb14d890415c14f93069ce +title: Step 44 +challengeType: 0 +dashedName: step-44 +--- + +# --description-- + +Finally, display the `form` modal with the values of the input fields by using `classList` to toggle the `hidden` class on `taskForm`. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(editTask.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md new file mode 100644 index 00000000000..7ad62060776 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md @@ -0,0 +1,391 @@ +--- +id: 64fb154a7c48cd159924bb18 +title: Step 45 +challengeType: 0 +dashedName: step-45 +--- + +# --description-- + +At this point, editing a task won't reflect when you submit the task. To make the editing functional, go back to the `if` statement inside the `addOrUpdateTask` function. Create an `else` block and set `taskData[dataArrIndex]` to `taskObj`. + +# --hints-- + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*/) +``` + +Your `else` block should have the code `taskData[dataArrIndex] = taskObj`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*taskData\[dataArrIndex\]\s*=\s*taskObj;?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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, + }; + + --fcc-editable-region-- + if (dataArrIndex === -1) { + taskData.unshift(taskObj); + } + + --fcc-editable-region-- + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md new file mode 100644 index 00000000000..4ec33172878 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md @@ -0,0 +1,401 @@ +--- +id: 64fb1c4dc0feb219149a7c7d +title: Step 46 +challengeType: 0 +dashedName: step-46 +--- + +# --description-- + +If the user attempts to edit a task but decides not to make any changes before closing the form, there is no need to display the modal with the `Cancel` and `Discard` buttons. + +Inside the `closeTaskFormBtn` event listener, use `const` to create another variable named `formInputValuesUpdated`. Check if the user made changes while trying to edit a task by verifying that the `titleInput` value **is not equal to** `currentTask.title`, the `dateInput` value **is not equal to** `currentTask.date`, and the `descriptionInput` value **is not equal to** `currentTask.description`. + +# --hints-- + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*/) +``` + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title` or `dateInput.value` is not equal to `currentTask.date`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date/) +``` + +Your `formInputValuesUpdated` variable should have the value `titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date\s*\|\|\s*descriptionInput\.value\s*!==\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + --fcc-editable-region-- + + --fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md new file mode 100644 index 00000000000..bb8a87acf60 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md @@ -0,0 +1,390 @@ +--- +id: 64fb285637fa1e1c222033e3 +title: Step 47 +challengeType: 0 +dashedName: step-47 +--- + +# --description-- + +Now add `formInputValuesUpdated` as the second mandatory condition in the `if` statement using the `AND` operator. + +This way, the `Cancel` and `Discard` buttons in the modal won't be displayed to the user if they haven't made any changes to the input fields while attempting to edit a task. + +# --hints-- + +Your `if` should have the condition `formInputsContainValues && formInputValuesUpdated`. + +```js +assert.match(code.split("if (formInputsContainValues"), /\s*&&\s*formInputValuesUpdated/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description; + +--fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md new file mode 100644 index 00000000000..94fdc40185a --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md @@ -0,0 +1,414 @@ +--- +id: 64fb29348a60361ccd45c1e2 +title: Step 48 +challengeType: 0 +dashedName: step-48 +--- + +# --description-- + +`localStorage` offers methods for saving, retrieving, and deleting items. The items you save can be of any JavaScript data type. + +For instance, the `setItem()` method is used to save an item, and the `getItem()` method retrieves the item. To delete a specific item, you can utilize the `removeItem()` method, or if you want to delete all items in the storage, you can use `clear()`. + +Here's how you can save an item: + +```js +localStorage.setItem("key", value); // value could be string, number, or any other data type +``` + +A `myTaskArr` array has been provided for you. Use the `setItem()` method to save it with a key of `data`. + +After that, open your browser console and go to the `Applications` tab, select `Local Storage`, and the freeCodeCamp domain you see. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +Your `localStorage.setItem()` should have a key of `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*myTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md new file mode 100644 index 00000000000..2ce0b111937 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md @@ -0,0 +1,404 @@ +--- +id: 64fefebad99209211ec30537 +title: Step 49 +challengeType: 0 +dashedName: step-49 +--- + +# --description-- + +If you check the `Application` tab of your browser console, you'll notice a series of `[object Object]`. This is because everything you save in `localStorage` needs to be in string format. + +To resolve the issue, wrap the data you're saving in the `JSON.stringify()` method. Then, check local storage again to observe the results. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +You should wrap `JSON.stringify()` around `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*JSON\.stringify\(myTaskArr\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- +localStorage.setItem("data", myTaskArr); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md new file mode 100644 index 00000000000..1cb0c0c5930 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md @@ -0,0 +1,406 @@ +--- +id: 64ff0313700dad264d19dfe4 +title: Step 50 +challengeType: 0 +dashedName: step-50 +--- + +# --description-- + +Now that you have the `myTaskArr` array saved in `localStorage` correctly, you can retrieve it with `getItem()` by specifying the key you used to save the item. + +Use the `getItem()` method to retrieve the `myTaskArr` array and assign it to the variable `getTaskArr`. Then, log the `getTaskArr` variable to the console to see the result. + +# --hints-- + +You should use `const` to create a `getTaskArr` variable and assign `localStorage.getItem("data")` to it. + +```js +assert.match(code, /const\s*getTaskArr\s*=\s*localStorage\.getItem\(('|")data\1\);?/) +``` + +You should log the `getTaskArr` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md new file mode 100644 index 00000000000..608713897b5 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md @@ -0,0 +1,411 @@ +--- +id: 64ff04cc33779427a6412449 +title: Step 51 +challengeType: 0 +dashedName: step-51 +--- + +# --description-- + +The item you retrieve is a string, as you saved it with `JSON.stringify()`. To view it in its original form before saving, you need to use `JSON.parse()`. + +Use `getItem()` to retrieve the `myTaskArr` array again. This time, wrap it inside `JSON.parse()`, assign it to the variable `getTaskArrObj` and log the `getTaskArrObj` to the console. + +Check the console to see the difference between `getTaskArr` and `getTaskObj`. + +# --hints-- + +You should use `const` to create a `getTaskArrObj` variable and assign it to `JSON.parse(localStorage.getItem('data'));`. + +```js +assert.match(code, /const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\(('|")data\1\)\);?/) +``` + +You should log the `getTaskArrObj` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md new file mode 100644 index 00000000000..09bad9433fa --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md @@ -0,0 +1,406 @@ +--- +id: 64ff068e0426eb288874ed79 +title: Step 52 +challengeType: 0 +dashedName: step-52 +--- + +# --description-- + +You can use `localStorage.removeItem()` to remove a specific item and `localStorage.clear()` to clear all items in the local storage. + +Remove the `data` item from local storage and open the console to observe the result. You should see `null`. + +# --hints-- + +You should use `localStorage.removeItem()` to remove the `data` item from the browser's local storage. + +```js +assert.match(code, /localStorage\.removeItem\(('|")data\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md new file mode 100644 index 00000000000..5dc3bd6e1d1 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md @@ -0,0 +1,413 @@ +--- +id: 64ff23daf176a92de95f24dc +title: Step 53 +challengeType: 0 +dashedName: step-53 +--- + +# --description-- + +Using `localStorage.clear()` won't just delete a single item from local storage but will remove all items. + +Remove `localStorage.removeItem()` and use `localStorage.clear()` instead. You don't need to pass in anything. You should also see `null` in the console. + +# --hints-- + +You should remove `localStorage.removeItem("data")`. + +```js +assert.notMatch(code, /localStorage\.removeItem\(('|")data\1\);/) +``` + +You should remove everything from the browser `local storage` with `localStorage.clear()`. + +```js +assert.match(code, /localStorage\.clear\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- +localStorage.removeItem("data"); + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md new file mode 100644 index 00000000000..0d6c42c989e --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md @@ -0,0 +1,404 @@ +--- +id: 64ff24b80431f62ec6b93f65 +title: Step 54 +challengeType: 0 +dashedName: step-54 +--- + +# --description-- + +Remove the `myTaskArr` array and all of the code for `localStorage` because you don't need them anymore. + +# --hints-- + +You should remove `myTaskArr` and all the code related to `localStorage` that you've just learned. + +```js +assert.notMatch(code, /const\s*myTaskArr\s*=\s*\[\s*\{\s*task:\s"Walk\s*the\s*Dog",\s*date:\s*"22-04-2022"\s*\},\s*\{\s*task:\s"Read\s*some\s*books",\s*date:\s*"02-11-2023"\s*\},\s*\{\s*task:\s"Watch\s*football",\s*date:\s*"10-08-2021"\s*\},\s*\];?\s*localStorage\.setItem\("data", JSON\.stringify\(myTaskArr\)\);\s*localStorage\.clear\(\);?\s*const\s*getTaskArr\s*=\s*localStorage\.getItem\("data"\)\s*console\.log\(getTaskArr\)\s*const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\("data"\)\);?\s*console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +--fcc-editable-region-- +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +localStorage.clear(); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md new file mode 100644 index 00000000000..cbbcd4df6e1 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md @@ -0,0 +1,404 @@ +--- +id: 65003986d17d1e1865b269c0 +title: Step 55 +challengeType: 0 +dashedName: step-55 +--- + +# --description-- + +Now you should save the task items to local storage when the user adds, updates, or removes a task. + +Inside the `addOrUpdateTask` function, use `setItem()` to save the tasks with a key of `data`, then pass the `taskData` array as its parameter. Ensure that you stringify the `taskData`. + +This would persist data once the user adds or updates tasks. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +assert.match(code, /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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; + } + --fcc-editable-region-- + + --fcc-editable-region-- + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md new file mode 100644 index 00000000000..ec3a700096a --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md @@ -0,0 +1,407 @@ +--- +id: 650046832f92c01a35834bca +title: Step 56 +challengeType: 0 +dashedName: step-56 +--- + +# --description-- + +You also want a deleted task to be removed from local storage. For this, you don't need the `removeItem()` or `clear()` methods. Since you already use `splice()` to remove the deleted task from `taskData`, all you need to do now is save `taskData` to local storage again. + +Use `setItem()` to save the `taskData` array again. Pass in `data` as the key and ensure that `taskData` is stringified before saving. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); + --fcc-editable-region-- + + --fcc-editable-region-- +} + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md new file mode 100644 index 00000000000..40cf6384c4e --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md @@ -0,0 +1,392 @@ +--- +id: 650048b0764f9c1b798200e2 +title: Step 57 +challengeType: 0 +dashedName: step-57 +--- + +# --description-- + +If you add, update, or remove a task, it should reflect in the UI. However, that's not happening now because you have yet to retrieve the tasks. To do this, you need to modify your initial `taskData` to be an empty array. + +Set `taskData` to the retrieval of `data` from local storage **or** an empty array. Make sure you parse the data coming with `JSON.parse()` because you saved it as a string. + +# --hints-- + +You should set the `taskData` variable to `JSON.parse(localStorage.getItem("data")) || [];`. + +```js +assert.match(code, /const\s*taskData\s*=\s*JSON.parse\(localStorage\.getItem\(('|")data\1\)\)\s*\|\|\s*\[\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- +const taskData = []; +--fcc-editable-region-- +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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md new file mode 100644 index 00000000000..08ced41b63f --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md @@ -0,0 +1,776 @@ +--- +id: 65004ba581d03d1d5628b41c +title: Step 58 +challengeType: 0 +dashedName: step-58 +--- + +# --description-- + +You've retrieved the task item(s) now, but they still don't reflect in the UI when the page loads. However, they appear when you add a new task. + +To fix this issue, you can check if there's a task inside `taskData`, and then call the `updateTaskContainer()`. + +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 the condition `taskData.length`. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*/) +``` + +You should call the `updateTaskContainer` function in your `if` statement. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*updateTaskContainer\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + taskForm.classList.toggle("hidden"); +} + +const reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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 + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md new file mode 100644 index 00000000000..6b21e6888ce --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md @@ -0,0 +1,314 @@ +--- +id: 650300a25b6f72964ab8aca6 +title: Step 13 +challengeType: 0 +dashedName: step-13 +--- + +# --description-- + +To make the `id` more unique, add another hyphen and use `Date.now()`. + +# --hints-- + +You should attach `-${Date.now()}` to the existing value of the `id` key. Don't forget it needs to be inside the template string. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + const taskObj = { + id: `${titleInput.value.toLowerCase().split(' ').join('-')}`, + }; + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md new file mode 100644 index 00000000000..0c9fbc152b7 --- /dev/null +++ b/curriculum/challenges/espanol/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md @@ -0,0 +1,337 @@ +--- +id: 65099dbd8f137d58e5c0ff16 +title: Step 21 +challengeType: 0 +dashedName: step-21 +--- + +# --description-- + +Create one more `p` element and interpolate the `description` you destructured as the text. Also, create a `strong` element inside the paragraph with the text `Description:`. + +# --hints-- + +You should create a `p` element with `${description}` as the text. + +```js +assert.match(code, /

.*\$\{description\}<\/p>/) +``` + +You should create a `strong` element with the text `Description:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Description:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/espanol/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md b/curriculum/challenges/espanol/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md index 7cce6981cca..b4cada2af12 100644 --- a/curriculum/challenges/espanol/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md +++ b/curriculum/challenges/espanol/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md @@ -12,7 +12,7 @@ This video will help you understand the equations of simple and compound interes Here is the Colab notebook to go along with this video. -Here is an additional Colab notebook that shows you one way to put many of these interest and payment forumlas into Python functions. Also you will see an example of using some formulas to output results, notice a trend, and follow up with other formulas to analyze patterns. +Here is an additional Colab notebook that shows you one way to put many of these interest and payment formulas into Python functions. Also you will see an example of using some formulas to output results, notice a trend, and follow up with other formulas to analyze patterns. # --question-- diff --git a/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md b/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md index 39ab831dad7..144796de1b0 100644 --- a/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md +++ b/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md @@ -71,8 +71,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md b/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md index 53ae7b50350..871e1408079 100644 --- a/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md +++ b/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md @@ -73,7 +73,7 @@ substringDivisibility(5); ```js function substringDivisibility(n) { - function isSubDivisable(digits) { + function isSubDivisible(digits) { const factors = [2, 3, 5, 7, 11, 13, 17]; for (let i = 1; i < digits.length - 2; i++) { @@ -108,17 +108,17 @@ function substringDivisibility(n) { } const allowedDigits = [...new Array(n + 1).keys()]; - const divisablePandigitals = []; + const divisiblePandigitals = []; heapsPermutations( allowedDigits.length, allowedDigits, - isSubDivisable, - divisablePandigitals + isSubDivisible, + divisiblePandigitals ); let sum = 0; - for (let i = 0; i < divisablePandigitals.length; i++) { - sum += divisablePandigitals[i]; + for (let i = 0; i < divisiblePandigitals.length; i++) { + sum += divisiblePandigitals[i]; } return sum; diff --git a/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md b/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md index 7cd5bc67b14..2d583a52355 100644 --- a/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md +++ b/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md @@ -67,8 +67,8 @@ class PrimeSeive { const prime = 2 * i + 3; primes.push(prime); // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } @@ -101,10 +101,10 @@ class PrimeSeive { }; function consecutivePrimeSum(limit) { - // Initalize seive + // Initialize seive const primeSeive = new PrimeSeive(limit); - // Initalize for longest sum < 100 + // Initialize for longest sum < 100 let bestPrime = 41; let bestI = 0; let bestJ = 5; diff --git a/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md b/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md index cdbfc3e568b..5b55ddd8079 100644 --- a/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md +++ b/curriculum/challenges/espanol/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/espanol/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md b/curriculum/challenges/espanol/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md index 78c29aa7952..4fa15980a0d 100644 --- a/curriculum/challenges/espanol/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md +++ b/curriculum/challenges/espanol/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md @@ -31,10 +31,10 @@ For $1 ≤ k ≤ 200$, find $\sum{m(k)}$. # --hints-- -`efficientExponentation()` should return `1582`. +`efficientExponentiation()` should return `1582`. ```js -assert.strictEqual(efficientExponentation(), 1582); +assert.strictEqual(efficientExponentiation(), 1582); ``` # --seed-- @@ -42,12 +42,12 @@ assert.strictEqual(efficientExponentation(), 1582); ## --seed-contents-- ```js -function efficientExponentation() { +function efficientExponentiation() { return true; } -efficientExponentation(); +efficientExponentiation(); ``` # --solutions-- diff --git a/curriculum/challenges/espanol/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md b/curriculum/challenges/espanol/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md index 49d59948762..e402ff347c5 100644 --- a/curriculum/challenges/espanol/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md +++ b/curriculum/challenges/espanol/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/espanol/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md b/curriculum/challenges/espanol/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md index 596f703da28..b843f6f2be2 100644 --- a/curriculum/challenges/espanol/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md +++ b/curriculum/challenges/espanol/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md @@ -35,7 +35,7 @@ concealedSquare(); ```js // Check if n**2 matches the pattern -function squareMatchs(n) { +function squareMatches(n) { // Need BigInt due to size of values let nSquared = (BigInt(n) * BigInt(n)).toString(); @@ -55,8 +55,8 @@ function concealedSquare() { for (let x = maxSquareRoot; x >= minSquareRoot; x -= 10) { // Note: 3*3 = 9 and 7*7 = 49 are only trailing digits // that can produce 9 as trailing digit in square - if (squareMatchs(x + 3)) return (x + 3)*10; - if (squareMatchs(x + 7)) return (x + 7)*10; + if (squareMatches(x + 3)) return (x + 3)*10; + if (squareMatches(x + 7)) return (x + 7)*10; } return -1; } diff --git a/curriculum/challenges/german/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md b/curriculum/challenges/german/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md index e7a58ff65e8..343b845bc38 100644 --- a/curriculum/challenges/german/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md +++ b/curriculum/challenges/german/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md @@ -54,7 +54,24 @@ assert.match(code, /myStorage\.car\.inside/); `gloveBoxContents` sollte mit `const` deklariert werden. ```js -assert.match(code, /const\s+gloveBoxContents\s*=\s*myStorage\.car\.inside\[\s*("|')glove box\1\s*\]|const\s*{\s*('|")glove box\2:\s*gloveBoxContents\s*}\s*=\s*myStorage\.car\.inside;/); +assert.match(code, /const\s+gloveBoxContents\s*=/); +``` + +You should not change the `myStorage` object. + +```js +const expectedMyStorage = { + "car":{ + "inside":{ + "glove box":"maps", + "passenger seat":"crumbs" + }, + "outside":{ + "trunk":"jack" + } + } +}; +assert.deepStrictEqual(myStorage, expectedMyStorage); ``` # --seed-- diff --git a/curriculum/challenges/german/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md b/curriculum/challenges/german/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md index 86feb017e67..3878e5dffe1 100644 --- a/curriculum/challenges/german/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md +++ b/curriculum/challenges/german/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md @@ -10,7 +10,7 @@ dashedName: catch-misspelled-variable-and-function-names Die Methoden `console.log()` und `typeof` sind die beiden wichtigsten Methoden, um Zwischenwerte und Typen von Programmausgaben zu überprüfen. Jetzt ist es an der Zeit, sich mit den häufigsten Formen von Bugs zu beschäftigen. Ein Problem auf der Ebene der Syntax, mit dem schnelle Tipper mitfühlen können, ist der einfache Rechtschreibfehler. -Vertauschte, fehlende oder falsch großgeschriebene Zeichen in einem Variablen- oder Funktionsnamen führen dazu, dass der Browser nach einem Objekt sucht, das nicht existiert – und sich in Form eines Referenzfehlers beschwert. Bei JavaScript-Variablen- und Funktionsnamen wird zwischen Groß- und Kleinschreibung unterschieden. +Transposed, missing, or miscapitalized characters in a variable or function name will have the browser looking for an object that doesn't exist - and complain in the form of a reference error. Bei JavaScript-Variablen- und Funktionsnamen wird zwischen Groß- und Kleinschreibung unterschieden. # --instructions-- diff --git a/curriculum/challenges/german/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md b/curriculum/challenges/german/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md index 55f9a6c7327..199c6637ef6 100644 --- a/curriculum/challenges/german/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md +++ b/curriculum/challenges/german/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md @@ -58,8 +58,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/german/06-quality-assurance/quality-assurance-projects/issue-tracker.md b/curriculum/challenges/german/06-quality-assurance/quality-assurance-projects/issue-tracker.md index a00a9bf9508..80793a48aa2 100644 --- a/curriculum/challenges/german/06-quality-assurance/quality-assurance-projects/issue-tracker.md +++ b/curriculum/challenges/german/06-quality-assurance/quality-assurance-projects/issue-tracker.md @@ -231,13 +231,13 @@ async (getUserInput) => { }; const url = getUserInput('url') + '/api/issues/fcc-project'; const itemToUpdate = await $.post(url, initialData); - const updateSucccess = await $.ajax({ + const updateSuccess = await $.ajax({ url: url, type: 'PUT', data: { _id: itemToUpdate._id, issue_text: 'New Issue Text' } }); - assert.isObject(updateSucccess); - assert.deepEqual(updateSucccess, { + assert.isObject(updateSuccess); + assert.deepEqual(updateSuccess, { result: 'successfully updated', _id: itemToUpdate._id }); diff --git a/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/24-game.md b/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/24-game.md index 6b0e5b9e7ed..eb280cacae0 100644 --- a/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/24-game.md +++ b/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/24-game.md @@ -82,7 +82,7 @@ const OPERATORS_ = { "/": (a, b) => a / b, } -const PRECIDENCE_ = { +const PRECEDENCE_ = { "+": 1, "-": 1, "*": 2, @@ -114,7 +114,7 @@ function evaluate_(expression) { case "*": case "/": while (stack.length && - PRECIDENCE_[c] <= PRECIDENCE_[stack[stack.length-1]]) { + PRECEDENCE_[c] <= PRECEDENCE_[stack[stack.length-1]]) { postfix += stack.pop(); } stack.push(c); diff --git a/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md b/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md index 43ff89b7a2f..ac19b1c310c 100644 --- a/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md +++ b/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md @@ -175,7 +175,7 @@ function dealFreeCell(seed) { const rng = FreeCellRNG(seed); const deck = getDeck(); - const deltCards = [[], [], [], [], [], [], []]; + const dealtCards = [[], [], [], [], [], [], []]; let currentColumn = 0; let currentRow = 0; @@ -195,7 +195,7 @@ function dealFreeCell(seed) { card = deck.pop(); // Deal this card - deltCards[currentRow].push(card); + dealtCards[currentRow].push(card); currentColumn += 1; if (currentColumn === 8) { currentColumn = 0; @@ -203,6 +203,6 @@ function dealFreeCell(seed) { } } - return deltCards; + return dealtCards; } ``` diff --git a/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/department-numbers.md b/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/department-numbers.md index 5edd175be5e..aec2da195d2 100644 --- a/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/department-numbers.md +++ b/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/department-numbers.md @@ -101,7 +101,7 @@ function combinations(possibleNumbers, total) { function combinations(possibleNumbers, total) { let firstNumber; let secondNumber; - let thridNumber; + let thirdNumber; const allCombinations = []; for (let i = 0; i < possibleNumbers.length; i += 1) { @@ -112,10 +112,10 @@ function combinations(possibleNumbers, total) { secondNumber = possibleNumbers[j]; if (j !== i && firstNumber + secondNumber <= total) { - thridNumber = total - firstNumber - secondNumber; + thirdNumber = total - firstNumber - secondNumber; - if (thridNumber !== firstNumber && thridNumber !== secondNumber && possibleNumbers.includes(thridNumber)) { - allCombinations.push([firstNumber, secondNumber, thridNumber]); + if (thirdNumber !== firstNumber && thirdNumber !== secondNumber && possibleNumbers.includes(thirdNumber)) { + allCombinations.push([firstNumber, secondNumber, thirdNumber]); } } } diff --git a/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/lu-decomposition.md b/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/lu-decomposition.md index a11dcb9df98..601c2102361 100644 --- a/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/lu-decomposition.md +++ b/curriculum/challenges/german/10-coding-interview-prep/rosetta-code/lu-decomposition.md @@ -80,7 +80,7 @@ $PA = LU$ # --instructions-- -The task is to implement a routine which will take a square nxn matrix $A$ and return a lower triangular matrix $L$, a upper triangular matrix $U$ and a permutation matrix $P$, so that the above equation is fullfilled. The returned value should be in the form `[L, U, P]`. +The task is to implement a routine which will take a square nxn matrix $A$ and return a lower triangular matrix $L$, a upper triangular matrix $U$ and a permutation matrix $P$, so that the above equation is fulfilled. The returned value should be in the form `[L, U, P]`. # --hints-- diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md index 722f7d7d33f..b936314f17f 100644 --- a/curriculum/challenges/german/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md +++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md @@ -9,7 +9,7 @@ dashedName: step-82 Der Schnabel und die Füße des Pinguins haben die gleiche `color`. -Erstelle eine neue benutzerdefinierte CSS-Variable namens `--penguin-picorna` und ersetze alle relevanten Eigenschaftswerte durch diese. +Create a new custom CSS variable named `--penguin-picorna`, and replace all relevant property values with it. # --hints-- diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md index 36c410d71c6..b1c9d67864d 100644 --- a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md @@ -11,7 +11,7 @@ Inside `pick`, use `let` to initialize a variable named `numbers` and set it to # --hints-- -Your `pick` function should initalize `numbers` to an empty array `[]`. +Your `pick` function should initialize `numbers` to an empty array `[]`. ```js assert.match(pick.toString(), /numbers\s*=\s*\[\]/); diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md new file mode 100644 index 00000000000..4b042aa87ba --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md @@ -0,0 +1,310 @@ +--- +id: 64e4e4c4ec263b62ae7bf54d +title: Step 1 +challengeType: 0 +dashedName: step-1 +--- + +# --description-- + +In this project, you will learn how `localStorage` works in JavaScript by building a Todo app. LocalStorage is a web storage feature of JavaScript that lets you persist data by storing the data as a **key:value** pair. + +The HTML and CSS for this project have been provided for you. Take a look at the files to get yourself familiarized with them. + +Begin by accessing the `task-form`, `confirm-close-dialog`, and `open-task-form-btn` elements with the `getElementById()` method. Save them in the variables `taskForm`, `confirmCloseDialog`, and `openTaskFormBtn`. + +# --hints-- + +You should use `getElementById()` to access the `task-form` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should assign the `task-form` element to the variable `taskForm`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+taskForm\s*=\s*document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should use `getElementById()` to access the `confirm-close-dialog` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should assign the `confirm-close-dialog` element to the variable `confirmCloseDialog`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+confirmCloseDialog\s*=\s*document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should use `getElementById()` to access the `open-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +You should assign the `open-task-form-btn` element to the variable `openTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+openTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md new file mode 100644 index 00000000000..32310409728 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md @@ -0,0 +1,310 @@ +--- +id: 64e4e6c86954de67a3e44ee3 +title: Step 2 +challengeType: 0 +dashedName: step-2 +--- + +# --description-- + +You need to access more elements with the `getElementById()` method. This time you need the `close-task-form-btn`, `add-or-update-task-btn`, and `cancel-btn` elements. Save them in the variables `closeTaskFormBtn`, `addOrUpdateTaskBtn`, and `cancelBtn`. + +# --hints-- + +You should use `getElementById()` to access the `close-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should assign the `close-task-form-btn` element to the variable `closeTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+closeTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `add-or-update-task-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should assign the `add-or-update-task-btn` element to the variable `addOrUpdateTaskBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+addOrUpdateTaskBtn\s*=\s*document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `cancel-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +You should assign the `cancel-btn` element to the variable `cancelBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+cancelBtn\s*=\s*document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md new file mode 100644 index 00000000000..17fc6b76517 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md @@ -0,0 +1,313 @@ +--- +id: 64e4e6fe78b5aa67ef2fc3e7 +title: Step 3 +challengeType: 0 +dashedName: step-3 +--- + +# --description-- + +Next, access the `discard-btn`, `tasks-container`, and `title-input` elements using the `getElementById()` method. Save them in variables named `discardBtn`, `tasksContainer`, and `titleInput`, respectively. + +# --hints-- + +You should use `getElementById()` to access the `discard-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should assign the `discard-btn` element to the variable `discardBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+discardBtn\s*=\s*document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `tasks-container` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should assign the `tasks-container` element to the variable `tasksContainer`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+tasksContainer\s*=\s*document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should use `getElementById()` to access the `title-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +You should assign the `title-input` element to the variable `titleInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+titleInput\s*=\s*document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md new file mode 100644 index 00000000000..6e543d36acc --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md @@ -0,0 +1,304 @@ +--- +id: 64e4e7241f52bb682eeb8211 +title: Step 4 +challengeType: 0 +dashedName: step-4 +--- + +# --description-- + +The last set of elements you need to get from the HTML file are the `date-input` and `description-input` elements. Save them in the variables `dateInput` and `descriptionInput` respectively. + +# --hints-- + +You should use `getElementById()` to access the `date-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should assign the `date-input` element to the variable `dateInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+dateInput\s*=\s*document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should use `getElementById()` to access the `description-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +You should assign the `description-input` element to the variable `descriptionInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+descriptionInput\s*=\s*document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md new file mode 100644 index 00000000000..6f0cd6e9fc4 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md @@ -0,0 +1,296 @@ +--- +id: 64e4e74d0fb4f0687bf4145d +title: Step 5 +challengeType: 0 +dashedName: step-5 +--- + +# --description-- + +Create a `taskData` constant and set it to an empty array. This array will store all the tasks along with their associated data, including title, due date, and description. This storage will enable you to keep track of tasks, display them on the page, and save them to `localStorage`. + +Use `let` to create a `currentTask` variable and set it to an empty object. This variable will be used to track the state when editing and discarding tasks. + +# --hints-- + +You should create a `taskData` constant set to an empty array. + +```js +assert.match(code, /const\s+taskData\s*=\s*\[\];?/) +``` + +You should use `let` to create an empty `currentTask` object. + +```js +assert.match(code, /let\s+currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md new file mode 100644 index 00000000000..7eba2896bf4 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md @@ -0,0 +1,307 @@ +--- +id: 64e4e78a7ea4a168de4e6a38 +title: Step 6 +challengeType: 0 +dashedName: step-6 +--- + +# --description-- + +Now, you will work on opening and closing the form modal. + +Add a `click` event listener to the `openTaskFormBtn` variable, and then use `classList` to toggle the `hidden` class on the `taskForm` element. + +Now you can click on the "Add new Task" button and see the form modal. + +# --hints-- + +You should call the `addEventListener()` method on your `openTaskFormBtn` variable. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax, then use `classList` property and it's `toggle` method on the `taskForm`, to toggle the class `hidden`. Don't use curly braces. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*taskForm\.classList\.toggle\(\1hidden\1\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md new file mode 100644 index 00000000000..6a29158134e --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md @@ -0,0 +1,309 @@ +--- +id: 64e4e7bbedb22d6939001ad3 +title: Step 7 +challengeType: 0 +dashedName: step-7 +--- + +# --description-- + +Add a `click` event listener to the `closeTaskFormBtn` variable, then use the `showModal()` method on your `confirmCloseDialog` variable. This will display a modal with the `Discard` and `Cancel` buttons. + +`showModal()` is a method associated with the HTML `dialog` element. It is used to display a modal dialog box on a web page. + +# --hints-- + +You should call the `addEventListener()` method on your `closeTaskFormBtn` variable. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `showModal()` method on `confirmCloseDialog`. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(["'`]click["'`],\s*\(.*\)\s*=>\s*{?\s*confirmCloseDialog\.showModal\(\);?\s*}?\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md new file mode 100644 index 00000000000..311984043a0 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md @@ -0,0 +1,316 @@ +--- +id: 64e4eaaa9070a66aecbfe603 +title: Step 8 +challengeType: 0 +dashedName: step-8 +--- + +# --description-- + +If the user clicks the `Cancel` button, you want to cancel the process (close the modal showing the two buttons) so the user can continue editing. + +Add a `click` event listener to the `cancelBtn` element, then use the `close()` method on the `confirmCloseDialog` variable. + +`close()` is a method of the window object you can use to close the current window, or a modal you create with the `dialog` element. + +# --hints-- + +You should call the `addEventListener()` method on your `cancelBtn` variable. + +```js +assert.match(code, /cancelBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `close()` method on `confirmCloseDialog`. Don't use curly braces. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*confirmCloseDialog\.close\(\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- + +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md new file mode 100644 index 00000000000..66a40c9771b --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md @@ -0,0 +1,327 @@ +--- +id: 64e4ebc7eabc5a6babd479cd +title: Step 9 +challengeType: 0 +dashedName: step-9 +--- + +# --description-- + +If the user clicks the `Discard` button, you want to close the modal showing the `Cancel` and `Discard` buttons, then hide the form modal. + +Add a click event listener to `discardBtn`, then use the `close()` method on the `confirmCloseDialog` variable. Also, use `classList` to toggle the class `hidden` on `taskForm` so the form modal will close too. + +# --hints-- + +You should call the `addEventListener()` method on your `discardBtn` variable. + +```js +assert.match(code, /discardBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{/) +``` + +Your event listener should use the `close()` method on `confirmCloseDialog`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?/) +``` + +Your event listener should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?\s*taskForm\.classList\.toggle\(\1hidden\1\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md new file mode 100644 index 00000000000..f4002dc0937 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md @@ -0,0 +1,326 @@ +--- +id: 64e4ecd7735a566c9266a338 +title: Step 10 +challengeType: 0 +dashedName: step-10 +--- + +# --description-- + +Now that you've worked on opening and closing the modal, it's time to get the values from the input fields, save them into the `taskData` array, and display them on the page. + +To start, add a `submit` event listener to your `taskForm` element and pass in `e` as the parameter of your arrow function. Inside the curly braces, use the `preventDefault()` method to stop the browser from refreshing the page after submitting the form. + +# --hints-- + +You should call the `addEventListener()` method on your `taskForm` variable. + +```js +assert.match(code, /taskForm\.addEventListener\(/) +``` + +Your event listener should listen for a `submit` event. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces with `e` as the parameter. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{/) +``` + +You should use the `e.preventDefault()` method to stop the browser from reloading the page. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{\s*e\.preventDefault\(\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md new file mode 100644 index 00000000000..148e52f3e93 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md @@ -0,0 +1,337 @@ +--- +id: 64e4eec13546c06d61a63d59 +title: Step 11 +challengeType: 0 +dashedName: step-11 +--- + +# --description-- + +You need to determine whether the task being added already exists or not. If it doesn't exist, you will add it, and if it already exists, you will set it up for editing. You can use the `findIndex()` method to accomplish this. + +`findIndex` is an array method that lets find the index of the first element in an array that satisfies a given testing function. + +Here's an example: + +```js +const numbers = [3, 1, 5, 6, 10, 9, 8]; +const firstEvenNumIndex = numbers.findIndex((num) => num % 2 === 0); + +console.log(firstEvenNumIndex); // Output: 3 – because the first even number (6) is at index 3 +``` + +Declare a `dataArrIndex` variable using `const` and set it to the result of the `findIndex()` method applied to the `taskData` array. Utilize arrow syntax to provide a callback function with `item` as the parameter, and within the callback, check if the `id` property of `item` is equal to the `id` property of `currentTask`. + +If the task exists, this returns the index, and if it doesn't exist, it returns `-1`. + +# --hints-- + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === currentTask.id`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?\s*=>\s*item.id\s*===\s*currentTask.id\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md new file mode 100644 index 00000000000..10826de6f17 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md @@ -0,0 +1,340 @@ +--- +id: 64e4f01d6c72086e016a8626 +title: Step 12 +challengeType: 0 +dashedName: step-12 +--- + +# --description-- + +Next, retrieve the values from the input fields and store them in a `taskObj` object. Each task should also have a unique `id`. + +Create a `taskObj` object with an `id` property as the first property. For the value of the `id` property, retrieve the value of the `titleInput` field, convert it to lowercase, and then use the `split()` and `join()` methods to hyphenate it. + +Make sure all of those are in template literals because you need the `id` property value as a string. + +# --hints-- + +You should use `const` to create a `taskObj` object. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*/) +``` + +Your `taskObj` object should have a key of `id`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id/) +``` + +You should use template strings to get `titleInput.value` as the value of your `id` key and convert it to lowercase. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)/) +``` + +You should use `.split(' ')` on `titleInput.value.toLowerCase()`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)/) +``` + +You should use `.join('-')` on `titleInput.value.toLowerCase().split(' ')`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md new file mode 100644 index 00000000000..764de1b0918 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md @@ -0,0 +1,327 @@ +--- +id: 64ec89ee549ecf802de2b3e2 +title: Step 14 +challengeType: 0 +dashedName: step-14 +--- + +# --description-- + +Retrieve the values from the `titleInput`, `dateInput`, and `descriptionInput` fields, and then save them in the properties `title`, `date`, and `description` of the `taskObj` object. + +# --hints-- + +You should get the value of `titleInput` and save it in a `title` key. + +```js +assert.match(code, /title:\s*titleInput\.value,/) +``` + +You should get the value of `dateInput` and save it in a `date` key. + +```js +assert.match(code, /date:\s*dateInput\.value,/) +``` + +You should get the value of `descriptionInput` and save it in a `description` key. + +```js +assert.match(code, /description:\s*descriptionInput\.value,?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + const taskObj = { + id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`, + --fcc-editable-region-- + + --fcc-editable-region-- + }; +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md new file mode 100644 index 00000000000..3d009188d81 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md @@ -0,0 +1,332 @@ +--- +id: 64ec8f717b261e824d82d6a5 +title: Step 15 +challengeType: 0 +dashedName: step-15 +--- + +# --description-- + +Now that you have obtained the values from the input fields and generated an `id`, you want to add them to your `taskData` array to keep track of each task. However, you should only do this if the task is new. If the task already exists, you will set it up for editing. This is why you have the `dataArrIndex` variable, which provides the index of each task. + +Create an `if` statement with the condition `dataArrIndex === -1`. Within the `if` statement, use the `unshift()` method to add the `taskObj` object to the beginning of the `taskData` array. + +`unshift()` is an array method that is used to add one or more elements to the beginning of an array. + +**N.B:** Try adding a task and log `taskData` to the console to see what it looks like. + +# --hints-- + +You should create an `if` statement with the condition `dataArrIndex === -1`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{/) +``` + +Your `if` statement should have `taskData.unshift(taskObj)` in it's body. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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, + }; + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md new file mode 100644 index 00000000000..f1202bb93fe --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md @@ -0,0 +1,331 @@ +--- +id: 64ec9145e424d8835a4e0f28 +title: Step 16 +challengeType: 0 +dashedName: step-16 +--- + +# --description-- + +Now that you have saved the task in the `taskData` array, you should display the task on the page by looping through it. + +Use `forEach()` on `taskData`, then destructure `id`, `title`, `date`, `description` as the parameters. Don't return anything yet. + +# --hints-- + +You should use `forEach()` on `taskData`. + +```js +assert.match(code, /taskData\.forEach\(\s*/) +``` + +You should destructure `id`, `title`, `date`, `description` as the parameters of the callback of your `forEach()`. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md new file mode 100644 index 00000000000..06bdd7c6ae5 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md @@ -0,0 +1,336 @@ +--- +id: 64ec9282cd547785258cecf2 +title: Step 17 +challengeType: 0 +dashedName: step-17 +--- + +# --description-- + +Using arrow syntax, create an implicit return and use addition assignment to set the `innerHTML` of `tasksContainer` to empty backticks. + +# --hints-- + +You should not alter the existing `taskData.forEach()` and the values you destructured. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +You should use arrow syntax and attach `innerHTML` to `tasksContainer`. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\(\s*tasksContainer\.innerHTML\s*\))?/) +``` + +You should use addition assignment to set the `innerHTML` of `tasksContainer` to an empty pair of backticks. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\s*\(?tasksContainer\.innerHTML\s*\+=\s*`\s*`\)?)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + taskData.forEach(({id, title, date, description})); + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md new file mode 100644 index 00000000000..b5115f99a51 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md @@ -0,0 +1,333 @@ +--- +id: 64ec9343769e8f85c1e17e05 +title: Step 18 +challengeType: 0 +dashedName: step-18 +--- + +# --description-- + +Create a `div` element with the class of `task`. Utilize template strings to set the `id` attribute of the `div` to the `id` you destructured from the task data. + +# --hints-- + +You should create a `div` element with the class `task`. + +```js +assert.match(code, /\s*<\/div>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` + --fcc-editable-region-- + + --fcc-editable-region-- + `) + ); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md new file mode 100644 index 00000000000..81dd3530420 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md @@ -0,0 +1,348 @@ +--- +id: 64ec94f0de20c086e09b0fc3 +title: Step 19 +challengeType: 0 +dashedName: step-19 +--- + +# --description-- + +Create a `p` element and use template strings to set its content to the `title` you destructured. Right before the content of the `p` element, create a `strong` element with the text `Title:`. + +# --hints-- + +You should create a `p` element. + +```js +assert.match(code, /

/) +``` + +You should interpolate `${title}` as the text of your `p` element. + +```js +assert.match(code, /

.*\$\{title\}<\/p>/) +``` + +You should create a `strong` element after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)/) +``` + +Your `strong` element should have the text `Title:`. + +```js +assert.match(code, /(?<=

)Title:\s*<\/strong>\s*/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md new file mode 100644 index 00000000000..4820d100d4d --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md @@ -0,0 +1,336 @@ +--- +id: 64ec959a76336c8767f5cd4d +title: Step 20 +challengeType: 0 +dashedName: step-20 +--- + +# --description-- + +Similarly to the previous step, create another `p` element, and interpolate the `date` you destructured as the text content. Inside this paragraph, create a `strong` element with the text `Date:`. + +# --hints-- + +You should create a `p` element and interpolate `${date}` as the text. + +```js +assert.match(code, /

.*\$\{date\}<\/p>/) +``` + +You should create a `strong` element with the text `Date:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Date:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md new file mode 100644 index 00000000000..4525b0e154c --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md @@ -0,0 +1,340 @@ +--- +id: 64ec96761156a187ed32b274 +title: Step 22 +challengeType: 0 +dashedName: step-22 +--- + +# --description-- + +To allow for task management, you need to include both a delete and an edit button for each task. + +Create two `button` elements with the `type` attribute set to `button` and the `class` attribute set to `btn`. Set the text of the first button to `Edit` and the text of the second button to `Delete`. + +# --hints-- + +You should create a `button` element of type `button`, a class `btn` and `Edit` as the text, in that order. + +```js +assert.match(code, /Edit<\/button/) +``` + +You should create a `button` element of type `button` a class `btn` and `Delete` as the text, in that order. + +```js +assert.match(code, /Delete<\/button/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md new file mode 100644 index 00000000000..0081fa88e1a --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md @@ -0,0 +1,336 @@ +--- +id: 64ec9b10356c2d8aa05d9ce1 +title: Step 23 +challengeType: 0 +dashedName: step-23 +--- + +# --description-- + +After adding the task to the page, you should close the form modal to view the task. To do this, utilize `classList` to toggle the `hidden` class on the `taskForm` element. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +const splitter = code.split("taskData.forEach") +assert.match(splitter[1], /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md new file mode 100644 index 00000000000..6ae1e4dff48 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md @@ -0,0 +1,347 @@ +--- +id: 64ec9c55fdeef78bacd2fc3b +title: Step 24 +challengeType: 0 +dashedName: step-24 +--- + +# --description-- + +If you attempt to add another task now, you'll notice that the input fields retain the values you entered for the previous task. To resolve this, you need to clear the input fields after adding a task. + +Instead of clearing the input fields one by one, it's a good practice to create a function that handles clearing those fields. You can then call this function whenever you need to clear the input fields again. + +Use arrow syntax to create a `reset` function and set it to a pair of curly braces. + +# --hints-- + +You should use `const` and arrow syntax to create a `reset` function. + +```js +assert.match(code, /const\s+reset\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +Your `reset` function should be empty. + +```js +assert.match(reset.toString(), /\(\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md new file mode 100644 index 00000000000..e12fb12ab81 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md @@ -0,0 +1,366 @@ +--- +id: 64fac365aeb8ad70b69b366f +title: Step 25 +challengeType: 0 +dashedName: step-25 +--- + +# --description-- + +Inside the `reset` function, set each value of `titleInput`, `dateInput`, `descriptionInput` to an empty string. + +Also, use `classList` to toggle the class `hidden` on the `taskForm` and set `currentTask` to an empty object. That's because at this point, `currentTask` will be filled with the task the user might have added. + +# --hints-- + +You should set `titleInput.value` to an empty string. + +```js +assert.match(reset.toString(), /titleInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `dateInput.value` to an empty string. + +```js +assert.match(reset.toString(), /dateInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `descriptionInput.value` to an empty string. + +```js +assert.match(reset.toString(), /descriptionInput\.value\s*=\s*('|")\1;?/) +``` + +You should use `classList` to toggle the class `hidden` on `taskForm` + +```js +assert.match(reset.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +You should set `currentTask` to an empty object. + +```js +assert.match(reset.toString(), /currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- +const reset = () => { + +} +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md new file mode 100644 index 00000000000..5f281d0de16 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md @@ -0,0 +1,351 @@ +--- +id: 64fac4d1773e7a719b1254de +title: Step 26 +challengeType: 0 +dashedName: step-26 +--- + +# --description-- + +Remove the existing code toggling the class of `hidden` on `taskForm` and call the `reset` function instead. This would clear the input fields and also hide the form modal for the user to see the added task. + +# --hints-- + +Yous should remove the code toggling the `hidden` class on `taskForm`. + +```js +const splitter = code.split('') +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /reset\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + +--fcc-editable-region-- + taskForm.classList.toggle("hidden"); +--fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md new file mode 100644 index 00000000000..c1a7b0f8965 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md @@ -0,0 +1,351 @@ +--- +id: 64fac6a497811572b338e5e5 +title: Step 27 +challengeType: 0 +dashedName: step-27 +--- + +# --description-- + +Also, remove the existing code toggling the class `hidden` on `taskForm` inside the `discardBtn` event listener and call the `reset` function instead. That's because when you click the `Discard` button, everything in the input fields should go away. + +# --hints-- + +Yous should remove the code toggling the class `hidden` on `taskForm`. + +```js +const splitter = code.split("confirmCloseDialog.close();") +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /confirmCloseDialog\.close\(\);?\s*reset\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); +--fcc-editable-region-- + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md new file mode 100644 index 00000000000..84005e1b01a --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md @@ -0,0 +1,346 @@ +--- +id: 64faca774fd9fd74bc084cc9 +title: Step 28 +challengeType: 0 +dashedName: step-28 +--- + +# --description-- + +You should display the `Cancel` and `Discard` buttons to the user only if there is some text present in the input fields. + +To begin, within the `closeTaskFormBtn` event listener, create a `formInputsContainValues` variable to check if there is a value in the `titleInput` field **or** the `dateInput` field **or** the `descriptionInput` field. + +# --hints-- + +You should use `const` to create a variable `formInputsContainValues` with the value `titleInput.value || dateInput.value || descriptionInput.value;` + +```js +assert.match(code, /const\s*formInputsContainValues\s*=\s*titleInput\.value\s*\|\|\s*dateInput\.value\s*\|\|\s*descriptionInput\.value;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); + +}); +--fcc-editable-region-- + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md new file mode 100644 index 00000000000..07482464ebf --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md @@ -0,0 +1,362 @@ +--- +id: 64facf6180824876f70a2e86 +title: Step 29 +challengeType: 0 +dashedName: step-29 +--- + +# --description-- + +Create an `if` statement to check if `formInputsContainValues` is true. If `formInputsContainValues` is true, indicating that there are changes, use the `showModal()` method on `confirmCloseDialog`. Otherwise, if there are no changes, call the `reset()` function to clear the input fields and hide the form modal. + +# --hints-- + +You should create an `if` statement with the condition `formInputsContainValues`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{/) +``` + +The `if` block of your `if` statement should contain `confirmCloseDialog.showModal();`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?/) +``` + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{/) +``` + +You should call the `reset()` function in the `else` block of your `if` statement. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{\s*reset\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 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; +--fcc-editable-region-- + confirmCloseDialog.showModal(); +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md new file mode 100644 index 00000000000..95b71fba69f --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md @@ -0,0 +1,372 @@ +--- +id: 64fad07f43a101779cb8692a +title: Step 30 +challengeType: 0 +dashedName: step-30 +--- + +# --description-- + +You can enhance code readability and maintainability by refactoring the `submit` event listener into two separate functions. The first function can be used to add the input values to `taskData`, while the second function can be responsible for adding the tasks to the DOM. + +Use arrow syntax to create an `addOrUpdateTask` function. Then move the `dataArrIndex` variable, the `taskObj` object, and the `if` statement into the `addOrUpdateTask` function. + +# --hints-- + +You should use `const` and arrow syntax to create an `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +You should move the `dataArrIndex` variable into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?/) +``` + +You should move the `taskObj` object into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?/) +``` + +You should move the `if` statement with the condition `dataArrIndex === 1` into your `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?\s*if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md new file mode 100644 index 00000000000..8192699bb61 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md @@ -0,0 +1,358 @@ +--- +id: 64fad9cd2eeb1e7ca2ca8c8b +title: Step 31 +challengeType: 0 +dashedName: step-31 +--- + +# --description-- + +Use arrow syntax to create an `updateTaskContainer` function. Then move the `taskData.forEach()` and its content into it. + +# --hints-- + +You should use `const` and arrow syntax to create a `updateTaskContainer` function. + +```js +assert.match(code, /const\s*updateTaskContainer\s*=\s*\(\)\s*=>\s*\{/) +``` + +You should move `taskData.forEach()` and its content into the `updateTaskContainer()` function. + +```js +assert.match(code, /const\s+updateTaskContainer\s+=\s*\(\)\s*=>\s*\{\s*taskData\.forEach\(\s*\(\{\s*id,\s*title,\s*date,\s*description\s*\}\)\s*=>\s*\(tasksContainer\.innerHTML\s*\+=\s*`\s*\s*

Title:<\/strong>\s*\$\{title\}<\/p>\s*

Date:<\/strong>\s*\$\{date\}<\/p>\s*

Description:<\/strong>\s*\$\{description\}<\/p>\s*Edit<\/button>\s*Delete<\/button>\s*<\/div>\s*`\)\s*\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md new file mode 100644 index 00000000000..18b2a462de4 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md @@ -0,0 +1,360 @@ +--- +id: 64fadae4f2d51b7d5d8b98d8 +title: Step 32 +challengeType: 0 +dashedName: step-32 +--- + +# --description-- + +Inside the `addOrUpdateTask` function, call the `updateTaskContainer` and `reset` functions. + +# --hints-- + +You should call the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\)\s*/) +``` + +You should call the `reset` function after calling the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\);?\s*reset\(\);?\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + reset() +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md new file mode 100644 index 00000000000..657bb4aa05b --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md @@ -0,0 +1,355 @@ +--- +id: 64fadff23375f27ff06c6d40 +title: Step 33 +challengeType: 0 +dashedName: step-33 +--- + +# --description-- + +Now remove the `reset()` call inside the `taskForm` submit event listener and call the `addOrUpdateTask` function instead. + +# --hints-- + +You should remove `reset()` and call `addOrUpdateTask()`. + +```js +assert.match(code, /addOrUpdateTask\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + --fcc-editable-region-- + reset() + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md new file mode 100644 index 00000000000..bd759fb7e5f --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md @@ -0,0 +1,364 @@ +--- +id: 64fae068bcdc9c805bd8399e +title: Step 35 +challengeType: 0 +dashedName: step-35 +--- + +# --description-- + +To enable editing and deleting for each task, add an `onclick` attribute to both buttons. Set the value of the `onclick` attribute to `editTask(this)` for the `Edit` button and `deleteTask(this)` for the `Delete` button. The `editTask(this)` function will handle editing, while the `deleteTask(this)` function will handle deletion. + +`this` is a keyword that refers to the current context. In this case, `this` points to the element that triggers the event – the buttons. + +# --hints-- + +You should add `onclick="editTask(this)"` as the first attribute of the edit button. + +```js +assert.match(code, /Edit<\/button>/) +``` + +You should add `onclick="deleteTask(this)"` as the first attribute of the delete button. + +```js +assert.match(code, /Delete<\/button>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + + --fcc-editable-region-- +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md new file mode 100644 index 00000000000..c6fac350818 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md @@ -0,0 +1,371 @@ +--- +id: 64faedcd16a1e985c4c2dc94 +title: Step 36 +challengeType: 0 +dashedName: step-36 +--- + +# --description-- + +Create a `deleteTask` function using arrow syntax. Pass `buttonEl` as the parameter and define an empty set of curly braces for the function body. + +# --hints-- + +You should use `const` and arrow syntax to create a `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `deleteTask` function should take a `buttonEl` parameter. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `deleteTask` function should be empty. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md new file mode 100644 index 00000000000..27631c77f2f --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md @@ -0,0 +1,359 @@ +--- +id: 64faf0418e828c0114a558a7 +title: Step 34 +challengeType: 0 +dashedName: step-34 +--- + +# --description-- + +There's a problem. If you add a task, and then add another, the previous task gets duplicated. This means you need to clear out the existing contents of `tasksContainer` before adding a new task. + +Set the `innerHTML` of `taskContainer` back to an empty string. + + +# --hints-- + +You should set the `innerHTML` of `tasksContainer` to an empty string. + +```js +assert.match(code, /tasksContainer\.innerHTML\s*=\s*("|')\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + --fcc-editable-region-- + + --fcc-editable-region-- + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md new file mode 100644 index 00000000000..9cdbd7860ca --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md @@ -0,0 +1,382 @@ +--- +id: 64faf65b22ad8d07df9be14d +title: Step 37 +challengeType: 0 +dashedName: step-37 +--- + +# --description-- + +You need to find the index of the task you want to delete first. + +Create a `dataArrIndex` variable and set its value using the `findIndex()` method on the `taskData` array. Pass `item` as the parameter for the arrow callback function, and within the callback, check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. Don't use curly braces. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?\s*item\s*\)?\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\s*\);?\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md new file mode 100644 index 00000000000..0e54adadf06 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md @@ -0,0 +1,398 @@ +--- +id: 64faf874364ec308f875f636 +title: Step 38 +challengeType: 0 +dashedName: step-38 +--- + +# --description-- + +You need to remove the task from the DOM using `remove()` and from the `taskData` array using `splice()`. + +`splice()` is an array method that modifies arrays by removing, replacing, or adding elements at a specified index, while also returning the removed elements. It can take up to three parameters: the first one is the mandatory index at which to start, the second is the number of items to remove, and the third is an optional replacement element. Here's an example: + +```js +const fruits = ["mango", "date", "cherry", "banana", "apple"]; + +// Remove date and cherry from the array starting at index 1 +const removedFruits = fruits.splice(1, 2); + +console.log(fruits); // [ 'mango', 'banana', 'apple' ] +console.log(removedFruits); // [ 'date', 'cherry' ] +``` + +Use the `remove()` method to remove the `parentElement` of the `buttonEl` from the DOM. Then use `splice()` to remove the task from the `taskData` array. Pass in `dataArrIndex` and `1` as the parameters of your `splice()`. + +`dataArrIndex` is the index to start and `1` is the number of items to remove. + +# --hints-- + +You should use the `remove()` method to remove the parent element of `buttonEl`. + +```js +assert.match(deleteTask.toString(), /buttonEl\.parentElement\.remove\(\);?/) +``` + +You should use `splice()` on the `taskData` array. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(/) +``` + +The first parameter of your `splice()` method should be `dataArrIndex`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex/) +``` + +The second parameter of your `splice()` method should be `1`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex,\s*1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md new file mode 100644 index 00000000000..3f379d51764 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md @@ -0,0 +1,381 @@ +--- +id: 64fafac95328110a69bcb75f +title: Step 39 +challengeType: 0 +dashedName: step-39 +--- + +# --description-- + +Use arrow syntax to create an `editTask` function. Pass in `buttonEl` as the parameter and add empty curly braces for the body. + +# --hints-- + +You should use `const` and arrow syntax to create an `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `editTask` function should take a `buttonEl` parameter. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `editTask` function should be empty. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md new file mode 100644 index 00000000000..bc0e2a84503 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md @@ -0,0 +1,391 @@ +--- +id: 64fb0fa0968f2b113b2d90e9 +title: Step 40 +challengeType: 0 +dashedName: step-40 +--- + +# --description-- + +As you did in the `deleteTask` function, you need to find the index of the task to be edited. + +Create a `dataArrIndex` variable. For its value, utilize the `findIndex()` method on `taskData`. Pass `item` as the parameter to its callback function and check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md new file mode 100644 index 00000000000..14d527ecf40 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md @@ -0,0 +1,373 @@ +--- +id: 64fb1061ca838611ed6a7d6b +title: Step 41 +challengeType: 0 +dashedName: step-41 +--- + +# --description-- + +Use square bracket notation to retrieve the task to be edited from the `taskData` array using the `dataArrIndex`. Then, assign it to the `currentTask` object to keep track of it. + +# --hints-- + +You should assign `taskData[dataArrIndex]` to `currentTask`. + +```js +assert.match(code, /currentTask\s*=\s*taskData\[dataArrIndex\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex((item) => item.id === buttonEl.parentElement.id); + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md new file mode 100644 index 00000000000..73ef19befcc --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md @@ -0,0 +1,389 @@ +--- +id: 64fb1321e189a6136d200f77 +title: Step 42 +challengeType: 0 +dashedName: step-42 +--- + +# --description-- + +The task to be edited is now in the `currentTask` object. Stage it for editing inside the input fields by setting the value of `titleInput` to `currentTask.title`, `dateInput` to `currentTask.date`, and `descriptionInput` to `currentTask.description`. + +# --hints-- + +You should set `titleInput.value` to `currentTask.title`. + +```js +assert.match(editTask.toString(), /titleInput\.value\s*=\s*currentTask\.title;?/) +``` + +You should set `dateInput.value` to `currentTask.date`. + +```js +assert.match(editTask.toString(), /dateInput\.value\s*=\s*currentTask\.date;?/) +``` + +You should set `descriptionInput.value` to `currentTask.description`. + +```js +assert.match(editTask.toString(), /descriptionInput\.value\s*=\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + currentTask = taskData[dataArrIndex]; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md new file mode 100644 index 00000000000..c6312bf6bd0 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md @@ -0,0 +1,386 @@ +--- +id: 64fb1436adef3e145b4c3501 +title: Step 43 +challengeType: 0 +dashedName: step-43 +--- + +# --description-- + +Set the `innerText` of the `addOrUpdateTaskBtn` button to `Update Task` and its `aria-label` to `Update Task` as well. + +# --hints-- + +You should set the inner text of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.innerText\s*=\s*("|')Update Task\1;?/) +``` + +You should set the `ariaLabel` of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.ariaLabel\s*=\s*("|')Update Task\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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; + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md new file mode 100644 index 00000000000..101870d098a --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md @@ -0,0 +1,384 @@ +--- +id: 64fb14d890415c14f93069ce +title: Step 44 +challengeType: 0 +dashedName: step-44 +--- + +# --description-- + +Finally, display the `form` modal with the values of the input fields by using `classList` to toggle the `hidden` class on `taskForm`. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(editTask.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md new file mode 100644 index 00000000000..7ad62060776 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md @@ -0,0 +1,391 @@ +--- +id: 64fb154a7c48cd159924bb18 +title: Step 45 +challengeType: 0 +dashedName: step-45 +--- + +# --description-- + +At this point, editing a task won't reflect when you submit the task. To make the editing functional, go back to the `if` statement inside the `addOrUpdateTask` function. Create an `else` block and set `taskData[dataArrIndex]` to `taskObj`. + +# --hints-- + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*/) +``` + +Your `else` block should have the code `taskData[dataArrIndex] = taskObj`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*taskData\[dataArrIndex\]\s*=\s*taskObj;?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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, + }; + + --fcc-editable-region-- + if (dataArrIndex === -1) { + taskData.unshift(taskObj); + } + + --fcc-editable-region-- + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md new file mode 100644 index 00000000000..4ec33172878 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md @@ -0,0 +1,401 @@ +--- +id: 64fb1c4dc0feb219149a7c7d +title: Step 46 +challengeType: 0 +dashedName: step-46 +--- + +# --description-- + +If the user attempts to edit a task but decides not to make any changes before closing the form, there is no need to display the modal with the `Cancel` and `Discard` buttons. + +Inside the `closeTaskFormBtn` event listener, use `const` to create another variable named `formInputValuesUpdated`. Check if the user made changes while trying to edit a task by verifying that the `titleInput` value **is not equal to** `currentTask.title`, the `dateInput` value **is not equal to** `currentTask.date`, and the `descriptionInput` value **is not equal to** `currentTask.description`. + +# --hints-- + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*/) +``` + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title` or `dateInput.value` is not equal to `currentTask.date`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date/) +``` + +Your `formInputValuesUpdated` variable should have the value `titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date\s*\|\|\s*descriptionInput\.value\s*!==\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + --fcc-editable-region-- + + --fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md new file mode 100644 index 00000000000..bb8a87acf60 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md @@ -0,0 +1,390 @@ +--- +id: 64fb285637fa1e1c222033e3 +title: Step 47 +challengeType: 0 +dashedName: step-47 +--- + +# --description-- + +Now add `formInputValuesUpdated` as the second mandatory condition in the `if` statement using the `AND` operator. + +This way, the `Cancel` and `Discard` buttons in the modal won't be displayed to the user if they haven't made any changes to the input fields while attempting to edit a task. + +# --hints-- + +Your `if` should have the condition `formInputsContainValues && formInputValuesUpdated`. + +```js +assert.match(code.split("if (formInputsContainValues"), /\s*&&\s*formInputValuesUpdated/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description; + +--fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md new file mode 100644 index 00000000000..94fdc40185a --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md @@ -0,0 +1,414 @@ +--- +id: 64fb29348a60361ccd45c1e2 +title: Step 48 +challengeType: 0 +dashedName: step-48 +--- + +# --description-- + +`localStorage` offers methods for saving, retrieving, and deleting items. The items you save can be of any JavaScript data type. + +For instance, the `setItem()` method is used to save an item, and the `getItem()` method retrieves the item. To delete a specific item, you can utilize the `removeItem()` method, or if you want to delete all items in the storage, you can use `clear()`. + +Here's how you can save an item: + +```js +localStorage.setItem("key", value); // value could be string, number, or any other data type +``` + +A `myTaskArr` array has been provided for you. Use the `setItem()` method to save it with a key of `data`. + +After that, open your browser console and go to the `Applications` tab, select `Local Storage`, and the freeCodeCamp domain you see. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +Your `localStorage.setItem()` should have a key of `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*myTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md new file mode 100644 index 00000000000..2ce0b111937 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md @@ -0,0 +1,404 @@ +--- +id: 64fefebad99209211ec30537 +title: Step 49 +challengeType: 0 +dashedName: step-49 +--- + +# --description-- + +If you check the `Application` tab of your browser console, you'll notice a series of `[object Object]`. This is because everything you save in `localStorage` needs to be in string format. + +To resolve the issue, wrap the data you're saving in the `JSON.stringify()` method. Then, check local storage again to observe the results. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +You should wrap `JSON.stringify()` around `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*JSON\.stringify\(myTaskArr\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- +localStorage.setItem("data", myTaskArr); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md new file mode 100644 index 00000000000..1cb0c0c5930 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md @@ -0,0 +1,406 @@ +--- +id: 64ff0313700dad264d19dfe4 +title: Step 50 +challengeType: 0 +dashedName: step-50 +--- + +# --description-- + +Now that you have the `myTaskArr` array saved in `localStorage` correctly, you can retrieve it with `getItem()` by specifying the key you used to save the item. + +Use the `getItem()` method to retrieve the `myTaskArr` array and assign it to the variable `getTaskArr`. Then, log the `getTaskArr` variable to the console to see the result. + +# --hints-- + +You should use `const` to create a `getTaskArr` variable and assign `localStorage.getItem("data")` to it. + +```js +assert.match(code, /const\s*getTaskArr\s*=\s*localStorage\.getItem\(('|")data\1\);?/) +``` + +You should log the `getTaskArr` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md new file mode 100644 index 00000000000..608713897b5 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md @@ -0,0 +1,411 @@ +--- +id: 64ff04cc33779427a6412449 +title: Step 51 +challengeType: 0 +dashedName: step-51 +--- + +# --description-- + +The item you retrieve is a string, as you saved it with `JSON.stringify()`. To view it in its original form before saving, you need to use `JSON.parse()`. + +Use `getItem()` to retrieve the `myTaskArr` array again. This time, wrap it inside `JSON.parse()`, assign it to the variable `getTaskArrObj` and log the `getTaskArrObj` to the console. + +Check the console to see the difference between `getTaskArr` and `getTaskObj`. + +# --hints-- + +You should use `const` to create a `getTaskArrObj` variable and assign it to `JSON.parse(localStorage.getItem('data'));`. + +```js +assert.match(code, /const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\(('|")data\1\)\);?/) +``` + +You should log the `getTaskArrObj` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md new file mode 100644 index 00000000000..09bad9433fa --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md @@ -0,0 +1,406 @@ +--- +id: 64ff068e0426eb288874ed79 +title: Step 52 +challengeType: 0 +dashedName: step-52 +--- + +# --description-- + +You can use `localStorage.removeItem()` to remove a specific item and `localStorage.clear()` to clear all items in the local storage. + +Remove the `data` item from local storage and open the console to observe the result. You should see `null`. + +# --hints-- + +You should use `localStorage.removeItem()` to remove the `data` item from the browser's local storage. + +```js +assert.match(code, /localStorage\.removeItem\(('|")data\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md new file mode 100644 index 00000000000..5dc3bd6e1d1 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md @@ -0,0 +1,413 @@ +--- +id: 64ff23daf176a92de95f24dc +title: Step 53 +challengeType: 0 +dashedName: step-53 +--- + +# --description-- + +Using `localStorage.clear()` won't just delete a single item from local storage but will remove all items. + +Remove `localStorage.removeItem()` and use `localStorage.clear()` instead. You don't need to pass in anything. You should also see `null` in the console. + +# --hints-- + +You should remove `localStorage.removeItem("data")`. + +```js +assert.notMatch(code, /localStorage\.removeItem\(('|")data\1\);/) +``` + +You should remove everything from the browser `local storage` with `localStorage.clear()`. + +```js +assert.match(code, /localStorage\.clear\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- +localStorage.removeItem("data"); + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md new file mode 100644 index 00000000000..0d6c42c989e --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md @@ -0,0 +1,404 @@ +--- +id: 64ff24b80431f62ec6b93f65 +title: Step 54 +challengeType: 0 +dashedName: step-54 +--- + +# --description-- + +Remove the `myTaskArr` array and all of the code for `localStorage` because you don't need them anymore. + +# --hints-- + +You should remove `myTaskArr` and all the code related to `localStorage` that you've just learned. + +```js +assert.notMatch(code, /const\s*myTaskArr\s*=\s*\[\s*\{\s*task:\s"Walk\s*the\s*Dog",\s*date:\s*"22-04-2022"\s*\},\s*\{\s*task:\s"Read\s*some\s*books",\s*date:\s*"02-11-2023"\s*\},\s*\{\s*task:\s"Watch\s*football",\s*date:\s*"10-08-2021"\s*\},\s*\];?\s*localStorage\.setItem\("data", JSON\.stringify\(myTaskArr\)\);\s*localStorage\.clear\(\);?\s*const\s*getTaskArr\s*=\s*localStorage\.getItem\("data"\)\s*console\.log\(getTaskArr\)\s*const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\("data"\)\);?\s*console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +--fcc-editable-region-- +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +localStorage.clear(); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md new file mode 100644 index 00000000000..cbbcd4df6e1 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md @@ -0,0 +1,404 @@ +--- +id: 65003986d17d1e1865b269c0 +title: Step 55 +challengeType: 0 +dashedName: step-55 +--- + +# --description-- + +Now you should save the task items to local storage when the user adds, updates, or removes a task. + +Inside the `addOrUpdateTask` function, use `setItem()` to save the tasks with a key of `data`, then pass the `taskData` array as its parameter. Ensure that you stringify the `taskData`. + +This would persist data once the user adds or updates tasks. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +assert.match(code, /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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; + } + --fcc-editable-region-- + + --fcc-editable-region-- + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md new file mode 100644 index 00000000000..ec3a700096a --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md @@ -0,0 +1,407 @@ +--- +id: 650046832f92c01a35834bca +title: Step 56 +challengeType: 0 +dashedName: step-56 +--- + +# --description-- + +You also want a deleted task to be removed from local storage. For this, you don't need the `removeItem()` or `clear()` methods. Since you already use `splice()` to remove the deleted task from `taskData`, all you need to do now is save `taskData` to local storage again. + +Use `setItem()` to save the `taskData` array again. Pass in `data` as the key and ensure that `taskData` is stringified before saving. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); + --fcc-editable-region-- + + --fcc-editable-region-- +} + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md new file mode 100644 index 00000000000..40cf6384c4e --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md @@ -0,0 +1,392 @@ +--- +id: 650048b0764f9c1b798200e2 +title: Step 57 +challengeType: 0 +dashedName: step-57 +--- + +# --description-- + +If you add, update, or remove a task, it should reflect in the UI. However, that's not happening now because you have yet to retrieve the tasks. To do this, you need to modify your initial `taskData` to be an empty array. + +Set `taskData` to the retrieval of `data` from local storage **or** an empty array. Make sure you parse the data coming with `JSON.parse()` because you saved it as a string. + +# --hints-- + +You should set the `taskData` variable to `JSON.parse(localStorage.getItem("data")) || [];`. + +```js +assert.match(code, /const\s*taskData\s*=\s*JSON.parse\(localStorage\.getItem\(('|")data\1\)\)\s*\|\|\s*\[\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- +const taskData = []; +--fcc-editable-region-- +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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md new file mode 100644 index 00000000000..08ced41b63f --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md @@ -0,0 +1,776 @@ +--- +id: 65004ba581d03d1d5628b41c +title: Step 58 +challengeType: 0 +dashedName: step-58 +--- + +# --description-- + +You've retrieved the task item(s) now, but they still don't reflect in the UI when the page loads. However, they appear when you add a new task. + +To fix this issue, you can check if there's a task inside `taskData`, and then call the `updateTaskContainer()`. + +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 the condition `taskData.length`. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*/) +``` + +You should call the `updateTaskContainer` function in your `if` statement. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*updateTaskContainer\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + taskForm.classList.toggle("hidden"); +} + +const reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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 + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md new file mode 100644 index 00000000000..6b21e6888ce --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md @@ -0,0 +1,314 @@ +--- +id: 650300a25b6f72964ab8aca6 +title: Step 13 +challengeType: 0 +dashedName: step-13 +--- + +# --description-- + +To make the `id` more unique, add another hyphen and use `Date.now()`. + +# --hints-- + +You should attach `-${Date.now()}` to the existing value of the `id` key. Don't forget it needs to be inside the template string. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + const taskObj = { + id: `${titleInput.value.toLowerCase().split(' ').join('-')}`, + }; + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md new file mode 100644 index 00000000000..0c9fbc152b7 --- /dev/null +++ b/curriculum/challenges/german/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md @@ -0,0 +1,337 @@ +--- +id: 65099dbd8f137d58e5c0ff16 +title: Step 21 +challengeType: 0 +dashedName: step-21 +--- + +# --description-- + +Create one more `p` element and interpolate the `description` you destructured as the text. Also, create a `strong` element inside the paragraph with the text `Description:`. + +# --hints-- + +You should create a `p` element with `${description}` as the text. + +```js +assert.match(code, /

.*\$\{description\}<\/p>/) +``` + +You should create a `strong` element with the text `Description:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Description:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/german/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md b/curriculum/challenges/german/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md index 7cce6981cca..b4cada2af12 100644 --- a/curriculum/challenges/german/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md +++ b/curriculum/challenges/german/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md @@ -12,7 +12,7 @@ This video will help you understand the equations of simple and compound interes Here is the Colab notebook to go along with this video. -Here is an additional Colab notebook that shows you one way to put many of these interest and payment forumlas into Python functions. Also you will see an example of using some formulas to output results, notice a trend, and follow up with other formulas to analyze patterns. +Here is an additional Colab notebook that shows you one way to put many of these interest and payment formulas into Python functions. Also you will see an example of using some formulas to output results, notice a trend, and follow up with other formulas to analyze patterns. # --question-- diff --git a/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md b/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md index 39ab831dad7..144796de1b0 100644 --- a/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md +++ b/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md @@ -71,8 +71,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md b/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md index 53ae7b50350..871e1408079 100644 --- a/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md +++ b/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md @@ -73,7 +73,7 @@ substringDivisibility(5); ```js function substringDivisibility(n) { - function isSubDivisable(digits) { + function isSubDivisible(digits) { const factors = [2, 3, 5, 7, 11, 13, 17]; for (let i = 1; i < digits.length - 2; i++) { @@ -108,17 +108,17 @@ function substringDivisibility(n) { } const allowedDigits = [...new Array(n + 1).keys()]; - const divisablePandigitals = []; + const divisiblePandigitals = []; heapsPermutations( allowedDigits.length, allowedDigits, - isSubDivisable, - divisablePandigitals + isSubDivisible, + divisiblePandigitals ); let sum = 0; - for (let i = 0; i < divisablePandigitals.length; i++) { - sum += divisablePandigitals[i]; + for (let i = 0; i < divisiblePandigitals.length; i++) { + sum += divisiblePandigitals[i]; } return sum; diff --git a/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md b/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md index 7cd5bc67b14..2d583a52355 100644 --- a/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md +++ b/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md @@ -67,8 +67,8 @@ class PrimeSeive { const prime = 2 * i + 3; primes.push(prime); // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } @@ -101,10 +101,10 @@ class PrimeSeive { }; function consecutivePrimeSum(limit) { - // Initalize seive + // Initialize seive const primeSeive = new PrimeSeive(limit); - // Initalize for longest sum < 100 + // Initialize for longest sum < 100 let bestPrime = 41; let bestI = 0; let bestJ = 5; diff --git a/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md b/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md index cdbfc3e568b..5b55ddd8079 100644 --- a/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md +++ b/curriculum/challenges/german/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/german/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md b/curriculum/challenges/german/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md index 78c29aa7952..4fa15980a0d 100644 --- a/curriculum/challenges/german/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md +++ b/curriculum/challenges/german/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md @@ -31,10 +31,10 @@ For $1 ≤ k ≤ 200$, find $\sum{m(k)}$. # --hints-- -`efficientExponentation()` should return `1582`. +`efficientExponentiation()` should return `1582`. ```js -assert.strictEqual(efficientExponentation(), 1582); +assert.strictEqual(efficientExponentiation(), 1582); ``` # --seed-- @@ -42,12 +42,12 @@ assert.strictEqual(efficientExponentation(), 1582); ## --seed-contents-- ```js -function efficientExponentation() { +function efficientExponentiation() { return true; } -efficientExponentation(); +efficientExponentiation(); ``` # --solutions-- diff --git a/curriculum/challenges/german/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md b/curriculum/challenges/german/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md index 49d59948762..e402ff347c5 100644 --- a/curriculum/challenges/german/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md +++ b/curriculum/challenges/german/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/german/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md b/curriculum/challenges/german/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md index 596f703da28..b843f6f2be2 100644 --- a/curriculum/challenges/german/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md +++ b/curriculum/challenges/german/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md @@ -35,7 +35,7 @@ concealedSquare(); ```js // Check if n**2 matches the pattern -function squareMatchs(n) { +function squareMatches(n) { // Need BigInt due to size of values let nSquared = (BigInt(n) * BigInt(n)).toString(); @@ -55,8 +55,8 @@ function concealedSquare() { for (let x = maxSquareRoot; x >= minSquareRoot; x -= 10) { // Note: 3*3 = 9 and 7*7 = 49 are only trailing digits // that can produce 9 as trailing digit in square - if (squareMatchs(x + 3)) return (x + 3)*10; - if (squareMatchs(x + 7)) return (x + 7)*10; + if (squareMatches(x + 3)) return (x + 3)*10; + if (squareMatches(x + 7)) return (x + 7)*10; } return -1; } diff --git a/curriculum/challenges/italian/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md b/curriculum/challenges/italian/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md index 6eff44e6050..e725961bf33 100644 --- a/curriculum/challenges/italian/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md +++ b/curriculum/challenges/italian/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md @@ -54,7 +54,24 @@ assert.match(code, /myStorage\.car\.inside/); `gloveBoxContents` dovrebbe ancora essere dichiarata con `const`. ```js -assert.match(code, /const\s+gloveBoxContents\s*=\s*myStorage\.car\.inside\[\s*("|')glove box\1\s*\]|const\s*{\s*('|")glove box\2:\s*gloveBoxContents\s*}\s*=\s*myStorage\.car\.inside;/); +assert.match(code, /const\s+gloveBoxContents\s*=/); +``` + +You should not change the `myStorage` object. + +```js +const expectedMyStorage = { + "car":{ + "inside":{ + "glove box":"maps", + "passenger seat":"crumbs" + }, + "outside":{ + "trunk":"jack" + } + } +}; +assert.deepStrictEqual(myStorage, expectedMyStorage); ``` # --seed-- diff --git a/curriculum/challenges/italian/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md b/curriculum/challenges/italian/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md index 9dbd9619c0c..ad97f1a027a 100644 --- a/curriculum/challenges/italian/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md +++ b/curriculum/challenges/italian/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md @@ -10,7 +10,7 @@ dashedName: catch-misspelled-variable-and-function-names I metodi `console.log()` e `typeof` sono i due modi principali per controllare i valori intermedi e i tipi degli output del programma. Ora è il momento di approfondire le forme più comuni di bug. Un problema di sintassi di cui si rammaricano i digitatori veloci è il semplice errore di ortografia. -Caratteri spostati, mancanti, o con maiuscole errate nel nome di una variabile o di una funzione costringeranno il browser a cercare un oggetto che non esiste - e a lamentarsi tramite un errore di riferimento. In JavaScript, le variabili e i nomi di funzione sono sensibili alle maiuscole. +Transposed, missing, or miscapitalized characters in a variable or function name will have the browser looking for an object that doesn't exist - and complain in the form of a reference error. In JavaScript, le variabili e i nomi di funzione sono sensibili alle maiuscole. # --instructions-- diff --git a/curriculum/challenges/italian/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md b/curriculum/challenges/italian/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md index bac1aef0bb9..26538b4cb01 100644 --- a/curriculum/challenges/italian/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md +++ b/curriculum/challenges/italian/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md @@ -58,8 +58,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/italian/06-quality-assurance/quality-assurance-projects/issue-tracker.md b/curriculum/challenges/italian/06-quality-assurance/quality-assurance-projects/issue-tracker.md index 42d99b6c1ed..f1cc6c8dc9f 100644 --- a/curriculum/challenges/italian/06-quality-assurance/quality-assurance-projects/issue-tracker.md +++ b/curriculum/challenges/italian/06-quality-assurance/quality-assurance-projects/issue-tracker.md @@ -231,13 +231,13 @@ async (getUserInput) => { }; const url = getUserInput('url') + '/api/issues/fcc-project'; const itemToUpdate = await $.post(url, initialData); - const updateSucccess = await $.ajax({ + const updateSuccess = await $.ajax({ url: url, type: 'PUT', data: { _id: itemToUpdate._id, issue_text: 'New Issue Text' } }); - assert.isObject(updateSucccess); - assert.deepEqual(updateSucccess, { + assert.isObject(updateSuccess); + assert.deepEqual(updateSuccess, { result: 'successfully updated', _id: itemToUpdate._id }); diff --git a/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/24-game.md b/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/24-game.md index 41ba57e16d2..30f4ba7b2e5 100644 --- a/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/24-game.md +++ b/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/24-game.md @@ -82,7 +82,7 @@ const OPERATORS_ = { "/": (a, b) => a / b, } -const PRECIDENCE_ = { +const PRECEDENCE_ = { "+": 1, "-": 1, "*": 2, @@ -114,7 +114,7 @@ function evaluate_(expression) { case "*": case "/": while (stack.length && - PRECIDENCE_[c] <= PRECIDENCE_[stack[stack.length-1]]) { + PRECEDENCE_[c] <= PRECEDENCE_[stack[stack.length-1]]) { postfix += stack.pop(); } stack.push(c); diff --git a/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md b/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md index 9dfe4de0645..af717d80af4 100644 --- a/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md +++ b/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md @@ -175,7 +175,7 @@ function dealFreeCell(seed) { const rng = FreeCellRNG(seed); const deck = getDeck(); - const deltCards = [[], [], [], [], [], [], []]; + const dealtCards = [[], [], [], [], [], [], []]; let currentColumn = 0; let currentRow = 0; @@ -195,7 +195,7 @@ function dealFreeCell(seed) { card = deck.pop(); // Deal this card - deltCards[currentRow].push(card); + dealtCards[currentRow].push(card); currentColumn += 1; if (currentColumn === 8) { currentColumn = 0; @@ -203,6 +203,6 @@ function dealFreeCell(seed) { } } - return deltCards; + return dealtCards; } ``` diff --git a/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/department-numbers.md b/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/department-numbers.md index 51f0986cea2..6397735c700 100644 --- a/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/department-numbers.md +++ b/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/department-numbers.md @@ -101,7 +101,7 @@ function combinations(possibleNumbers, total) { function combinations(possibleNumbers, total) { let firstNumber; let secondNumber; - let thridNumber; + let thirdNumber; const allCombinations = []; for (let i = 0; i < possibleNumbers.length; i += 1) { @@ -112,10 +112,10 @@ function combinations(possibleNumbers, total) { secondNumber = possibleNumbers[j]; if (j !== i && firstNumber + secondNumber <= total) { - thridNumber = total - firstNumber - secondNumber; + thirdNumber = total - firstNumber - secondNumber; - if (thridNumber !== firstNumber && thridNumber !== secondNumber && possibleNumbers.includes(thridNumber)) { - allCombinations.push([firstNumber, secondNumber, thridNumber]); + if (thirdNumber !== firstNumber && thirdNumber !== secondNumber && possibleNumbers.includes(thirdNumber)) { + allCombinations.push([firstNumber, secondNumber, thirdNumber]); } } } diff --git a/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/lu-decomposition.md b/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/lu-decomposition.md index 3d6d87028f6..e610042ffe8 100644 --- a/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/lu-decomposition.md +++ b/curriculum/challenges/italian/10-coding-interview-prep/rosetta-code/lu-decomposition.md @@ -80,7 +80,7 @@ $PA = LU$ # --instructions-- -Il compito è quello di implementare una routine che richiederà una matrice quadrata nxn $A$ e restituirà una matrice triangolare inferiore $L$, una matrice triangolare superiore $U$ e una matrice di permutazione $P$, in modo che l'equazione di cui sopra sia soddisfatta. Il valore restituito dovrebbe essere nella forma `[L, U, P]`. +The task is to implement a routine which will take a square nxn matrix $A$ and return a lower triangular matrix $L$, a upper triangular matrix $U$ and a permutation matrix $P$, so that the above equation is fulfilled. Il valore restituito dovrebbe essere nella forma `[L, U, P]`. # --hints-- diff --git a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/615380dff67172357fcf0425.md b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/615380dff67172357fcf0425.md index 6d612a773be..457a4b81b84 100644 --- a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/615380dff67172357fcf0425.md +++ b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/615380dff67172357fcf0425.md @@ -7,9 +7,9 @@ dashedName: step-11 # --description-- -Now your images are too big. +Ora le immagini sono troppo grandi. -Create a `.gallery img` selector to target them. Give them all a `width` of `100%` and a `max-width` of `350px`. +Create a `.gallery img` selector to target them. Imposta per tutte `width` al `100%` e `max-width` a `350px`. Also set the `height` property to `300px` to keep your images a uniform size. diff --git a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/615f171d05def3218035dc85.md b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/615f171d05def3218035dc85.md index a2756de469f..cd7dd66884b 100644 --- a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/615f171d05def3218035dc85.md +++ b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/615f171d05def3218035dc85.md @@ -18,7 +18,7 @@ You should have a `body` selector. assert.exists(new __helpers.CSSHelp(document).getStyle('body')); ``` -Your `body` selector should have a `margin` property set to 0 as the value. +Il selettore `body` dovrebbe avere una proprietà `margin` con il valore 0. ```js assert.equal(new __helpers.CSSHelp(document).getStyle('body')?.marginTop, '0px'); diff --git a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/6493bc0d99879635209565aa.md b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/6493bc0d99879635209565aa.md index 07af535331f..5511946776c 100644 --- a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/6493bc0d99879635209565aa.md +++ b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/6493bc0d99879635209565aa.md @@ -7,9 +7,9 @@ dashedName: step-9 # --description-- -The `border-box` sizing model does the opposite of `content-box`. The total width of the element, including padding and border, will be the explicit width set. The content of the element will shrink to make room for the padding and border. +Il modello di dimensionamento `border-box` fa l'opposto di `content-box`. La larghezza totale dell'elemento, inclusi padding e bordo, sarà la larghezza esplicitamente impostata. Il contenuto dell'elemento si restringerà per fare spazio a padding e bordo. -Change the `box-sizing` property to `border-box`. Notice how your blue image borders now fit within your red gallery border. +Cambia la proprietà `box-sizing` in `border-box`. Nota come i bordi delle immagini blu ora si adattano al bordo della galleria rossa. # --hints-- diff --git a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/6494da0daf5df5197963671d.md b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/6494da0daf5df5197963671d.md index 781bfe093d1..d450a501ce7 100644 --- a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/6494da0daf5df5197963671d.md +++ b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/6494da0daf5df5197963671d.md @@ -7,11 +7,11 @@ dashedName: step-8 # --description-- -Notice how the blue image border extends beyond the red gallery border. This is due to the way browsers calculate the size of container elements. +Nota come i bordi delle immagini blu ora vanno oltre il bordo della galleria rossa. Ciò è dovuto al modo in cui i browser calcolano la dimensione degli elementi contenitore. -The `box-sizing` property is used to set this behavior. By default, the `content-box` model is used. With this model, when an element has a specific width, that width is calculated based only on the element's content. Padding and border values get added to the total width, so the element grows to accommodate these values. +La proprietà `box-sizing` viene utilizzata per definire questo comportamento. Per impostazione predefinita, viene utilizzato il modello `content-box`. Con questo modello, quando un elemento ha una larghezza specifica, tale larghezza viene calcolata solo in base al contenuto dell'elemento. I valori di padding e bordo vengono aggiunti alla larghezza totale, con l'elemento che cresce per adattarsi a questi valori. -Try setting `box-sizing` to `content-box` explicitly, with the global `*` selector. At this point, you will not see any changes, because you are using the default value. +Prova a impostare esplicitamente `box-sizing` su `content-box` con il selettore globale `*`. In questo momento, non vedrai alcun cambiamento, perché stai usando il valore predefinito. # --hints-- @@ -21,13 +21,13 @@ You should have a `*` selector. assert.exists(new __helpers.CSSHelp(document).getStyle('*')); ``` -Your `*` selector should have a `box-sizing` property. +Il selettore `*` dovrebbe avere una proprietà `box-sizing`. ```js assert.exists(new __helpers.CSSHelp(document).getStyle('*')?.boxSizing); ``` -Your `*` selector should have its `box-sizing` property set to `content-box`. +Il selettore `*` dovrebbe avere la proprietà `box-sizing` impostata su `content-box`. ```js assert.equal(new __helpers.CSSHelp(document).getStyle('*')?.boxSizing, 'content-box'); diff --git a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/64dd076a1b33c86d84b4232f.md b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/64dd076a1b33c86d84b4232f.md index b62a6d87885..64bafb44ad1 100644 --- a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/64dd076a1b33c86d84b4232f.md +++ b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/64dd076a1b33c86d84b4232f.md @@ -7,11 +7,11 @@ dashedName: step-7 # --description-- -In order to better visualize how your elements are sized, adding a border can be helpful. +Per visualizzare meglio la dimensione degli elementi, può essere utile aggiungere un bordo. -Give your `.gallery` element a `width` of `50%` and a `border` set to `5px solid red`. +Per l'elemento `.gallery` imposta `width` al `50%` e `border` su `5px solid red`. -Then give your `img` elements a `width` of `100%`, `padding` set to `5px`, and a `border` set to `5px solid blue`. +Quindi dai agli elementi `img` una `width` del `100%`, imposta `padding` su `5px` e `border` su `5px solid blue`. # --hints-- @@ -21,25 +21,25 @@ You should have a `.gallery` selector. assert.exists(new __helpers.CSSHelp(document).getStyle('.gallery')); ``` -Your `.gallery` selector should have a `width` property. +Il selettore `.gallery` dovrebbe avere una proprietà `width`. ```js assert.exists(new __helpers.CSSHelp(document).getStyle('.gallery')?.width); ``` -Your `.gallery` selector should have a `width` property set to `50%`. +Il selettore `.gallery` dovrebbe avere una proprietà `width` impostata al `50%`. ```js assert.equal(new __helpers.CSSHelp(document).getStyle('.gallery')?.width, '50%'); ``` -Your `.gallery` selector should have a `border` property. +Il selettore `.gallery` dovrebbe avere una proprietà `border`. ```js assert.exists(new __helpers.CSSHelp(document).getStyle('.gallery')?.border); ``` -Your `.gallery` selector should have a `border` property set to `5px solid red`. +Il selettore `.gallery` dovrebbe avere una proprietà `border` con il valore `5px solid red`. ```js assert.equal(new __helpers.CSSHelp(document).getStyle('.gallery')?.border, '5px solid red'); @@ -51,7 +51,7 @@ You should have an `img` selector. assert.exists(new __helpers.CSSHelp(document).getStyle('img')); ``` -Your `img` selector should have a `width` property. +Il selettore `img` dovrebbe avere una proprietà `width`. ```js assert.exists(new __helpers.CSSHelp(document).getStyle('img')?.width); @@ -63,25 +63,25 @@ Your `img` selector should have a `width` property set to `100%`. assert.equal(new __helpers.CSSHelp(document).getStyle('img')?.width, '100%'); ``` -Your `img` selector should have a `padding` property. +Il selettore `img` dovrebbe avere una proprietà `padding`. ```js assert.exists(new __helpers.CSSHelp(document).getStyle('img')?.padding); ``` -Your `img` selector should have a `padding` property set to `5px`. +Il selettore `img` dovrebbe avere una proprietà `padding` con il valore `5px`. ```js assert.equal(new __helpers.CSSHelp(document).getStyle('img')?.padding, '5px'); ``` -Your `img` selector should have a `border` property. +Il selettore `img` dovrebbe avere una proprietà `border`. ```js assert.exists(new __helpers.CSSHelp(document).getStyle('img')?.border); ``` -Your `img` selector should have a `border` property set to `5px solid blue`. +Il selettore `img` dovrebbe avere una proprietà `border` impostata su `5px solid blue`. ```js assert.equal(new __helpers.CSSHelp(document).getStyle('img')?.border, '5px solid blue'); diff --git a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/64dd136e4e8b0894f9c70d86.md b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/64dd136e4e8b0894f9c70d86.md index 7b1c43d14af..0de477feea4 100644 --- a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/64dd136e4e8b0894f9c70d86.md +++ b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-flexbox-by-building-a-photo-gallery/64dd136e4e8b0894f9c70d86.md @@ -7,19 +7,19 @@ dashedName: step-10 # --description-- -Now that you have figured out your `box-sizing` approach, you can clean up the CSS you added to see the changes. +Ora che hai capito l'approccio `box-sizing`, puoi ripulire il CSS che hai aggiunto per vedere le modifiche. -Remove your `.gallery` and `img` selectors, and all rules within. +Rimuovi i selettori `.gallery` e `img` e tutte le regole al loro interno. # --hints-- -You should not have a `.gallery` selector. +Non dovresti avere un selettore `.gallery`. ```js assert.notExists(new __helpers.CSSHelp(document).getStyle('.gallery')); ``` -You should not have an `img` selector. +Non dovresti avere un selettore `img`. ```js assert.notExists(new __helpers.CSSHelp(document).getStyle('img')); diff --git a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md index 734cb3bbf53..a66d8d19ef4 100644 --- a/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md +++ b/curriculum/challenges/italian/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md @@ -9,7 +9,7 @@ dashedName: step-82 Il becco e le zampe del pinguino condividono lo stesso valore `color`. -Crea una nuova variabile CSS personalizzata denominata `--penguin-picorna` e sostituisci tutti i valori della proprietà dove necessario. +Create a new custom CSS variable named `--penguin-picorna`, and replace all relevant property values with it. # --hints-- diff --git a/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-forms-by-building-a-registration-form/60fab8367d35de04e5cb7929.md b/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-forms-by-building-a-registration-form/60fab8367d35de04e5cb7929.md index 1037e09359d..e6032d98a7f 100644 --- a/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-forms-by-building-a-registration-form/60fab8367d35de04e5cb7929.md +++ b/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-forms-by-building-a-registration-form/60fab8367d35de04e5cb7929.md @@ -7,7 +7,7 @@ dashedName: step-31 # --description-- -Add `I accept the terms and conditions` text to the newly added label, then link the text `terms and conditions` to the following location: +Aggiungi il testo `I accept the terms and conditions` al label appena aggiunto, poi collega il testo `terms and conditions` alla posizione seguente: ```md https://www.freecodecamp.org/news/terms-of-service/ @@ -15,25 +15,25 @@ https://www.freecodecamp.org/news/terms-of-service/ # --hints-- -You should add `I accept the terms and conditions` text to the label following the third fieldset. +Dovresti aggiungere il testo `I accept the terms and conditions` al label successivo al terzo fieldset. ```js assert.equal(document.querySelector('fieldset:nth-child(3) + label')?.innerText.trim(), 'I accept the terms and conditions'); ``` -You should use an `a` element to link to the terms and conditions. +Dovresti usare un elemento `a` per trasformare terms and conditions in un link. ```js assert.exists(document.querySelector('fieldset:nth-child(3) + label > input + a')); ``` -You should give the `a` element an `href` of `https://www.freecodecamp.org/news/terms-of-service/`. +Dovresti assegnare all'elemento `a` un attributo `href` con il valore `https://www.freecodecamp.org/news/terms-of-service/`. ```js assert.match(document.querySelector('fieldset:nth-child(3) + label > input + a')?.href, /https:\/\/www\.freecodecamp\.org\/news\/terms-of-service\/?/); ``` -You should only wrap the `a` element around the text `terms and conditions`. +L'elemento `a` dovrebbe racchiudere soltanto il testo `terms and conditions`. ```js assert.equal(document.querySelector('fieldset:nth-child(3) + label > input + a')?.textContent, 'terms and conditions'); diff --git a/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-forms-by-building-a-registration-form/62ff8e998d3e7eae14d6ae3b.md b/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-forms-by-building-a-registration-form/62ff8e998d3e7eae14d6ae3b.md index e347ba46d5e..0991cd19fa1 100644 --- a/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-forms-by-building-a-registration-form/62ff8e998d3e7eae14d6ae3b.md +++ b/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-forms-by-building-a-registration-form/62ff8e998d3e7eae14d6ae3b.md @@ -7,45 +7,45 @@ dashedName: step-30 # --description-- -You need to confirm that the user has read the terms and conditions. +Devi confermare che l'utente ha letto i termini e le condizioni. -Add `label` element after the third `fieldset`, and a `input` element with `type` attribute of `checkbox` inside the newly added `label` element. Make this `input` element `required` because users should not sign up without reading the terms and conditions. +Aggiungi un elemento `label` dopo il terzo `fieldset` e un elemento `input` con un attributo`type` del valore di `checkbox` all'interno dell'elemento `label` appena aggiunto. Fai sì che questo elemento `input` sia `required` in modo che gli utenti non possano iscriversi senza leggere i termini e le condizioni di utilizzo. -Don't forget to add an `id` attribute of `terms-and-conditions` for accessibility. +Non dimenticare di aggiungere un attributo `id` del valore `terms-and-conditions` per l'accessibilità. # --hints-- -You should add an `label` after the third `fieldset` element. +Dovresti aggiungere un `label` dopo il terzo elemento `fieldset`. ```js assert.exists(document.querySelector('fieldset:nth-child(3) + label')); ``` -You should add an `input` to the `label` element. +Dovresti aggiungere un `input` all'elemento `label`. ```js assert.exists(document.querySelector('fieldset:nth-child(3) + label > input')); ``` -You should add a `type` attribute of value `checkbox` to the `input` element. +Dovresti aggiungere un attributo `type` con valore `checkbox` all'elemento `input`. ```js assert.equal(document.querySelector('fieldset:nth-child(3) + label > input')?.type, 'checkbox'); ``` -You should add a `required` attribute to the `input` element. +Dovresti aggiungere un attributo `required` all'elemento `input`. ```js assert.equal(document.querySelector('fieldset:nth-child(3) + label > input')?.required, true); ``` -The `input` element should have an `id` of `terms-and-conditions`. +L'elemento `input` dovrebbe avere un `id` con il valore `terms-and-conditions`. ```js assert(document.querySelector('fieldset:nth-child(3) + label > input')?.matches('#terms-and-conditions')) ``` -The `label` element should have a `for` attribute with a value of `terms-and-conditions`. +L'elemento `label` dovrebbe avere un attributo `for` con il valore `terms-and-conditions`. ```js assert(document.querySelector('fieldset:nth-child(3) + label')?.matches('label[for="terms-and-conditions"]')) diff --git a/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-forms-by-building-a-registration-form/65045fa2267ce52da6a73676.md b/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-forms-by-building-a-registration-form/65045fa2267ce52da6a73676.md index 927b3680160..5aeb9643796 100644 --- a/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-forms-by-building-a-registration-form/65045fa2267ce52da6a73676.md +++ b/curriculum/challenges/italian/14-responsive-web-design-22/learn-html-forms-by-building-a-registration-form/65045fa2267ce52da6a73676.md @@ -7,31 +7,31 @@ dashedName: step-29 # --description-- -Follow accessibility best practices by linking the `input` elements and the `label` elements in the second `fieldset`. +Segui le migliori pratiche di accessibilità collegando gli elementi `input` e gli elementi `label` nel secondo `fieldset`. -Use `personal-account`, and `business-account` as values for the respective `id` attributes. +Usa `personal-account` e `business-account` come valori per i rispettivi attributi `id`. # --hints-- -The first `input` element should have an `id` of `personal-account`. +Il primo elemento `input` dovrebbe avere un `id` con il valore `personal-account`. ```js assert(document.querySelectorAll('fieldset:nth-of-type(2) input')?.[0]?.matches('#personal-account')) ``` -The second `input` element should have an `id` of `business-account`. +Il secondo elemento `input` dovrebbe avere un `id` con il valore `business-account`. ```js assert(document.querySelectorAll('fieldset:nth-of-type(2) input')?.[1]?.matches('#business-account')) ``` -The first `label` element should have a `for` attribute with a value of `personal-account`. +Il primo elemento `label` dovrebbe avere un attributo `for` con il valore `personal-account`. ```js assert(document.querySelectorAll('fieldset:nth-of-type(2) label')?.[0]?.matches('label[for="personal-account"]')) ``` -The second `label` element should have a `for` attribute with a value of `business-account`. +Il secondo elemento `label` dovrebbe avere un attributo `for` con il valore `business-account`. ```js assert(document.querySelectorAll('fieldset:nth-of-type(2) label')?.[1]?.matches('label[for="business-account"]')) diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md index db3c1484e3e..395c89c81d3 100644 --- a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md @@ -11,7 +11,7 @@ All'interno di `pick`, usa `let` per inizializzare una variabile chiamata `numbe # --hints-- -La funzione `pick` dovrebbe inizializzare `numbers` su un array vuoto `[]`. +Your `pick` function should initialize `numbers` to an empty array `[]`. ```js assert.match(pick.toString(), /numbers\s*=\s*\[\]/); diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md new file mode 100644 index 00000000000..4b042aa87ba --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md @@ -0,0 +1,310 @@ +--- +id: 64e4e4c4ec263b62ae7bf54d +title: Step 1 +challengeType: 0 +dashedName: step-1 +--- + +# --description-- + +In this project, you will learn how `localStorage` works in JavaScript by building a Todo app. LocalStorage is a web storage feature of JavaScript that lets you persist data by storing the data as a **key:value** pair. + +The HTML and CSS for this project have been provided for you. Take a look at the files to get yourself familiarized with them. + +Begin by accessing the `task-form`, `confirm-close-dialog`, and `open-task-form-btn` elements with the `getElementById()` method. Save them in the variables `taskForm`, `confirmCloseDialog`, and `openTaskFormBtn`. + +# --hints-- + +You should use `getElementById()` to access the `task-form` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should assign the `task-form` element to the variable `taskForm`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+taskForm\s*=\s*document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should use `getElementById()` to access the `confirm-close-dialog` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should assign the `confirm-close-dialog` element to the variable `confirmCloseDialog`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+confirmCloseDialog\s*=\s*document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should use `getElementById()` to access the `open-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +You should assign the `open-task-form-btn` element to the variable `openTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+openTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md new file mode 100644 index 00000000000..32310409728 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md @@ -0,0 +1,310 @@ +--- +id: 64e4e6c86954de67a3e44ee3 +title: Step 2 +challengeType: 0 +dashedName: step-2 +--- + +# --description-- + +You need to access more elements with the `getElementById()` method. This time you need the `close-task-form-btn`, `add-or-update-task-btn`, and `cancel-btn` elements. Save them in the variables `closeTaskFormBtn`, `addOrUpdateTaskBtn`, and `cancelBtn`. + +# --hints-- + +You should use `getElementById()` to access the `close-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should assign the `close-task-form-btn` element to the variable `closeTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+closeTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `add-or-update-task-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should assign the `add-or-update-task-btn` element to the variable `addOrUpdateTaskBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+addOrUpdateTaskBtn\s*=\s*document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `cancel-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +You should assign the `cancel-btn` element to the variable `cancelBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+cancelBtn\s*=\s*document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md new file mode 100644 index 00000000000..17fc6b76517 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md @@ -0,0 +1,313 @@ +--- +id: 64e4e6fe78b5aa67ef2fc3e7 +title: Step 3 +challengeType: 0 +dashedName: step-3 +--- + +# --description-- + +Next, access the `discard-btn`, `tasks-container`, and `title-input` elements using the `getElementById()` method. Save them in variables named `discardBtn`, `tasksContainer`, and `titleInput`, respectively. + +# --hints-- + +You should use `getElementById()` to access the `discard-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should assign the `discard-btn` element to the variable `discardBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+discardBtn\s*=\s*document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `tasks-container` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should assign the `tasks-container` element to the variable `tasksContainer`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+tasksContainer\s*=\s*document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should use `getElementById()` to access the `title-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +You should assign the `title-input` element to the variable `titleInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+titleInput\s*=\s*document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md new file mode 100644 index 00000000000..6e543d36acc --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md @@ -0,0 +1,304 @@ +--- +id: 64e4e7241f52bb682eeb8211 +title: Step 4 +challengeType: 0 +dashedName: step-4 +--- + +# --description-- + +The last set of elements you need to get from the HTML file are the `date-input` and `description-input` elements. Save them in the variables `dateInput` and `descriptionInput` respectively. + +# --hints-- + +You should use `getElementById()` to access the `date-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should assign the `date-input` element to the variable `dateInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+dateInput\s*=\s*document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should use `getElementById()` to access the `description-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +You should assign the `description-input` element to the variable `descriptionInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+descriptionInput\s*=\s*document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md new file mode 100644 index 00000000000..6f0cd6e9fc4 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md @@ -0,0 +1,296 @@ +--- +id: 64e4e74d0fb4f0687bf4145d +title: Step 5 +challengeType: 0 +dashedName: step-5 +--- + +# --description-- + +Create a `taskData` constant and set it to an empty array. This array will store all the tasks along with their associated data, including title, due date, and description. This storage will enable you to keep track of tasks, display them on the page, and save them to `localStorage`. + +Use `let` to create a `currentTask` variable and set it to an empty object. This variable will be used to track the state when editing and discarding tasks. + +# --hints-- + +You should create a `taskData` constant set to an empty array. + +```js +assert.match(code, /const\s+taskData\s*=\s*\[\];?/) +``` + +You should use `let` to create an empty `currentTask` object. + +```js +assert.match(code, /let\s+currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md new file mode 100644 index 00000000000..7eba2896bf4 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md @@ -0,0 +1,307 @@ +--- +id: 64e4e78a7ea4a168de4e6a38 +title: Step 6 +challengeType: 0 +dashedName: step-6 +--- + +# --description-- + +Now, you will work on opening and closing the form modal. + +Add a `click` event listener to the `openTaskFormBtn` variable, and then use `classList` to toggle the `hidden` class on the `taskForm` element. + +Now you can click on the "Add new Task" button and see the form modal. + +# --hints-- + +You should call the `addEventListener()` method on your `openTaskFormBtn` variable. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax, then use `classList` property and it's `toggle` method on the `taskForm`, to toggle the class `hidden`. Don't use curly braces. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*taskForm\.classList\.toggle\(\1hidden\1\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md new file mode 100644 index 00000000000..6a29158134e --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md @@ -0,0 +1,309 @@ +--- +id: 64e4e7bbedb22d6939001ad3 +title: Step 7 +challengeType: 0 +dashedName: step-7 +--- + +# --description-- + +Add a `click` event listener to the `closeTaskFormBtn` variable, then use the `showModal()` method on your `confirmCloseDialog` variable. This will display a modal with the `Discard` and `Cancel` buttons. + +`showModal()` is a method associated with the HTML `dialog` element. It is used to display a modal dialog box on a web page. + +# --hints-- + +You should call the `addEventListener()` method on your `closeTaskFormBtn` variable. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `showModal()` method on `confirmCloseDialog`. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(["'`]click["'`],\s*\(.*\)\s*=>\s*{?\s*confirmCloseDialog\.showModal\(\);?\s*}?\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md new file mode 100644 index 00000000000..311984043a0 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md @@ -0,0 +1,316 @@ +--- +id: 64e4eaaa9070a66aecbfe603 +title: Step 8 +challengeType: 0 +dashedName: step-8 +--- + +# --description-- + +If the user clicks the `Cancel` button, you want to cancel the process (close the modal showing the two buttons) so the user can continue editing. + +Add a `click` event listener to the `cancelBtn` element, then use the `close()` method on the `confirmCloseDialog` variable. + +`close()` is a method of the window object you can use to close the current window, or a modal you create with the `dialog` element. + +# --hints-- + +You should call the `addEventListener()` method on your `cancelBtn` variable. + +```js +assert.match(code, /cancelBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `close()` method on `confirmCloseDialog`. Don't use curly braces. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*confirmCloseDialog\.close\(\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- + +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md new file mode 100644 index 00000000000..66a40c9771b --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md @@ -0,0 +1,327 @@ +--- +id: 64e4ebc7eabc5a6babd479cd +title: Step 9 +challengeType: 0 +dashedName: step-9 +--- + +# --description-- + +If the user clicks the `Discard` button, you want to close the modal showing the `Cancel` and `Discard` buttons, then hide the form modal. + +Add a click event listener to `discardBtn`, then use the `close()` method on the `confirmCloseDialog` variable. Also, use `classList` to toggle the class `hidden` on `taskForm` so the form modal will close too. + +# --hints-- + +You should call the `addEventListener()` method on your `discardBtn` variable. + +```js +assert.match(code, /discardBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{/) +``` + +Your event listener should use the `close()` method on `confirmCloseDialog`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?/) +``` + +Your event listener should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?\s*taskForm\.classList\.toggle\(\1hidden\1\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md new file mode 100644 index 00000000000..f4002dc0937 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md @@ -0,0 +1,326 @@ +--- +id: 64e4ecd7735a566c9266a338 +title: Step 10 +challengeType: 0 +dashedName: step-10 +--- + +# --description-- + +Now that you've worked on opening and closing the modal, it's time to get the values from the input fields, save them into the `taskData` array, and display them on the page. + +To start, add a `submit` event listener to your `taskForm` element and pass in `e` as the parameter of your arrow function. Inside the curly braces, use the `preventDefault()` method to stop the browser from refreshing the page after submitting the form. + +# --hints-- + +You should call the `addEventListener()` method on your `taskForm` variable. + +```js +assert.match(code, /taskForm\.addEventListener\(/) +``` + +Your event listener should listen for a `submit` event. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces with `e` as the parameter. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{/) +``` + +You should use the `e.preventDefault()` method to stop the browser from reloading the page. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{\s*e\.preventDefault\(\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md new file mode 100644 index 00000000000..148e52f3e93 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md @@ -0,0 +1,337 @@ +--- +id: 64e4eec13546c06d61a63d59 +title: Step 11 +challengeType: 0 +dashedName: step-11 +--- + +# --description-- + +You need to determine whether the task being added already exists or not. If it doesn't exist, you will add it, and if it already exists, you will set it up for editing. You can use the `findIndex()` method to accomplish this. + +`findIndex` is an array method that lets find the index of the first element in an array that satisfies a given testing function. + +Here's an example: + +```js +const numbers = [3, 1, 5, 6, 10, 9, 8]; +const firstEvenNumIndex = numbers.findIndex((num) => num % 2 === 0); + +console.log(firstEvenNumIndex); // Output: 3 – because the first even number (6) is at index 3 +``` + +Declare a `dataArrIndex` variable using `const` and set it to the result of the `findIndex()` method applied to the `taskData` array. Utilize arrow syntax to provide a callback function with `item` as the parameter, and within the callback, check if the `id` property of `item` is equal to the `id` property of `currentTask`. + +If the task exists, this returns the index, and if it doesn't exist, it returns `-1`. + +# --hints-- + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === currentTask.id`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?\s*=>\s*item.id\s*===\s*currentTask.id\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md new file mode 100644 index 00000000000..10826de6f17 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md @@ -0,0 +1,340 @@ +--- +id: 64e4f01d6c72086e016a8626 +title: Step 12 +challengeType: 0 +dashedName: step-12 +--- + +# --description-- + +Next, retrieve the values from the input fields and store them in a `taskObj` object. Each task should also have a unique `id`. + +Create a `taskObj` object with an `id` property as the first property. For the value of the `id` property, retrieve the value of the `titleInput` field, convert it to lowercase, and then use the `split()` and `join()` methods to hyphenate it. + +Make sure all of those are in template literals because you need the `id` property value as a string. + +# --hints-- + +You should use `const` to create a `taskObj` object. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*/) +``` + +Your `taskObj` object should have a key of `id`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id/) +``` + +You should use template strings to get `titleInput.value` as the value of your `id` key and convert it to lowercase. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)/) +``` + +You should use `.split(' ')` on `titleInput.value.toLowerCase()`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)/) +``` + +You should use `.join('-')` on `titleInput.value.toLowerCase().split(' ')`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md new file mode 100644 index 00000000000..764de1b0918 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md @@ -0,0 +1,327 @@ +--- +id: 64ec89ee549ecf802de2b3e2 +title: Step 14 +challengeType: 0 +dashedName: step-14 +--- + +# --description-- + +Retrieve the values from the `titleInput`, `dateInput`, and `descriptionInput` fields, and then save them in the properties `title`, `date`, and `description` of the `taskObj` object. + +# --hints-- + +You should get the value of `titleInput` and save it in a `title` key. + +```js +assert.match(code, /title:\s*titleInput\.value,/) +``` + +You should get the value of `dateInput` and save it in a `date` key. + +```js +assert.match(code, /date:\s*dateInput\.value,/) +``` + +You should get the value of `descriptionInput` and save it in a `description` key. + +```js +assert.match(code, /description:\s*descriptionInput\.value,?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + const taskObj = { + id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`, + --fcc-editable-region-- + + --fcc-editable-region-- + }; +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md new file mode 100644 index 00000000000..3d009188d81 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md @@ -0,0 +1,332 @@ +--- +id: 64ec8f717b261e824d82d6a5 +title: Step 15 +challengeType: 0 +dashedName: step-15 +--- + +# --description-- + +Now that you have obtained the values from the input fields and generated an `id`, you want to add them to your `taskData` array to keep track of each task. However, you should only do this if the task is new. If the task already exists, you will set it up for editing. This is why you have the `dataArrIndex` variable, which provides the index of each task. + +Create an `if` statement with the condition `dataArrIndex === -1`. Within the `if` statement, use the `unshift()` method to add the `taskObj` object to the beginning of the `taskData` array. + +`unshift()` is an array method that is used to add one or more elements to the beginning of an array. + +**N.B:** Try adding a task and log `taskData` to the console to see what it looks like. + +# --hints-- + +You should create an `if` statement with the condition `dataArrIndex === -1`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{/) +``` + +Your `if` statement should have `taskData.unshift(taskObj)` in it's body. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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, + }; + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md new file mode 100644 index 00000000000..f1202bb93fe --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md @@ -0,0 +1,331 @@ +--- +id: 64ec9145e424d8835a4e0f28 +title: Step 16 +challengeType: 0 +dashedName: step-16 +--- + +# --description-- + +Now that you have saved the task in the `taskData` array, you should display the task on the page by looping through it. + +Use `forEach()` on `taskData`, then destructure `id`, `title`, `date`, `description` as the parameters. Don't return anything yet. + +# --hints-- + +You should use `forEach()` on `taskData`. + +```js +assert.match(code, /taskData\.forEach\(\s*/) +``` + +You should destructure `id`, `title`, `date`, `description` as the parameters of the callback of your `forEach()`. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md new file mode 100644 index 00000000000..06bdd7c6ae5 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md @@ -0,0 +1,336 @@ +--- +id: 64ec9282cd547785258cecf2 +title: Step 17 +challengeType: 0 +dashedName: step-17 +--- + +# --description-- + +Using arrow syntax, create an implicit return and use addition assignment to set the `innerHTML` of `tasksContainer` to empty backticks. + +# --hints-- + +You should not alter the existing `taskData.forEach()` and the values you destructured. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +You should use arrow syntax and attach `innerHTML` to `tasksContainer`. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\(\s*tasksContainer\.innerHTML\s*\))?/) +``` + +You should use addition assignment to set the `innerHTML` of `tasksContainer` to an empty pair of backticks. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\s*\(?tasksContainer\.innerHTML\s*\+=\s*`\s*`\)?)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + taskData.forEach(({id, title, date, description})); + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md new file mode 100644 index 00000000000..b5115f99a51 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md @@ -0,0 +1,333 @@ +--- +id: 64ec9343769e8f85c1e17e05 +title: Step 18 +challengeType: 0 +dashedName: step-18 +--- + +# --description-- + +Create a `div` element with the class of `task`. Utilize template strings to set the `id` attribute of the `div` to the `id` you destructured from the task data. + +# --hints-- + +You should create a `div` element with the class `task`. + +```js +assert.match(code, /\s*<\/div>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` + --fcc-editable-region-- + + --fcc-editable-region-- + `) + ); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md new file mode 100644 index 00000000000..81dd3530420 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md @@ -0,0 +1,348 @@ +--- +id: 64ec94f0de20c086e09b0fc3 +title: Step 19 +challengeType: 0 +dashedName: step-19 +--- + +# --description-- + +Create a `p` element and use template strings to set its content to the `title` you destructured. Right before the content of the `p` element, create a `strong` element with the text `Title:`. + +# --hints-- + +You should create a `p` element. + +```js +assert.match(code, /

/) +``` + +You should interpolate `${title}` as the text of your `p` element. + +```js +assert.match(code, /

.*\$\{title\}<\/p>/) +``` + +You should create a `strong` element after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)/) +``` + +Your `strong` element should have the text `Title:`. + +```js +assert.match(code, /(?<=

)Title:\s*<\/strong>\s*/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md new file mode 100644 index 00000000000..4820d100d4d --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md @@ -0,0 +1,336 @@ +--- +id: 64ec959a76336c8767f5cd4d +title: Step 20 +challengeType: 0 +dashedName: step-20 +--- + +# --description-- + +Similarly to the previous step, create another `p` element, and interpolate the `date` you destructured as the text content. Inside this paragraph, create a `strong` element with the text `Date:`. + +# --hints-- + +You should create a `p` element and interpolate `${date}` as the text. + +```js +assert.match(code, /

.*\$\{date\}<\/p>/) +``` + +You should create a `strong` element with the text `Date:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Date:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md new file mode 100644 index 00000000000..4525b0e154c --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md @@ -0,0 +1,340 @@ +--- +id: 64ec96761156a187ed32b274 +title: Step 22 +challengeType: 0 +dashedName: step-22 +--- + +# --description-- + +To allow for task management, you need to include both a delete and an edit button for each task. + +Create two `button` elements with the `type` attribute set to `button` and the `class` attribute set to `btn`. Set the text of the first button to `Edit` and the text of the second button to `Delete`. + +# --hints-- + +You should create a `button` element of type `button`, a class `btn` and `Edit` as the text, in that order. + +```js +assert.match(code, /Edit<\/button/) +``` + +You should create a `button` element of type `button` a class `btn` and `Delete` as the text, in that order. + +```js +assert.match(code, /Delete<\/button/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md new file mode 100644 index 00000000000..0081fa88e1a --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md @@ -0,0 +1,336 @@ +--- +id: 64ec9b10356c2d8aa05d9ce1 +title: Step 23 +challengeType: 0 +dashedName: step-23 +--- + +# --description-- + +After adding the task to the page, you should close the form modal to view the task. To do this, utilize `classList` to toggle the `hidden` class on the `taskForm` element. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +const splitter = code.split("taskData.forEach") +assert.match(splitter[1], /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md new file mode 100644 index 00000000000..6ae1e4dff48 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md @@ -0,0 +1,347 @@ +--- +id: 64ec9c55fdeef78bacd2fc3b +title: Step 24 +challengeType: 0 +dashedName: step-24 +--- + +# --description-- + +If you attempt to add another task now, you'll notice that the input fields retain the values you entered for the previous task. To resolve this, you need to clear the input fields after adding a task. + +Instead of clearing the input fields one by one, it's a good practice to create a function that handles clearing those fields. You can then call this function whenever you need to clear the input fields again. + +Use arrow syntax to create a `reset` function and set it to a pair of curly braces. + +# --hints-- + +You should use `const` and arrow syntax to create a `reset` function. + +```js +assert.match(code, /const\s+reset\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +Your `reset` function should be empty. + +```js +assert.match(reset.toString(), /\(\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md new file mode 100644 index 00000000000..e12fb12ab81 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md @@ -0,0 +1,366 @@ +--- +id: 64fac365aeb8ad70b69b366f +title: Step 25 +challengeType: 0 +dashedName: step-25 +--- + +# --description-- + +Inside the `reset` function, set each value of `titleInput`, `dateInput`, `descriptionInput` to an empty string. + +Also, use `classList` to toggle the class `hidden` on the `taskForm` and set `currentTask` to an empty object. That's because at this point, `currentTask` will be filled with the task the user might have added. + +# --hints-- + +You should set `titleInput.value` to an empty string. + +```js +assert.match(reset.toString(), /titleInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `dateInput.value` to an empty string. + +```js +assert.match(reset.toString(), /dateInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `descriptionInput.value` to an empty string. + +```js +assert.match(reset.toString(), /descriptionInput\.value\s*=\s*('|")\1;?/) +``` + +You should use `classList` to toggle the class `hidden` on `taskForm` + +```js +assert.match(reset.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +You should set `currentTask` to an empty object. + +```js +assert.match(reset.toString(), /currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- +const reset = () => { + +} +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md new file mode 100644 index 00000000000..5f281d0de16 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md @@ -0,0 +1,351 @@ +--- +id: 64fac4d1773e7a719b1254de +title: Step 26 +challengeType: 0 +dashedName: step-26 +--- + +# --description-- + +Remove the existing code toggling the class of `hidden` on `taskForm` and call the `reset` function instead. This would clear the input fields and also hide the form modal for the user to see the added task. + +# --hints-- + +Yous should remove the code toggling the `hidden` class on `taskForm`. + +```js +const splitter = code.split('') +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /reset\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + +--fcc-editable-region-- + taskForm.classList.toggle("hidden"); +--fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md new file mode 100644 index 00000000000..c1a7b0f8965 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md @@ -0,0 +1,351 @@ +--- +id: 64fac6a497811572b338e5e5 +title: Step 27 +challengeType: 0 +dashedName: step-27 +--- + +# --description-- + +Also, remove the existing code toggling the class `hidden` on `taskForm` inside the `discardBtn` event listener and call the `reset` function instead. That's because when you click the `Discard` button, everything in the input fields should go away. + +# --hints-- + +Yous should remove the code toggling the class `hidden` on `taskForm`. + +```js +const splitter = code.split("confirmCloseDialog.close();") +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /confirmCloseDialog\.close\(\);?\s*reset\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); +--fcc-editable-region-- + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md new file mode 100644 index 00000000000..84005e1b01a --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md @@ -0,0 +1,346 @@ +--- +id: 64faca774fd9fd74bc084cc9 +title: Step 28 +challengeType: 0 +dashedName: step-28 +--- + +# --description-- + +You should display the `Cancel` and `Discard` buttons to the user only if there is some text present in the input fields. + +To begin, within the `closeTaskFormBtn` event listener, create a `formInputsContainValues` variable to check if there is a value in the `titleInput` field **or** the `dateInput` field **or** the `descriptionInput` field. + +# --hints-- + +You should use `const` to create a variable `formInputsContainValues` with the value `titleInput.value || dateInput.value || descriptionInput.value;` + +```js +assert.match(code, /const\s*formInputsContainValues\s*=\s*titleInput\.value\s*\|\|\s*dateInput\.value\s*\|\|\s*descriptionInput\.value;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); + +}); +--fcc-editable-region-- + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md new file mode 100644 index 00000000000..07482464ebf --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md @@ -0,0 +1,362 @@ +--- +id: 64facf6180824876f70a2e86 +title: Step 29 +challengeType: 0 +dashedName: step-29 +--- + +# --description-- + +Create an `if` statement to check if `formInputsContainValues` is true. If `formInputsContainValues` is true, indicating that there are changes, use the `showModal()` method on `confirmCloseDialog`. Otherwise, if there are no changes, call the `reset()` function to clear the input fields and hide the form modal. + +# --hints-- + +You should create an `if` statement with the condition `formInputsContainValues`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{/) +``` + +The `if` block of your `if` statement should contain `confirmCloseDialog.showModal();`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?/) +``` + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{/) +``` + +You should call the `reset()` function in the `else` block of your `if` statement. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{\s*reset\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 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; +--fcc-editable-region-- + confirmCloseDialog.showModal(); +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md new file mode 100644 index 00000000000..95b71fba69f --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md @@ -0,0 +1,372 @@ +--- +id: 64fad07f43a101779cb8692a +title: Step 30 +challengeType: 0 +dashedName: step-30 +--- + +# --description-- + +You can enhance code readability and maintainability by refactoring the `submit` event listener into two separate functions. The first function can be used to add the input values to `taskData`, while the second function can be responsible for adding the tasks to the DOM. + +Use arrow syntax to create an `addOrUpdateTask` function. Then move the `dataArrIndex` variable, the `taskObj` object, and the `if` statement into the `addOrUpdateTask` function. + +# --hints-- + +You should use `const` and arrow syntax to create an `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +You should move the `dataArrIndex` variable into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?/) +``` + +You should move the `taskObj` object into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?/) +``` + +You should move the `if` statement with the condition `dataArrIndex === 1` into your `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?\s*if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md new file mode 100644 index 00000000000..8192699bb61 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md @@ -0,0 +1,358 @@ +--- +id: 64fad9cd2eeb1e7ca2ca8c8b +title: Step 31 +challengeType: 0 +dashedName: step-31 +--- + +# --description-- + +Use arrow syntax to create an `updateTaskContainer` function. Then move the `taskData.forEach()` and its content into it. + +# --hints-- + +You should use `const` and arrow syntax to create a `updateTaskContainer` function. + +```js +assert.match(code, /const\s*updateTaskContainer\s*=\s*\(\)\s*=>\s*\{/) +``` + +You should move `taskData.forEach()` and its content into the `updateTaskContainer()` function. + +```js +assert.match(code, /const\s+updateTaskContainer\s+=\s*\(\)\s*=>\s*\{\s*taskData\.forEach\(\s*\(\{\s*id,\s*title,\s*date,\s*description\s*\}\)\s*=>\s*\(tasksContainer\.innerHTML\s*\+=\s*`\s*\s*

Title:<\/strong>\s*\$\{title\}<\/p>\s*

Date:<\/strong>\s*\$\{date\}<\/p>\s*

Description:<\/strong>\s*\$\{description\}<\/p>\s*Edit<\/button>\s*Delete<\/button>\s*<\/div>\s*`\)\s*\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md new file mode 100644 index 00000000000..18b2a462de4 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md @@ -0,0 +1,360 @@ +--- +id: 64fadae4f2d51b7d5d8b98d8 +title: Step 32 +challengeType: 0 +dashedName: step-32 +--- + +# --description-- + +Inside the `addOrUpdateTask` function, call the `updateTaskContainer` and `reset` functions. + +# --hints-- + +You should call the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\)\s*/) +``` + +You should call the `reset` function after calling the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\);?\s*reset\(\);?\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + reset() +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md new file mode 100644 index 00000000000..657bb4aa05b --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md @@ -0,0 +1,355 @@ +--- +id: 64fadff23375f27ff06c6d40 +title: Step 33 +challengeType: 0 +dashedName: step-33 +--- + +# --description-- + +Now remove the `reset()` call inside the `taskForm` submit event listener and call the `addOrUpdateTask` function instead. + +# --hints-- + +You should remove `reset()` and call `addOrUpdateTask()`. + +```js +assert.match(code, /addOrUpdateTask\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + --fcc-editable-region-- + reset() + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md new file mode 100644 index 00000000000..bd759fb7e5f --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md @@ -0,0 +1,364 @@ +--- +id: 64fae068bcdc9c805bd8399e +title: Step 35 +challengeType: 0 +dashedName: step-35 +--- + +# --description-- + +To enable editing and deleting for each task, add an `onclick` attribute to both buttons. Set the value of the `onclick` attribute to `editTask(this)` for the `Edit` button and `deleteTask(this)` for the `Delete` button. The `editTask(this)` function will handle editing, while the `deleteTask(this)` function will handle deletion. + +`this` is a keyword that refers to the current context. In this case, `this` points to the element that triggers the event – the buttons. + +# --hints-- + +You should add `onclick="editTask(this)"` as the first attribute of the edit button. + +```js +assert.match(code, /Edit<\/button>/) +``` + +You should add `onclick="deleteTask(this)"` as the first attribute of the delete button. + +```js +assert.match(code, /Delete<\/button>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + + --fcc-editable-region-- +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md new file mode 100644 index 00000000000..c6fac350818 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md @@ -0,0 +1,371 @@ +--- +id: 64faedcd16a1e985c4c2dc94 +title: Step 36 +challengeType: 0 +dashedName: step-36 +--- + +# --description-- + +Create a `deleteTask` function using arrow syntax. Pass `buttonEl` as the parameter and define an empty set of curly braces for the function body. + +# --hints-- + +You should use `const` and arrow syntax to create a `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `deleteTask` function should take a `buttonEl` parameter. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `deleteTask` function should be empty. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md new file mode 100644 index 00000000000..27631c77f2f --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md @@ -0,0 +1,359 @@ +--- +id: 64faf0418e828c0114a558a7 +title: Step 34 +challengeType: 0 +dashedName: step-34 +--- + +# --description-- + +There's a problem. If you add a task, and then add another, the previous task gets duplicated. This means you need to clear out the existing contents of `tasksContainer` before adding a new task. + +Set the `innerHTML` of `taskContainer` back to an empty string. + + +# --hints-- + +You should set the `innerHTML` of `tasksContainer` to an empty string. + +```js +assert.match(code, /tasksContainer\.innerHTML\s*=\s*("|')\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + --fcc-editable-region-- + + --fcc-editable-region-- + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md new file mode 100644 index 00000000000..9cdbd7860ca --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md @@ -0,0 +1,382 @@ +--- +id: 64faf65b22ad8d07df9be14d +title: Step 37 +challengeType: 0 +dashedName: step-37 +--- + +# --description-- + +You need to find the index of the task you want to delete first. + +Create a `dataArrIndex` variable and set its value using the `findIndex()` method on the `taskData` array. Pass `item` as the parameter for the arrow callback function, and within the callback, check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. Don't use curly braces. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?\s*item\s*\)?\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\s*\);?\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md new file mode 100644 index 00000000000..0e54adadf06 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md @@ -0,0 +1,398 @@ +--- +id: 64faf874364ec308f875f636 +title: Step 38 +challengeType: 0 +dashedName: step-38 +--- + +# --description-- + +You need to remove the task from the DOM using `remove()` and from the `taskData` array using `splice()`. + +`splice()` is an array method that modifies arrays by removing, replacing, or adding elements at a specified index, while also returning the removed elements. It can take up to three parameters: the first one is the mandatory index at which to start, the second is the number of items to remove, and the third is an optional replacement element. Here's an example: + +```js +const fruits = ["mango", "date", "cherry", "banana", "apple"]; + +// Remove date and cherry from the array starting at index 1 +const removedFruits = fruits.splice(1, 2); + +console.log(fruits); // [ 'mango', 'banana', 'apple' ] +console.log(removedFruits); // [ 'date', 'cherry' ] +``` + +Use the `remove()` method to remove the `parentElement` of the `buttonEl` from the DOM. Then use `splice()` to remove the task from the `taskData` array. Pass in `dataArrIndex` and `1` as the parameters of your `splice()`. + +`dataArrIndex` is the index to start and `1` is the number of items to remove. + +# --hints-- + +You should use the `remove()` method to remove the parent element of `buttonEl`. + +```js +assert.match(deleteTask.toString(), /buttonEl\.parentElement\.remove\(\);?/) +``` + +You should use `splice()` on the `taskData` array. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(/) +``` + +The first parameter of your `splice()` method should be `dataArrIndex`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex/) +``` + +The second parameter of your `splice()` method should be `1`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex,\s*1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md new file mode 100644 index 00000000000..3f379d51764 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md @@ -0,0 +1,381 @@ +--- +id: 64fafac95328110a69bcb75f +title: Step 39 +challengeType: 0 +dashedName: step-39 +--- + +# --description-- + +Use arrow syntax to create an `editTask` function. Pass in `buttonEl` as the parameter and add empty curly braces for the body. + +# --hints-- + +You should use `const` and arrow syntax to create an `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `editTask` function should take a `buttonEl` parameter. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `editTask` function should be empty. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md new file mode 100644 index 00000000000..bc0e2a84503 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md @@ -0,0 +1,391 @@ +--- +id: 64fb0fa0968f2b113b2d90e9 +title: Step 40 +challengeType: 0 +dashedName: step-40 +--- + +# --description-- + +As you did in the `deleteTask` function, you need to find the index of the task to be edited. + +Create a `dataArrIndex` variable. For its value, utilize the `findIndex()` method on `taskData`. Pass `item` as the parameter to its callback function and check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md new file mode 100644 index 00000000000..14d527ecf40 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md @@ -0,0 +1,373 @@ +--- +id: 64fb1061ca838611ed6a7d6b +title: Step 41 +challengeType: 0 +dashedName: step-41 +--- + +# --description-- + +Use square bracket notation to retrieve the task to be edited from the `taskData` array using the `dataArrIndex`. Then, assign it to the `currentTask` object to keep track of it. + +# --hints-- + +You should assign `taskData[dataArrIndex]` to `currentTask`. + +```js +assert.match(code, /currentTask\s*=\s*taskData\[dataArrIndex\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex((item) => item.id === buttonEl.parentElement.id); + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md new file mode 100644 index 00000000000..73ef19befcc --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md @@ -0,0 +1,389 @@ +--- +id: 64fb1321e189a6136d200f77 +title: Step 42 +challengeType: 0 +dashedName: step-42 +--- + +# --description-- + +The task to be edited is now in the `currentTask` object. Stage it for editing inside the input fields by setting the value of `titleInput` to `currentTask.title`, `dateInput` to `currentTask.date`, and `descriptionInput` to `currentTask.description`. + +# --hints-- + +You should set `titleInput.value` to `currentTask.title`. + +```js +assert.match(editTask.toString(), /titleInput\.value\s*=\s*currentTask\.title;?/) +``` + +You should set `dateInput.value` to `currentTask.date`. + +```js +assert.match(editTask.toString(), /dateInput\.value\s*=\s*currentTask\.date;?/) +``` + +You should set `descriptionInput.value` to `currentTask.description`. + +```js +assert.match(editTask.toString(), /descriptionInput\.value\s*=\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + currentTask = taskData[dataArrIndex]; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md new file mode 100644 index 00000000000..c6312bf6bd0 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md @@ -0,0 +1,386 @@ +--- +id: 64fb1436adef3e145b4c3501 +title: Step 43 +challengeType: 0 +dashedName: step-43 +--- + +# --description-- + +Set the `innerText` of the `addOrUpdateTaskBtn` button to `Update Task` and its `aria-label` to `Update Task` as well. + +# --hints-- + +You should set the inner text of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.innerText\s*=\s*("|')Update Task\1;?/) +``` + +You should set the `ariaLabel` of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.ariaLabel\s*=\s*("|')Update Task\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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; + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md new file mode 100644 index 00000000000..101870d098a --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md @@ -0,0 +1,384 @@ +--- +id: 64fb14d890415c14f93069ce +title: Step 44 +challengeType: 0 +dashedName: step-44 +--- + +# --description-- + +Finally, display the `form` modal with the values of the input fields by using `classList` to toggle the `hidden` class on `taskForm`. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(editTask.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md new file mode 100644 index 00000000000..7ad62060776 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md @@ -0,0 +1,391 @@ +--- +id: 64fb154a7c48cd159924bb18 +title: Step 45 +challengeType: 0 +dashedName: step-45 +--- + +# --description-- + +At this point, editing a task won't reflect when you submit the task. To make the editing functional, go back to the `if` statement inside the `addOrUpdateTask` function. Create an `else` block and set `taskData[dataArrIndex]` to `taskObj`. + +# --hints-- + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*/) +``` + +Your `else` block should have the code `taskData[dataArrIndex] = taskObj`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*taskData\[dataArrIndex\]\s*=\s*taskObj;?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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, + }; + + --fcc-editable-region-- + if (dataArrIndex === -1) { + taskData.unshift(taskObj); + } + + --fcc-editable-region-- + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md new file mode 100644 index 00000000000..4ec33172878 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md @@ -0,0 +1,401 @@ +--- +id: 64fb1c4dc0feb219149a7c7d +title: Step 46 +challengeType: 0 +dashedName: step-46 +--- + +# --description-- + +If the user attempts to edit a task but decides not to make any changes before closing the form, there is no need to display the modal with the `Cancel` and `Discard` buttons. + +Inside the `closeTaskFormBtn` event listener, use `const` to create another variable named `formInputValuesUpdated`. Check if the user made changes while trying to edit a task by verifying that the `titleInput` value **is not equal to** `currentTask.title`, the `dateInput` value **is not equal to** `currentTask.date`, and the `descriptionInput` value **is not equal to** `currentTask.description`. + +# --hints-- + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*/) +``` + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title` or `dateInput.value` is not equal to `currentTask.date`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date/) +``` + +Your `formInputValuesUpdated` variable should have the value `titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date\s*\|\|\s*descriptionInput\.value\s*!==\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + --fcc-editable-region-- + + --fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md new file mode 100644 index 00000000000..bb8a87acf60 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md @@ -0,0 +1,390 @@ +--- +id: 64fb285637fa1e1c222033e3 +title: Step 47 +challengeType: 0 +dashedName: step-47 +--- + +# --description-- + +Now add `formInputValuesUpdated` as the second mandatory condition in the `if` statement using the `AND` operator. + +This way, the `Cancel` and `Discard` buttons in the modal won't be displayed to the user if they haven't made any changes to the input fields while attempting to edit a task. + +# --hints-- + +Your `if` should have the condition `formInputsContainValues && formInputValuesUpdated`. + +```js +assert.match(code.split("if (formInputsContainValues"), /\s*&&\s*formInputValuesUpdated/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description; + +--fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md new file mode 100644 index 00000000000..94fdc40185a --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md @@ -0,0 +1,414 @@ +--- +id: 64fb29348a60361ccd45c1e2 +title: Step 48 +challengeType: 0 +dashedName: step-48 +--- + +# --description-- + +`localStorage` offers methods for saving, retrieving, and deleting items. The items you save can be of any JavaScript data type. + +For instance, the `setItem()` method is used to save an item, and the `getItem()` method retrieves the item. To delete a specific item, you can utilize the `removeItem()` method, or if you want to delete all items in the storage, you can use `clear()`. + +Here's how you can save an item: + +```js +localStorage.setItem("key", value); // value could be string, number, or any other data type +``` + +A `myTaskArr` array has been provided for you. Use the `setItem()` method to save it with a key of `data`. + +After that, open your browser console and go to the `Applications` tab, select `Local Storage`, and the freeCodeCamp domain you see. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +Your `localStorage.setItem()` should have a key of `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*myTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md new file mode 100644 index 00000000000..2ce0b111937 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md @@ -0,0 +1,404 @@ +--- +id: 64fefebad99209211ec30537 +title: Step 49 +challengeType: 0 +dashedName: step-49 +--- + +# --description-- + +If you check the `Application` tab of your browser console, you'll notice a series of `[object Object]`. This is because everything you save in `localStorage` needs to be in string format. + +To resolve the issue, wrap the data you're saving in the `JSON.stringify()` method. Then, check local storage again to observe the results. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +You should wrap `JSON.stringify()` around `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*JSON\.stringify\(myTaskArr\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- +localStorage.setItem("data", myTaskArr); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md new file mode 100644 index 00000000000..1cb0c0c5930 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md @@ -0,0 +1,406 @@ +--- +id: 64ff0313700dad264d19dfe4 +title: Step 50 +challengeType: 0 +dashedName: step-50 +--- + +# --description-- + +Now that you have the `myTaskArr` array saved in `localStorage` correctly, you can retrieve it with `getItem()` by specifying the key you used to save the item. + +Use the `getItem()` method to retrieve the `myTaskArr` array and assign it to the variable `getTaskArr`. Then, log the `getTaskArr` variable to the console to see the result. + +# --hints-- + +You should use `const` to create a `getTaskArr` variable and assign `localStorage.getItem("data")` to it. + +```js +assert.match(code, /const\s*getTaskArr\s*=\s*localStorage\.getItem\(('|")data\1\);?/) +``` + +You should log the `getTaskArr` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md new file mode 100644 index 00000000000..608713897b5 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md @@ -0,0 +1,411 @@ +--- +id: 64ff04cc33779427a6412449 +title: Step 51 +challengeType: 0 +dashedName: step-51 +--- + +# --description-- + +The item you retrieve is a string, as you saved it with `JSON.stringify()`. To view it in its original form before saving, you need to use `JSON.parse()`. + +Use `getItem()` to retrieve the `myTaskArr` array again. This time, wrap it inside `JSON.parse()`, assign it to the variable `getTaskArrObj` and log the `getTaskArrObj` to the console. + +Check the console to see the difference between `getTaskArr` and `getTaskObj`. + +# --hints-- + +You should use `const` to create a `getTaskArrObj` variable and assign it to `JSON.parse(localStorage.getItem('data'));`. + +```js +assert.match(code, /const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\(('|")data\1\)\);?/) +``` + +You should log the `getTaskArrObj` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md new file mode 100644 index 00000000000..09bad9433fa --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md @@ -0,0 +1,406 @@ +--- +id: 64ff068e0426eb288874ed79 +title: Step 52 +challengeType: 0 +dashedName: step-52 +--- + +# --description-- + +You can use `localStorage.removeItem()` to remove a specific item and `localStorage.clear()` to clear all items in the local storage. + +Remove the `data` item from local storage and open the console to observe the result. You should see `null`. + +# --hints-- + +You should use `localStorage.removeItem()` to remove the `data` item from the browser's local storage. + +```js +assert.match(code, /localStorage\.removeItem\(('|")data\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md new file mode 100644 index 00000000000..5dc3bd6e1d1 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md @@ -0,0 +1,413 @@ +--- +id: 64ff23daf176a92de95f24dc +title: Step 53 +challengeType: 0 +dashedName: step-53 +--- + +# --description-- + +Using `localStorage.clear()` won't just delete a single item from local storage but will remove all items. + +Remove `localStorage.removeItem()` and use `localStorage.clear()` instead. You don't need to pass in anything. You should also see `null` in the console. + +# --hints-- + +You should remove `localStorage.removeItem("data")`. + +```js +assert.notMatch(code, /localStorage\.removeItem\(('|")data\1\);/) +``` + +You should remove everything from the browser `local storage` with `localStorage.clear()`. + +```js +assert.match(code, /localStorage\.clear\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- +localStorage.removeItem("data"); + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md new file mode 100644 index 00000000000..0d6c42c989e --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md @@ -0,0 +1,404 @@ +--- +id: 64ff24b80431f62ec6b93f65 +title: Step 54 +challengeType: 0 +dashedName: step-54 +--- + +# --description-- + +Remove the `myTaskArr` array and all of the code for `localStorage` because you don't need them anymore. + +# --hints-- + +You should remove `myTaskArr` and all the code related to `localStorage` that you've just learned. + +```js +assert.notMatch(code, /const\s*myTaskArr\s*=\s*\[\s*\{\s*task:\s"Walk\s*the\s*Dog",\s*date:\s*"22-04-2022"\s*\},\s*\{\s*task:\s"Read\s*some\s*books",\s*date:\s*"02-11-2023"\s*\},\s*\{\s*task:\s"Watch\s*football",\s*date:\s*"10-08-2021"\s*\},\s*\];?\s*localStorage\.setItem\("data", JSON\.stringify\(myTaskArr\)\);\s*localStorage\.clear\(\);?\s*const\s*getTaskArr\s*=\s*localStorage\.getItem\("data"\)\s*console\.log\(getTaskArr\)\s*const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\("data"\)\);?\s*console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +--fcc-editable-region-- +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +localStorage.clear(); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md new file mode 100644 index 00000000000..cbbcd4df6e1 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md @@ -0,0 +1,404 @@ +--- +id: 65003986d17d1e1865b269c0 +title: Step 55 +challengeType: 0 +dashedName: step-55 +--- + +# --description-- + +Now you should save the task items to local storage when the user adds, updates, or removes a task. + +Inside the `addOrUpdateTask` function, use `setItem()` to save the tasks with a key of `data`, then pass the `taskData` array as its parameter. Ensure that you stringify the `taskData`. + +This would persist data once the user adds or updates tasks. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +assert.match(code, /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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; + } + --fcc-editable-region-- + + --fcc-editable-region-- + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md new file mode 100644 index 00000000000..ec3a700096a --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md @@ -0,0 +1,407 @@ +--- +id: 650046832f92c01a35834bca +title: Step 56 +challengeType: 0 +dashedName: step-56 +--- + +# --description-- + +You also want a deleted task to be removed from local storage. For this, you don't need the `removeItem()` or `clear()` methods. Since you already use `splice()` to remove the deleted task from `taskData`, all you need to do now is save `taskData` to local storage again. + +Use `setItem()` to save the `taskData` array again. Pass in `data` as the key and ensure that `taskData` is stringified before saving. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); + --fcc-editable-region-- + + --fcc-editable-region-- +} + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md new file mode 100644 index 00000000000..40cf6384c4e --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md @@ -0,0 +1,392 @@ +--- +id: 650048b0764f9c1b798200e2 +title: Step 57 +challengeType: 0 +dashedName: step-57 +--- + +# --description-- + +If you add, update, or remove a task, it should reflect in the UI. However, that's not happening now because you have yet to retrieve the tasks. To do this, you need to modify your initial `taskData` to be an empty array. + +Set `taskData` to the retrieval of `data` from local storage **or** an empty array. Make sure you parse the data coming with `JSON.parse()` because you saved it as a string. + +# --hints-- + +You should set the `taskData` variable to `JSON.parse(localStorage.getItem("data")) || [];`. + +```js +assert.match(code, /const\s*taskData\s*=\s*JSON.parse\(localStorage\.getItem\(('|")data\1\)\)\s*\|\|\s*\[\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- +const taskData = []; +--fcc-editable-region-- +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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md new file mode 100644 index 00000000000..08ced41b63f --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md @@ -0,0 +1,776 @@ +--- +id: 65004ba581d03d1d5628b41c +title: Step 58 +challengeType: 0 +dashedName: step-58 +--- + +# --description-- + +You've retrieved the task item(s) now, but they still don't reflect in the UI when the page loads. However, they appear when you add a new task. + +To fix this issue, you can check if there's a task inside `taskData`, and then call the `updateTaskContainer()`. + +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 the condition `taskData.length`. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*/) +``` + +You should call the `updateTaskContainer` function in your `if` statement. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*updateTaskContainer\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + taskForm.classList.toggle("hidden"); +} + +const reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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 + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md new file mode 100644 index 00000000000..6b21e6888ce --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md @@ -0,0 +1,314 @@ +--- +id: 650300a25b6f72964ab8aca6 +title: Step 13 +challengeType: 0 +dashedName: step-13 +--- + +# --description-- + +To make the `id` more unique, add another hyphen and use `Date.now()`. + +# --hints-- + +You should attach `-${Date.now()}` to the existing value of the `id` key. Don't forget it needs to be inside the template string. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + const taskObj = { + id: `${titleInput.value.toLowerCase().split(' ').join('-')}`, + }; + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md new file mode 100644 index 00000000000..0c9fbc152b7 --- /dev/null +++ b/curriculum/challenges/italian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md @@ -0,0 +1,337 @@ +--- +id: 65099dbd8f137d58e5c0ff16 +title: Step 21 +challengeType: 0 +dashedName: step-21 +--- + +# --description-- + +Create one more `p` element and interpolate the `description` you destructured as the text. Also, create a `strong` element inside the paragraph with the text `Description:`. + +# --hints-- + +You should create a `p` element with `${description}` as the text. + +```js +assert.match(code, /

.*\$\{description\}<\/p>/) +``` + +You should create a `strong` element with the text `Description:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Description:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/italian/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md b/curriculum/challenges/italian/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md index e7b5887059a..c23a552a1c5 100644 --- a/curriculum/challenges/italian/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md +++ b/curriculum/challenges/italian/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md @@ -12,7 +12,7 @@ Questo video ti aiuterà a capire le equazioni dell'interesse semplice e compost Ecco il notebook Colab per questo video. -Ecco un notebook Colab aggiuntivo che mostra un modo per inserire le formule di interessi e pagamenti in funzioni di Python. Inoltre, vedrai anche un esempio di come usare alcune formule per generare risultati, individuare una tendenza e proseguire con altre formule per analizzare modelli. +Here is an additional Colab notebook that shows you one way to put many of these interest and payment formulas into Python functions. Also you will see an example of using some formulas to output results, notice a trend, and follow up with other formulas to analyze patterns. # --question-- diff --git a/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md b/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md index a32ba3e5a4a..2ad89798285 100644 --- a/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md +++ b/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md @@ -71,8 +71,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md b/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md index 5cd47851b2a..85e56755486 100644 --- a/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md +++ b/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md @@ -73,7 +73,7 @@ substringDivisibility(5); ```js function substringDivisibility(n) { - function isSubDivisable(digits) { + function isSubDivisible(digits) { const factors = [2, 3, 5, 7, 11, 13, 17]; for (let i = 1; i < digits.length - 2; i++) { @@ -108,17 +108,17 @@ function substringDivisibility(n) { } const allowedDigits = [...new Array(n + 1).keys()]; - const divisablePandigitals = []; + const divisiblePandigitals = []; heapsPermutations( allowedDigits.length, allowedDigits, - isSubDivisable, - divisablePandigitals + isSubDivisible, + divisiblePandigitals ); let sum = 0; - for (let i = 0; i < divisablePandigitals.length; i++) { - sum += divisablePandigitals[i]; + for (let i = 0; i < divisiblePandigitals.length; i++) { + sum += divisiblePandigitals[i]; } return sum; diff --git a/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md b/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md index bcab38f4885..8e1f0c577d2 100644 --- a/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md +++ b/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md @@ -67,8 +67,8 @@ class PrimeSeive { const prime = 2 * i + 3; primes.push(prime); // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } @@ -101,10 +101,10 @@ class PrimeSeive { }; function consecutivePrimeSum(limit) { - // Initalize seive + // Initialize seive const primeSeive = new PrimeSeive(limit); - // Initalize for longest sum < 100 + // Initialize for longest sum < 100 let bestPrime = 41; let bestI = 0; let bestJ = 5; diff --git a/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md b/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md index 2267e786ad5..7e65bb8d6de 100644 --- a/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md +++ b/curriculum/challenges/italian/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/italian/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md b/curriculum/challenges/italian/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md index 5302295f689..01c38f83229 100644 --- a/curriculum/challenges/italian/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md +++ b/curriculum/challenges/italian/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md @@ -31,10 +31,10 @@ Per $1 ≤ k ≤ 200$, trova $\sum{m(k)}$. # --hints-- -`efficientExponentation()` dovrebbe restituire `1582`. +`efficientExponentiation()` should return `1582`. ```js -assert.strictEqual(efficientExponentation(), 1582); +assert.strictEqual(efficientExponentiation(), 1582); ``` # --seed-- @@ -42,12 +42,12 @@ assert.strictEqual(efficientExponentation(), 1582); ## --seed-contents-- ```js -function efficientExponentation() { +function efficientExponentiation() { return true; } -efficientExponentation(); +efficientExponentiation(); ``` # --solutions-- diff --git a/curriculum/challenges/italian/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md b/curriculum/challenges/italian/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md index 83858a645f2..ad6362154e6 100644 --- a/curriculum/challenges/italian/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md +++ b/curriculum/challenges/italian/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/italian/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md b/curriculum/challenges/italian/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md index 33371151f91..80917c7889e 100644 --- a/curriculum/challenges/italian/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md +++ b/curriculum/challenges/italian/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md @@ -35,7 +35,7 @@ concealedSquare(); ```js // Check if n**2 matches the pattern -function squareMatchs(n) { +function squareMatches(n) { // Need BigInt due to size of values let nSquared = (BigInt(n) * BigInt(n)).toString(); @@ -55,8 +55,8 @@ function concealedSquare() { for (let x = maxSquareRoot; x >= minSquareRoot; x -= 10) { // Note: 3*3 = 9 and 7*7 = 49 are only trailing digits // that can produce 9 as trailing digit in square - if (squareMatchs(x + 3)) return (x + 3)*10; - if (squareMatchs(x + 7)) return (x + 7)*10; + if (squareMatches(x + 3)) return (x + 3)*10; + if (squareMatches(x + 7)) return (x + 7)*10; } return -1; } diff --git a/curriculum/challenges/japanese/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md b/curriculum/challenges/japanese/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md index 40db4d41e6b..a22200aef9e 100644 --- a/curriculum/challenges/japanese/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md +++ b/curriculum/challenges/japanese/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md @@ -54,7 +54,24 @@ assert.match(code, /myStorage\.car\.inside/); `gloveBoxContents` should still be declared with `const`. ```js -assert.match(code, /const\s+gloveBoxContents\s*=\s*myStorage\.car\.inside\[\s*("|')glove box\1\s*\]|const\s*{\s*('|")glove box\2:\s*gloveBoxContents\s*}\s*=\s*myStorage\.car\.inside;/); +assert.match(code, /const\s+gloveBoxContents\s*=/); +``` + +You should not change the `myStorage` object. + +```js +const expectedMyStorage = { + "car":{ + "inside":{ + "glove box":"maps", + "passenger seat":"crumbs" + }, + "outside":{ + "trunk":"jack" + } + } +}; +assert.deepStrictEqual(myStorage, expectedMyStorage); ``` # --seed-- diff --git a/curriculum/challenges/japanese/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md b/curriculum/challenges/japanese/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md index c3287f53f8a..cdc5732e21d 100644 --- a/curriculum/challenges/japanese/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md +++ b/curriculum/challenges/japanese/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md @@ -10,7 +10,7 @@ dashedName: catch-misspelled-variable-and-function-names `console.log()` と `typeof` メソッドの 2 つは、中間値やプログラム出力の型を確認する場合によく使用されます。 よくあるバグの原因として、 スペルミスがあります。タイピングの速い人が起こしがちな構文レベルの問題の 1 つです。 -変数名や関数名の文字が、入れ替わっていたり、不足していたり、大文字小文字が間違っていたりすると、ブラウザーで存在しないオブジェクトが検索され、結果として参照エラーという形でエラーが表示されます。 JavaScript の変数名や関数名は大文字と小文字が区別されます。 +Transposed, missing, or miscapitalized characters in a variable or function name will have the browser looking for an object that doesn't exist - and complain in the form of a reference error. JavaScript の変数名や関数名は大文字と小文字が区別されます。 # --instructions-- diff --git a/curriculum/challenges/japanese/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md b/curriculum/challenges/japanese/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md index 2c312d901a0..89f62fbe6ae 100644 --- a/curriculum/challenges/japanese/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md +++ b/curriculum/challenges/japanese/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md @@ -58,8 +58,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/japanese/06-quality-assurance/quality-assurance-projects/issue-tracker.md b/curriculum/challenges/japanese/06-quality-assurance/quality-assurance-projects/issue-tracker.md index ca78119330e..407adbb970b 100644 --- a/curriculum/challenges/japanese/06-quality-assurance/quality-assurance-projects/issue-tracker.md +++ b/curriculum/challenges/japanese/06-quality-assurance/quality-assurance-projects/issue-tracker.md @@ -231,13 +231,13 @@ async (getUserInput) => { }; const url = getUserInput('url') + '/api/issues/fcc-project'; const itemToUpdate = await $.post(url, initialData); - const updateSucccess = await $.ajax({ + const updateSuccess = await $.ajax({ url: url, type: 'PUT', data: { _id: itemToUpdate._id, issue_text: 'New Issue Text' } }); - assert.isObject(updateSucccess); - assert.deepEqual(updateSucccess, { + assert.isObject(updateSuccess); + assert.deepEqual(updateSuccess, { result: 'successfully updated', _id: itemToUpdate._id }); diff --git a/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/24-game.md b/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/24-game.md index 37826769391..e75dbf3acf7 100644 --- a/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/24-game.md +++ b/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/24-game.md @@ -82,7 +82,7 @@ const OPERATORS_ = { "/": (a, b) => a / b, } -const PRECIDENCE_ = { +const PRECEDENCE_ = { "+": 1, "-": 1, "*": 2, @@ -114,7 +114,7 @@ function evaluate_(expression) { case "*": case "/": while (stack.length && - PRECIDENCE_[c] <= PRECIDENCE_[stack[stack.length-1]]) { + PRECEDENCE_[c] <= PRECEDENCE_[stack[stack.length-1]]) { postfix += stack.pop(); } stack.push(c); diff --git a/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md b/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md index 9433bd3a11f..e8feefb3938 100644 --- a/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md +++ b/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md @@ -175,7 +175,7 @@ function dealFreeCell(seed) { const rng = FreeCellRNG(seed); const deck = getDeck(); - const deltCards = [[], [], [], [], [], [], []]; + const dealtCards = [[], [], [], [], [], [], []]; let currentColumn = 0; let currentRow = 0; @@ -195,7 +195,7 @@ function dealFreeCell(seed) { card = deck.pop(); // Deal this card - deltCards[currentRow].push(card); + dealtCards[currentRow].push(card); currentColumn += 1; if (currentColumn === 8) { currentColumn = 0; @@ -203,6 +203,6 @@ function dealFreeCell(seed) { } } - return deltCards; + return dealtCards; } ``` diff --git a/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/department-numbers.md b/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/department-numbers.md index 802ab0fbfa0..08ed1e73889 100644 --- a/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/department-numbers.md +++ b/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/department-numbers.md @@ -101,7 +101,7 @@ function combinations(possibleNumbers, total) { function combinations(possibleNumbers, total) { let firstNumber; let secondNumber; - let thridNumber; + let thirdNumber; const allCombinations = []; for (let i = 0; i < possibleNumbers.length; i += 1) { @@ -112,10 +112,10 @@ function combinations(possibleNumbers, total) { secondNumber = possibleNumbers[j]; if (j !== i && firstNumber + secondNumber <= total) { - thridNumber = total - firstNumber - secondNumber; + thirdNumber = total - firstNumber - secondNumber; - if (thridNumber !== firstNumber && thridNumber !== secondNumber && possibleNumbers.includes(thridNumber)) { - allCombinations.push([firstNumber, secondNumber, thridNumber]); + if (thirdNumber !== firstNumber && thirdNumber !== secondNumber && possibleNumbers.includes(thirdNumber)) { + allCombinations.push([firstNumber, secondNumber, thirdNumber]); } } } diff --git a/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/lu-decomposition.md b/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/lu-decomposition.md index dcb858fc81c..7543099186e 100644 --- a/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/lu-decomposition.md +++ b/curriculum/challenges/japanese/10-coding-interview-prep/rosetta-code/lu-decomposition.md @@ -80,7 +80,7 @@ $PA = LU$ # --instructions-- -タスクは、nxn の正方行列 $A$ を取り、下三角行列 $L$、上三角行列 $U$、および置換行列 $P$ を返すルーチンを実装して、上記の式が満たされるようにすることです。 戻り値は、 `[L, U, P]` の形式でなければなりません。 +The task is to implement a routine which will take a square nxn matrix $A$ and return a lower triangular matrix $L$, a upper triangular matrix $U$ and a permutation matrix $P$, so that the above equation is fulfilled. 戻り値は、 `[L, U, P]` の形式でなければなりません。 # --hints-- diff --git a/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md index 098db8a6e98..c8078873dcf 100644 --- a/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md +++ b/curriculum/challenges/japanese/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md @@ -9,7 +9,7 @@ dashedName: step-82 ペンギンのくちばしと足には同じ `color` が割り当てられています。 -新たに `--penguin-picorna` という名前のカスタム CSS 変数を作成し、上記の色に関係するプロパティの値をすべて変数に置き換えてください。 +Create a new custom CSS variable named `--penguin-picorna`, and replace all relevant property values with it. # --hints-- diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md index c335f2d7bc9..a1c1c26f120 100644 --- a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md @@ -11,7 +11,7 @@ Inside `pick`, use `let` to initialize a variable named `numbers` and set it to # --hints-- -Your `pick` function should initalize `numbers` to an empty array `[]`. +Your `pick` function should initialize `numbers` to an empty array `[]`. ```js assert.match(pick.toString(), /numbers\s*=\s*\[\]/); diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md new file mode 100644 index 00000000000..4b042aa87ba --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md @@ -0,0 +1,310 @@ +--- +id: 64e4e4c4ec263b62ae7bf54d +title: Step 1 +challengeType: 0 +dashedName: step-1 +--- + +# --description-- + +In this project, you will learn how `localStorage` works in JavaScript by building a Todo app. LocalStorage is a web storage feature of JavaScript that lets you persist data by storing the data as a **key:value** pair. + +The HTML and CSS for this project have been provided for you. Take a look at the files to get yourself familiarized with them. + +Begin by accessing the `task-form`, `confirm-close-dialog`, and `open-task-form-btn` elements with the `getElementById()` method. Save them in the variables `taskForm`, `confirmCloseDialog`, and `openTaskFormBtn`. + +# --hints-- + +You should use `getElementById()` to access the `task-form` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should assign the `task-form` element to the variable `taskForm`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+taskForm\s*=\s*document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should use `getElementById()` to access the `confirm-close-dialog` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should assign the `confirm-close-dialog` element to the variable `confirmCloseDialog`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+confirmCloseDialog\s*=\s*document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should use `getElementById()` to access the `open-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +You should assign the `open-task-form-btn` element to the variable `openTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+openTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md new file mode 100644 index 00000000000..32310409728 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md @@ -0,0 +1,310 @@ +--- +id: 64e4e6c86954de67a3e44ee3 +title: Step 2 +challengeType: 0 +dashedName: step-2 +--- + +# --description-- + +You need to access more elements with the `getElementById()` method. This time you need the `close-task-form-btn`, `add-or-update-task-btn`, and `cancel-btn` elements. Save them in the variables `closeTaskFormBtn`, `addOrUpdateTaskBtn`, and `cancelBtn`. + +# --hints-- + +You should use `getElementById()` to access the `close-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should assign the `close-task-form-btn` element to the variable `closeTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+closeTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `add-or-update-task-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should assign the `add-or-update-task-btn` element to the variable `addOrUpdateTaskBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+addOrUpdateTaskBtn\s*=\s*document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `cancel-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +You should assign the `cancel-btn` element to the variable `cancelBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+cancelBtn\s*=\s*document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md new file mode 100644 index 00000000000..17fc6b76517 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md @@ -0,0 +1,313 @@ +--- +id: 64e4e6fe78b5aa67ef2fc3e7 +title: Step 3 +challengeType: 0 +dashedName: step-3 +--- + +# --description-- + +Next, access the `discard-btn`, `tasks-container`, and `title-input` elements using the `getElementById()` method. Save them in variables named `discardBtn`, `tasksContainer`, and `titleInput`, respectively. + +# --hints-- + +You should use `getElementById()` to access the `discard-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should assign the `discard-btn` element to the variable `discardBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+discardBtn\s*=\s*document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `tasks-container` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should assign the `tasks-container` element to the variable `tasksContainer`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+tasksContainer\s*=\s*document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should use `getElementById()` to access the `title-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +You should assign the `title-input` element to the variable `titleInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+titleInput\s*=\s*document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md new file mode 100644 index 00000000000..6e543d36acc --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md @@ -0,0 +1,304 @@ +--- +id: 64e4e7241f52bb682eeb8211 +title: Step 4 +challengeType: 0 +dashedName: step-4 +--- + +# --description-- + +The last set of elements you need to get from the HTML file are the `date-input` and `description-input` elements. Save them in the variables `dateInput` and `descriptionInput` respectively. + +# --hints-- + +You should use `getElementById()` to access the `date-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should assign the `date-input` element to the variable `dateInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+dateInput\s*=\s*document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should use `getElementById()` to access the `description-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +You should assign the `description-input` element to the variable `descriptionInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+descriptionInput\s*=\s*document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md new file mode 100644 index 00000000000..6f0cd6e9fc4 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md @@ -0,0 +1,296 @@ +--- +id: 64e4e74d0fb4f0687bf4145d +title: Step 5 +challengeType: 0 +dashedName: step-5 +--- + +# --description-- + +Create a `taskData` constant and set it to an empty array. This array will store all the tasks along with their associated data, including title, due date, and description. This storage will enable you to keep track of tasks, display them on the page, and save them to `localStorage`. + +Use `let` to create a `currentTask` variable and set it to an empty object. This variable will be used to track the state when editing and discarding tasks. + +# --hints-- + +You should create a `taskData` constant set to an empty array. + +```js +assert.match(code, /const\s+taskData\s*=\s*\[\];?/) +``` + +You should use `let` to create an empty `currentTask` object. + +```js +assert.match(code, /let\s+currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md new file mode 100644 index 00000000000..7eba2896bf4 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md @@ -0,0 +1,307 @@ +--- +id: 64e4e78a7ea4a168de4e6a38 +title: Step 6 +challengeType: 0 +dashedName: step-6 +--- + +# --description-- + +Now, you will work on opening and closing the form modal. + +Add a `click` event listener to the `openTaskFormBtn` variable, and then use `classList` to toggle the `hidden` class on the `taskForm` element. + +Now you can click on the "Add new Task" button and see the form modal. + +# --hints-- + +You should call the `addEventListener()` method on your `openTaskFormBtn` variable. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax, then use `classList` property and it's `toggle` method on the `taskForm`, to toggle the class `hidden`. Don't use curly braces. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*taskForm\.classList\.toggle\(\1hidden\1\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md new file mode 100644 index 00000000000..6a29158134e --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md @@ -0,0 +1,309 @@ +--- +id: 64e4e7bbedb22d6939001ad3 +title: Step 7 +challengeType: 0 +dashedName: step-7 +--- + +# --description-- + +Add a `click` event listener to the `closeTaskFormBtn` variable, then use the `showModal()` method on your `confirmCloseDialog` variable. This will display a modal with the `Discard` and `Cancel` buttons. + +`showModal()` is a method associated with the HTML `dialog` element. It is used to display a modal dialog box on a web page. + +# --hints-- + +You should call the `addEventListener()` method on your `closeTaskFormBtn` variable. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `showModal()` method on `confirmCloseDialog`. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(["'`]click["'`],\s*\(.*\)\s*=>\s*{?\s*confirmCloseDialog\.showModal\(\);?\s*}?\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md new file mode 100644 index 00000000000..311984043a0 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md @@ -0,0 +1,316 @@ +--- +id: 64e4eaaa9070a66aecbfe603 +title: Step 8 +challengeType: 0 +dashedName: step-8 +--- + +# --description-- + +If the user clicks the `Cancel` button, you want to cancel the process (close the modal showing the two buttons) so the user can continue editing. + +Add a `click` event listener to the `cancelBtn` element, then use the `close()` method on the `confirmCloseDialog` variable. + +`close()` is a method of the window object you can use to close the current window, or a modal you create with the `dialog` element. + +# --hints-- + +You should call the `addEventListener()` method on your `cancelBtn` variable. + +```js +assert.match(code, /cancelBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `close()` method on `confirmCloseDialog`. Don't use curly braces. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*confirmCloseDialog\.close\(\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- + +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md new file mode 100644 index 00000000000..66a40c9771b --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md @@ -0,0 +1,327 @@ +--- +id: 64e4ebc7eabc5a6babd479cd +title: Step 9 +challengeType: 0 +dashedName: step-9 +--- + +# --description-- + +If the user clicks the `Discard` button, you want to close the modal showing the `Cancel` and `Discard` buttons, then hide the form modal. + +Add a click event listener to `discardBtn`, then use the `close()` method on the `confirmCloseDialog` variable. Also, use `classList` to toggle the class `hidden` on `taskForm` so the form modal will close too. + +# --hints-- + +You should call the `addEventListener()` method on your `discardBtn` variable. + +```js +assert.match(code, /discardBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{/) +``` + +Your event listener should use the `close()` method on `confirmCloseDialog`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?/) +``` + +Your event listener should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?\s*taskForm\.classList\.toggle\(\1hidden\1\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md new file mode 100644 index 00000000000..f4002dc0937 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md @@ -0,0 +1,326 @@ +--- +id: 64e4ecd7735a566c9266a338 +title: Step 10 +challengeType: 0 +dashedName: step-10 +--- + +# --description-- + +Now that you've worked on opening and closing the modal, it's time to get the values from the input fields, save them into the `taskData` array, and display them on the page. + +To start, add a `submit` event listener to your `taskForm` element and pass in `e` as the parameter of your arrow function. Inside the curly braces, use the `preventDefault()` method to stop the browser from refreshing the page after submitting the form. + +# --hints-- + +You should call the `addEventListener()` method on your `taskForm` variable. + +```js +assert.match(code, /taskForm\.addEventListener\(/) +``` + +Your event listener should listen for a `submit` event. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces with `e` as the parameter. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{/) +``` + +You should use the `e.preventDefault()` method to stop the browser from reloading the page. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{\s*e\.preventDefault\(\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md new file mode 100644 index 00000000000..148e52f3e93 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md @@ -0,0 +1,337 @@ +--- +id: 64e4eec13546c06d61a63d59 +title: Step 11 +challengeType: 0 +dashedName: step-11 +--- + +# --description-- + +You need to determine whether the task being added already exists or not. If it doesn't exist, you will add it, and if it already exists, you will set it up for editing. You can use the `findIndex()` method to accomplish this. + +`findIndex` is an array method that lets find the index of the first element in an array that satisfies a given testing function. + +Here's an example: + +```js +const numbers = [3, 1, 5, 6, 10, 9, 8]; +const firstEvenNumIndex = numbers.findIndex((num) => num % 2 === 0); + +console.log(firstEvenNumIndex); // Output: 3 – because the first even number (6) is at index 3 +``` + +Declare a `dataArrIndex` variable using `const` and set it to the result of the `findIndex()` method applied to the `taskData` array. Utilize arrow syntax to provide a callback function with `item` as the parameter, and within the callback, check if the `id` property of `item` is equal to the `id` property of `currentTask`. + +If the task exists, this returns the index, and if it doesn't exist, it returns `-1`. + +# --hints-- + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === currentTask.id`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?\s*=>\s*item.id\s*===\s*currentTask.id\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md new file mode 100644 index 00000000000..10826de6f17 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md @@ -0,0 +1,340 @@ +--- +id: 64e4f01d6c72086e016a8626 +title: Step 12 +challengeType: 0 +dashedName: step-12 +--- + +# --description-- + +Next, retrieve the values from the input fields and store them in a `taskObj` object. Each task should also have a unique `id`. + +Create a `taskObj` object with an `id` property as the first property. For the value of the `id` property, retrieve the value of the `titleInput` field, convert it to lowercase, and then use the `split()` and `join()` methods to hyphenate it. + +Make sure all of those are in template literals because you need the `id` property value as a string. + +# --hints-- + +You should use `const` to create a `taskObj` object. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*/) +``` + +Your `taskObj` object should have a key of `id`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id/) +``` + +You should use template strings to get `titleInput.value` as the value of your `id` key and convert it to lowercase. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)/) +``` + +You should use `.split(' ')` on `titleInput.value.toLowerCase()`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)/) +``` + +You should use `.join('-')` on `titleInput.value.toLowerCase().split(' ')`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md new file mode 100644 index 00000000000..764de1b0918 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md @@ -0,0 +1,327 @@ +--- +id: 64ec89ee549ecf802de2b3e2 +title: Step 14 +challengeType: 0 +dashedName: step-14 +--- + +# --description-- + +Retrieve the values from the `titleInput`, `dateInput`, and `descriptionInput` fields, and then save them in the properties `title`, `date`, and `description` of the `taskObj` object. + +# --hints-- + +You should get the value of `titleInput` and save it in a `title` key. + +```js +assert.match(code, /title:\s*titleInput\.value,/) +``` + +You should get the value of `dateInput` and save it in a `date` key. + +```js +assert.match(code, /date:\s*dateInput\.value,/) +``` + +You should get the value of `descriptionInput` and save it in a `description` key. + +```js +assert.match(code, /description:\s*descriptionInput\.value,?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + const taskObj = { + id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`, + --fcc-editable-region-- + + --fcc-editable-region-- + }; +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md new file mode 100644 index 00000000000..3d009188d81 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md @@ -0,0 +1,332 @@ +--- +id: 64ec8f717b261e824d82d6a5 +title: Step 15 +challengeType: 0 +dashedName: step-15 +--- + +# --description-- + +Now that you have obtained the values from the input fields and generated an `id`, you want to add them to your `taskData` array to keep track of each task. However, you should only do this if the task is new. If the task already exists, you will set it up for editing. This is why you have the `dataArrIndex` variable, which provides the index of each task. + +Create an `if` statement with the condition `dataArrIndex === -1`. Within the `if` statement, use the `unshift()` method to add the `taskObj` object to the beginning of the `taskData` array. + +`unshift()` is an array method that is used to add one or more elements to the beginning of an array. + +**N.B:** Try adding a task and log `taskData` to the console to see what it looks like. + +# --hints-- + +You should create an `if` statement with the condition `dataArrIndex === -1`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{/) +``` + +Your `if` statement should have `taskData.unshift(taskObj)` in it's body. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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, + }; + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md new file mode 100644 index 00000000000..f1202bb93fe --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md @@ -0,0 +1,331 @@ +--- +id: 64ec9145e424d8835a4e0f28 +title: Step 16 +challengeType: 0 +dashedName: step-16 +--- + +# --description-- + +Now that you have saved the task in the `taskData` array, you should display the task on the page by looping through it. + +Use `forEach()` on `taskData`, then destructure `id`, `title`, `date`, `description` as the parameters. Don't return anything yet. + +# --hints-- + +You should use `forEach()` on `taskData`. + +```js +assert.match(code, /taskData\.forEach\(\s*/) +``` + +You should destructure `id`, `title`, `date`, `description` as the parameters of the callback of your `forEach()`. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md new file mode 100644 index 00000000000..06bdd7c6ae5 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md @@ -0,0 +1,336 @@ +--- +id: 64ec9282cd547785258cecf2 +title: Step 17 +challengeType: 0 +dashedName: step-17 +--- + +# --description-- + +Using arrow syntax, create an implicit return and use addition assignment to set the `innerHTML` of `tasksContainer` to empty backticks. + +# --hints-- + +You should not alter the existing `taskData.forEach()` and the values you destructured. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +You should use arrow syntax and attach `innerHTML` to `tasksContainer`. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\(\s*tasksContainer\.innerHTML\s*\))?/) +``` + +You should use addition assignment to set the `innerHTML` of `tasksContainer` to an empty pair of backticks. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\s*\(?tasksContainer\.innerHTML\s*\+=\s*`\s*`\)?)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + taskData.forEach(({id, title, date, description})); + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md new file mode 100644 index 00000000000..b5115f99a51 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md @@ -0,0 +1,333 @@ +--- +id: 64ec9343769e8f85c1e17e05 +title: Step 18 +challengeType: 0 +dashedName: step-18 +--- + +# --description-- + +Create a `div` element with the class of `task`. Utilize template strings to set the `id` attribute of the `div` to the `id` you destructured from the task data. + +# --hints-- + +You should create a `div` element with the class `task`. + +```js +assert.match(code, /\s*<\/div>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` + --fcc-editable-region-- + + --fcc-editable-region-- + `) + ); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md new file mode 100644 index 00000000000..81dd3530420 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md @@ -0,0 +1,348 @@ +--- +id: 64ec94f0de20c086e09b0fc3 +title: Step 19 +challengeType: 0 +dashedName: step-19 +--- + +# --description-- + +Create a `p` element and use template strings to set its content to the `title` you destructured. Right before the content of the `p` element, create a `strong` element with the text `Title:`. + +# --hints-- + +You should create a `p` element. + +```js +assert.match(code, /

/) +``` + +You should interpolate `${title}` as the text of your `p` element. + +```js +assert.match(code, /

.*\$\{title\}<\/p>/) +``` + +You should create a `strong` element after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)/) +``` + +Your `strong` element should have the text `Title:`. + +```js +assert.match(code, /(?<=

)Title:\s*<\/strong>\s*/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md new file mode 100644 index 00000000000..4820d100d4d --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md @@ -0,0 +1,336 @@ +--- +id: 64ec959a76336c8767f5cd4d +title: Step 20 +challengeType: 0 +dashedName: step-20 +--- + +# --description-- + +Similarly to the previous step, create another `p` element, and interpolate the `date` you destructured as the text content. Inside this paragraph, create a `strong` element with the text `Date:`. + +# --hints-- + +You should create a `p` element and interpolate `${date}` as the text. + +```js +assert.match(code, /

.*\$\{date\}<\/p>/) +``` + +You should create a `strong` element with the text `Date:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Date:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md new file mode 100644 index 00000000000..4525b0e154c --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md @@ -0,0 +1,340 @@ +--- +id: 64ec96761156a187ed32b274 +title: Step 22 +challengeType: 0 +dashedName: step-22 +--- + +# --description-- + +To allow for task management, you need to include both a delete and an edit button for each task. + +Create two `button` elements with the `type` attribute set to `button` and the `class` attribute set to `btn`. Set the text of the first button to `Edit` and the text of the second button to `Delete`. + +# --hints-- + +You should create a `button` element of type `button`, a class `btn` and `Edit` as the text, in that order. + +```js +assert.match(code, /Edit<\/button/) +``` + +You should create a `button` element of type `button` a class `btn` and `Delete` as the text, in that order. + +```js +assert.match(code, /Delete<\/button/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md new file mode 100644 index 00000000000..0081fa88e1a --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md @@ -0,0 +1,336 @@ +--- +id: 64ec9b10356c2d8aa05d9ce1 +title: Step 23 +challengeType: 0 +dashedName: step-23 +--- + +# --description-- + +After adding the task to the page, you should close the form modal to view the task. To do this, utilize `classList` to toggle the `hidden` class on the `taskForm` element. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +const splitter = code.split("taskData.forEach") +assert.match(splitter[1], /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md new file mode 100644 index 00000000000..6ae1e4dff48 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md @@ -0,0 +1,347 @@ +--- +id: 64ec9c55fdeef78bacd2fc3b +title: Step 24 +challengeType: 0 +dashedName: step-24 +--- + +# --description-- + +If you attempt to add another task now, you'll notice that the input fields retain the values you entered for the previous task. To resolve this, you need to clear the input fields after adding a task. + +Instead of clearing the input fields one by one, it's a good practice to create a function that handles clearing those fields. You can then call this function whenever you need to clear the input fields again. + +Use arrow syntax to create a `reset` function and set it to a pair of curly braces. + +# --hints-- + +You should use `const` and arrow syntax to create a `reset` function. + +```js +assert.match(code, /const\s+reset\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +Your `reset` function should be empty. + +```js +assert.match(reset.toString(), /\(\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md new file mode 100644 index 00000000000..e12fb12ab81 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md @@ -0,0 +1,366 @@ +--- +id: 64fac365aeb8ad70b69b366f +title: Step 25 +challengeType: 0 +dashedName: step-25 +--- + +# --description-- + +Inside the `reset` function, set each value of `titleInput`, `dateInput`, `descriptionInput` to an empty string. + +Also, use `classList` to toggle the class `hidden` on the `taskForm` and set `currentTask` to an empty object. That's because at this point, `currentTask` will be filled with the task the user might have added. + +# --hints-- + +You should set `titleInput.value` to an empty string. + +```js +assert.match(reset.toString(), /titleInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `dateInput.value` to an empty string. + +```js +assert.match(reset.toString(), /dateInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `descriptionInput.value` to an empty string. + +```js +assert.match(reset.toString(), /descriptionInput\.value\s*=\s*('|")\1;?/) +``` + +You should use `classList` to toggle the class `hidden` on `taskForm` + +```js +assert.match(reset.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +You should set `currentTask` to an empty object. + +```js +assert.match(reset.toString(), /currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- +const reset = () => { + +} +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md new file mode 100644 index 00000000000..5f281d0de16 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md @@ -0,0 +1,351 @@ +--- +id: 64fac4d1773e7a719b1254de +title: Step 26 +challengeType: 0 +dashedName: step-26 +--- + +# --description-- + +Remove the existing code toggling the class of `hidden` on `taskForm` and call the `reset` function instead. This would clear the input fields and also hide the form modal for the user to see the added task. + +# --hints-- + +Yous should remove the code toggling the `hidden` class on `taskForm`. + +```js +const splitter = code.split('') +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /reset\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + +--fcc-editable-region-- + taskForm.classList.toggle("hidden"); +--fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md new file mode 100644 index 00000000000..c1a7b0f8965 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md @@ -0,0 +1,351 @@ +--- +id: 64fac6a497811572b338e5e5 +title: Step 27 +challengeType: 0 +dashedName: step-27 +--- + +# --description-- + +Also, remove the existing code toggling the class `hidden` on `taskForm` inside the `discardBtn` event listener and call the `reset` function instead. That's because when you click the `Discard` button, everything in the input fields should go away. + +# --hints-- + +Yous should remove the code toggling the class `hidden` on `taskForm`. + +```js +const splitter = code.split("confirmCloseDialog.close();") +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /confirmCloseDialog\.close\(\);?\s*reset\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); +--fcc-editable-region-- + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md new file mode 100644 index 00000000000..84005e1b01a --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md @@ -0,0 +1,346 @@ +--- +id: 64faca774fd9fd74bc084cc9 +title: Step 28 +challengeType: 0 +dashedName: step-28 +--- + +# --description-- + +You should display the `Cancel` and `Discard` buttons to the user only if there is some text present in the input fields. + +To begin, within the `closeTaskFormBtn` event listener, create a `formInputsContainValues` variable to check if there is a value in the `titleInput` field **or** the `dateInput` field **or** the `descriptionInput` field. + +# --hints-- + +You should use `const` to create a variable `formInputsContainValues` with the value `titleInput.value || dateInput.value || descriptionInput.value;` + +```js +assert.match(code, /const\s*formInputsContainValues\s*=\s*titleInput\.value\s*\|\|\s*dateInput\.value\s*\|\|\s*descriptionInput\.value;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); + +}); +--fcc-editable-region-- + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md new file mode 100644 index 00000000000..07482464ebf --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md @@ -0,0 +1,362 @@ +--- +id: 64facf6180824876f70a2e86 +title: Step 29 +challengeType: 0 +dashedName: step-29 +--- + +# --description-- + +Create an `if` statement to check if `formInputsContainValues` is true. If `formInputsContainValues` is true, indicating that there are changes, use the `showModal()` method on `confirmCloseDialog`. Otherwise, if there are no changes, call the `reset()` function to clear the input fields and hide the form modal. + +# --hints-- + +You should create an `if` statement with the condition `formInputsContainValues`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{/) +``` + +The `if` block of your `if` statement should contain `confirmCloseDialog.showModal();`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?/) +``` + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{/) +``` + +You should call the `reset()` function in the `else` block of your `if` statement. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{\s*reset\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 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; +--fcc-editable-region-- + confirmCloseDialog.showModal(); +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md new file mode 100644 index 00000000000..95b71fba69f --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md @@ -0,0 +1,372 @@ +--- +id: 64fad07f43a101779cb8692a +title: Step 30 +challengeType: 0 +dashedName: step-30 +--- + +# --description-- + +You can enhance code readability and maintainability by refactoring the `submit` event listener into two separate functions. The first function can be used to add the input values to `taskData`, while the second function can be responsible for adding the tasks to the DOM. + +Use arrow syntax to create an `addOrUpdateTask` function. Then move the `dataArrIndex` variable, the `taskObj` object, and the `if` statement into the `addOrUpdateTask` function. + +# --hints-- + +You should use `const` and arrow syntax to create an `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +You should move the `dataArrIndex` variable into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?/) +``` + +You should move the `taskObj` object into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?/) +``` + +You should move the `if` statement with the condition `dataArrIndex === 1` into your `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?\s*if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md new file mode 100644 index 00000000000..8192699bb61 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md @@ -0,0 +1,358 @@ +--- +id: 64fad9cd2eeb1e7ca2ca8c8b +title: Step 31 +challengeType: 0 +dashedName: step-31 +--- + +# --description-- + +Use arrow syntax to create an `updateTaskContainer` function. Then move the `taskData.forEach()` and its content into it. + +# --hints-- + +You should use `const` and arrow syntax to create a `updateTaskContainer` function. + +```js +assert.match(code, /const\s*updateTaskContainer\s*=\s*\(\)\s*=>\s*\{/) +``` + +You should move `taskData.forEach()` and its content into the `updateTaskContainer()` function. + +```js +assert.match(code, /const\s+updateTaskContainer\s+=\s*\(\)\s*=>\s*\{\s*taskData\.forEach\(\s*\(\{\s*id,\s*title,\s*date,\s*description\s*\}\)\s*=>\s*\(tasksContainer\.innerHTML\s*\+=\s*`\s*\s*

Title:<\/strong>\s*\$\{title\}<\/p>\s*

Date:<\/strong>\s*\$\{date\}<\/p>\s*

Description:<\/strong>\s*\$\{description\}<\/p>\s*Edit<\/button>\s*Delete<\/button>\s*<\/div>\s*`\)\s*\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md new file mode 100644 index 00000000000..18b2a462de4 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md @@ -0,0 +1,360 @@ +--- +id: 64fadae4f2d51b7d5d8b98d8 +title: Step 32 +challengeType: 0 +dashedName: step-32 +--- + +# --description-- + +Inside the `addOrUpdateTask` function, call the `updateTaskContainer` and `reset` functions. + +# --hints-- + +You should call the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\)\s*/) +``` + +You should call the `reset` function after calling the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\);?\s*reset\(\);?\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + reset() +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md new file mode 100644 index 00000000000..657bb4aa05b --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md @@ -0,0 +1,355 @@ +--- +id: 64fadff23375f27ff06c6d40 +title: Step 33 +challengeType: 0 +dashedName: step-33 +--- + +# --description-- + +Now remove the `reset()` call inside the `taskForm` submit event listener and call the `addOrUpdateTask` function instead. + +# --hints-- + +You should remove `reset()` and call `addOrUpdateTask()`. + +```js +assert.match(code, /addOrUpdateTask\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + --fcc-editable-region-- + reset() + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md new file mode 100644 index 00000000000..bd759fb7e5f --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md @@ -0,0 +1,364 @@ +--- +id: 64fae068bcdc9c805bd8399e +title: Step 35 +challengeType: 0 +dashedName: step-35 +--- + +# --description-- + +To enable editing and deleting for each task, add an `onclick` attribute to both buttons. Set the value of the `onclick` attribute to `editTask(this)` for the `Edit` button and `deleteTask(this)` for the `Delete` button. The `editTask(this)` function will handle editing, while the `deleteTask(this)` function will handle deletion. + +`this` is a keyword that refers to the current context. In this case, `this` points to the element that triggers the event – the buttons. + +# --hints-- + +You should add `onclick="editTask(this)"` as the first attribute of the edit button. + +```js +assert.match(code, /Edit<\/button>/) +``` + +You should add `onclick="deleteTask(this)"` as the first attribute of the delete button. + +```js +assert.match(code, /Delete<\/button>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + + --fcc-editable-region-- +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md new file mode 100644 index 00000000000..c6fac350818 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md @@ -0,0 +1,371 @@ +--- +id: 64faedcd16a1e985c4c2dc94 +title: Step 36 +challengeType: 0 +dashedName: step-36 +--- + +# --description-- + +Create a `deleteTask` function using arrow syntax. Pass `buttonEl` as the parameter and define an empty set of curly braces for the function body. + +# --hints-- + +You should use `const` and arrow syntax to create a `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `deleteTask` function should take a `buttonEl` parameter. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `deleteTask` function should be empty. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md new file mode 100644 index 00000000000..27631c77f2f --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md @@ -0,0 +1,359 @@ +--- +id: 64faf0418e828c0114a558a7 +title: Step 34 +challengeType: 0 +dashedName: step-34 +--- + +# --description-- + +There's a problem. If you add a task, and then add another, the previous task gets duplicated. This means you need to clear out the existing contents of `tasksContainer` before adding a new task. + +Set the `innerHTML` of `taskContainer` back to an empty string. + + +# --hints-- + +You should set the `innerHTML` of `tasksContainer` to an empty string. + +```js +assert.match(code, /tasksContainer\.innerHTML\s*=\s*("|')\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + --fcc-editable-region-- + + --fcc-editable-region-- + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md new file mode 100644 index 00000000000..9cdbd7860ca --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md @@ -0,0 +1,382 @@ +--- +id: 64faf65b22ad8d07df9be14d +title: Step 37 +challengeType: 0 +dashedName: step-37 +--- + +# --description-- + +You need to find the index of the task you want to delete first. + +Create a `dataArrIndex` variable and set its value using the `findIndex()` method on the `taskData` array. Pass `item` as the parameter for the arrow callback function, and within the callback, check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. Don't use curly braces. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?\s*item\s*\)?\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\s*\);?\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md new file mode 100644 index 00000000000..0e54adadf06 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md @@ -0,0 +1,398 @@ +--- +id: 64faf874364ec308f875f636 +title: Step 38 +challengeType: 0 +dashedName: step-38 +--- + +# --description-- + +You need to remove the task from the DOM using `remove()` and from the `taskData` array using `splice()`. + +`splice()` is an array method that modifies arrays by removing, replacing, or adding elements at a specified index, while also returning the removed elements. It can take up to three parameters: the first one is the mandatory index at which to start, the second is the number of items to remove, and the third is an optional replacement element. Here's an example: + +```js +const fruits = ["mango", "date", "cherry", "banana", "apple"]; + +// Remove date and cherry from the array starting at index 1 +const removedFruits = fruits.splice(1, 2); + +console.log(fruits); // [ 'mango', 'banana', 'apple' ] +console.log(removedFruits); // [ 'date', 'cherry' ] +``` + +Use the `remove()` method to remove the `parentElement` of the `buttonEl` from the DOM. Then use `splice()` to remove the task from the `taskData` array. Pass in `dataArrIndex` and `1` as the parameters of your `splice()`. + +`dataArrIndex` is the index to start and `1` is the number of items to remove. + +# --hints-- + +You should use the `remove()` method to remove the parent element of `buttonEl`. + +```js +assert.match(deleteTask.toString(), /buttonEl\.parentElement\.remove\(\);?/) +``` + +You should use `splice()` on the `taskData` array. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(/) +``` + +The first parameter of your `splice()` method should be `dataArrIndex`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex/) +``` + +The second parameter of your `splice()` method should be `1`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex,\s*1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md new file mode 100644 index 00000000000..3f379d51764 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md @@ -0,0 +1,381 @@ +--- +id: 64fafac95328110a69bcb75f +title: Step 39 +challengeType: 0 +dashedName: step-39 +--- + +# --description-- + +Use arrow syntax to create an `editTask` function. Pass in `buttonEl` as the parameter and add empty curly braces for the body. + +# --hints-- + +You should use `const` and arrow syntax to create an `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `editTask` function should take a `buttonEl` parameter. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `editTask` function should be empty. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md new file mode 100644 index 00000000000..bc0e2a84503 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md @@ -0,0 +1,391 @@ +--- +id: 64fb0fa0968f2b113b2d90e9 +title: Step 40 +challengeType: 0 +dashedName: step-40 +--- + +# --description-- + +As you did in the `deleteTask` function, you need to find the index of the task to be edited. + +Create a `dataArrIndex` variable. For its value, utilize the `findIndex()` method on `taskData`. Pass `item` as the parameter to its callback function and check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md new file mode 100644 index 00000000000..14d527ecf40 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md @@ -0,0 +1,373 @@ +--- +id: 64fb1061ca838611ed6a7d6b +title: Step 41 +challengeType: 0 +dashedName: step-41 +--- + +# --description-- + +Use square bracket notation to retrieve the task to be edited from the `taskData` array using the `dataArrIndex`. Then, assign it to the `currentTask` object to keep track of it. + +# --hints-- + +You should assign `taskData[dataArrIndex]` to `currentTask`. + +```js +assert.match(code, /currentTask\s*=\s*taskData\[dataArrIndex\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex((item) => item.id === buttonEl.parentElement.id); + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md new file mode 100644 index 00000000000..73ef19befcc --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md @@ -0,0 +1,389 @@ +--- +id: 64fb1321e189a6136d200f77 +title: Step 42 +challengeType: 0 +dashedName: step-42 +--- + +# --description-- + +The task to be edited is now in the `currentTask` object. Stage it for editing inside the input fields by setting the value of `titleInput` to `currentTask.title`, `dateInput` to `currentTask.date`, and `descriptionInput` to `currentTask.description`. + +# --hints-- + +You should set `titleInput.value` to `currentTask.title`. + +```js +assert.match(editTask.toString(), /titleInput\.value\s*=\s*currentTask\.title;?/) +``` + +You should set `dateInput.value` to `currentTask.date`. + +```js +assert.match(editTask.toString(), /dateInput\.value\s*=\s*currentTask\.date;?/) +``` + +You should set `descriptionInput.value` to `currentTask.description`. + +```js +assert.match(editTask.toString(), /descriptionInput\.value\s*=\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + currentTask = taskData[dataArrIndex]; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md new file mode 100644 index 00000000000..c6312bf6bd0 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md @@ -0,0 +1,386 @@ +--- +id: 64fb1436adef3e145b4c3501 +title: Step 43 +challengeType: 0 +dashedName: step-43 +--- + +# --description-- + +Set the `innerText` of the `addOrUpdateTaskBtn` button to `Update Task` and its `aria-label` to `Update Task` as well. + +# --hints-- + +You should set the inner text of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.innerText\s*=\s*("|')Update Task\1;?/) +``` + +You should set the `ariaLabel` of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.ariaLabel\s*=\s*("|')Update Task\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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; + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md new file mode 100644 index 00000000000..101870d098a --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md @@ -0,0 +1,384 @@ +--- +id: 64fb14d890415c14f93069ce +title: Step 44 +challengeType: 0 +dashedName: step-44 +--- + +# --description-- + +Finally, display the `form` modal with the values of the input fields by using `classList` to toggle the `hidden` class on `taskForm`. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(editTask.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md new file mode 100644 index 00000000000..7ad62060776 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md @@ -0,0 +1,391 @@ +--- +id: 64fb154a7c48cd159924bb18 +title: Step 45 +challengeType: 0 +dashedName: step-45 +--- + +# --description-- + +At this point, editing a task won't reflect when you submit the task. To make the editing functional, go back to the `if` statement inside the `addOrUpdateTask` function. Create an `else` block and set `taskData[dataArrIndex]` to `taskObj`. + +# --hints-- + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*/) +``` + +Your `else` block should have the code `taskData[dataArrIndex] = taskObj`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*taskData\[dataArrIndex\]\s*=\s*taskObj;?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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, + }; + + --fcc-editable-region-- + if (dataArrIndex === -1) { + taskData.unshift(taskObj); + } + + --fcc-editable-region-- + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md new file mode 100644 index 00000000000..4ec33172878 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md @@ -0,0 +1,401 @@ +--- +id: 64fb1c4dc0feb219149a7c7d +title: Step 46 +challengeType: 0 +dashedName: step-46 +--- + +# --description-- + +If the user attempts to edit a task but decides not to make any changes before closing the form, there is no need to display the modal with the `Cancel` and `Discard` buttons. + +Inside the `closeTaskFormBtn` event listener, use `const` to create another variable named `formInputValuesUpdated`. Check if the user made changes while trying to edit a task by verifying that the `titleInput` value **is not equal to** `currentTask.title`, the `dateInput` value **is not equal to** `currentTask.date`, and the `descriptionInput` value **is not equal to** `currentTask.description`. + +# --hints-- + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*/) +``` + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title` or `dateInput.value` is not equal to `currentTask.date`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date/) +``` + +Your `formInputValuesUpdated` variable should have the value `titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date\s*\|\|\s*descriptionInput\.value\s*!==\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + --fcc-editable-region-- + + --fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md new file mode 100644 index 00000000000..bb8a87acf60 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md @@ -0,0 +1,390 @@ +--- +id: 64fb285637fa1e1c222033e3 +title: Step 47 +challengeType: 0 +dashedName: step-47 +--- + +# --description-- + +Now add `formInputValuesUpdated` as the second mandatory condition in the `if` statement using the `AND` operator. + +This way, the `Cancel` and `Discard` buttons in the modal won't be displayed to the user if they haven't made any changes to the input fields while attempting to edit a task. + +# --hints-- + +Your `if` should have the condition `formInputsContainValues && formInputValuesUpdated`. + +```js +assert.match(code.split("if (formInputsContainValues"), /\s*&&\s*formInputValuesUpdated/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description; + +--fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md new file mode 100644 index 00000000000..94fdc40185a --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md @@ -0,0 +1,414 @@ +--- +id: 64fb29348a60361ccd45c1e2 +title: Step 48 +challengeType: 0 +dashedName: step-48 +--- + +# --description-- + +`localStorage` offers methods for saving, retrieving, and deleting items. The items you save can be of any JavaScript data type. + +For instance, the `setItem()` method is used to save an item, and the `getItem()` method retrieves the item. To delete a specific item, you can utilize the `removeItem()` method, or if you want to delete all items in the storage, you can use `clear()`. + +Here's how you can save an item: + +```js +localStorage.setItem("key", value); // value could be string, number, or any other data type +``` + +A `myTaskArr` array has been provided for you. Use the `setItem()` method to save it with a key of `data`. + +After that, open your browser console and go to the `Applications` tab, select `Local Storage`, and the freeCodeCamp domain you see. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +Your `localStorage.setItem()` should have a key of `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*myTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md new file mode 100644 index 00000000000..2ce0b111937 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md @@ -0,0 +1,404 @@ +--- +id: 64fefebad99209211ec30537 +title: Step 49 +challengeType: 0 +dashedName: step-49 +--- + +# --description-- + +If you check the `Application` tab of your browser console, you'll notice a series of `[object Object]`. This is because everything you save in `localStorage` needs to be in string format. + +To resolve the issue, wrap the data you're saving in the `JSON.stringify()` method. Then, check local storage again to observe the results. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +You should wrap `JSON.stringify()` around `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*JSON\.stringify\(myTaskArr\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- +localStorage.setItem("data", myTaskArr); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md new file mode 100644 index 00000000000..1cb0c0c5930 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md @@ -0,0 +1,406 @@ +--- +id: 64ff0313700dad264d19dfe4 +title: Step 50 +challengeType: 0 +dashedName: step-50 +--- + +# --description-- + +Now that you have the `myTaskArr` array saved in `localStorage` correctly, you can retrieve it with `getItem()` by specifying the key you used to save the item. + +Use the `getItem()` method to retrieve the `myTaskArr` array and assign it to the variable `getTaskArr`. Then, log the `getTaskArr` variable to the console to see the result. + +# --hints-- + +You should use `const` to create a `getTaskArr` variable and assign `localStorage.getItem("data")` to it. + +```js +assert.match(code, /const\s*getTaskArr\s*=\s*localStorage\.getItem\(('|")data\1\);?/) +``` + +You should log the `getTaskArr` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md new file mode 100644 index 00000000000..608713897b5 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md @@ -0,0 +1,411 @@ +--- +id: 64ff04cc33779427a6412449 +title: Step 51 +challengeType: 0 +dashedName: step-51 +--- + +# --description-- + +The item you retrieve is a string, as you saved it with `JSON.stringify()`. To view it in its original form before saving, you need to use `JSON.parse()`. + +Use `getItem()` to retrieve the `myTaskArr` array again. This time, wrap it inside `JSON.parse()`, assign it to the variable `getTaskArrObj` and log the `getTaskArrObj` to the console. + +Check the console to see the difference between `getTaskArr` and `getTaskObj`. + +# --hints-- + +You should use `const` to create a `getTaskArrObj` variable and assign it to `JSON.parse(localStorage.getItem('data'));`. + +```js +assert.match(code, /const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\(('|")data\1\)\);?/) +``` + +You should log the `getTaskArrObj` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md new file mode 100644 index 00000000000..09bad9433fa --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md @@ -0,0 +1,406 @@ +--- +id: 64ff068e0426eb288874ed79 +title: Step 52 +challengeType: 0 +dashedName: step-52 +--- + +# --description-- + +You can use `localStorage.removeItem()` to remove a specific item and `localStorage.clear()` to clear all items in the local storage. + +Remove the `data` item from local storage and open the console to observe the result. You should see `null`. + +# --hints-- + +You should use `localStorage.removeItem()` to remove the `data` item from the browser's local storage. + +```js +assert.match(code, /localStorage\.removeItem\(('|")data\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md new file mode 100644 index 00000000000..5dc3bd6e1d1 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md @@ -0,0 +1,413 @@ +--- +id: 64ff23daf176a92de95f24dc +title: Step 53 +challengeType: 0 +dashedName: step-53 +--- + +# --description-- + +Using `localStorage.clear()` won't just delete a single item from local storage but will remove all items. + +Remove `localStorage.removeItem()` and use `localStorage.clear()` instead. You don't need to pass in anything. You should also see `null` in the console. + +# --hints-- + +You should remove `localStorage.removeItem("data")`. + +```js +assert.notMatch(code, /localStorage\.removeItem\(('|")data\1\);/) +``` + +You should remove everything from the browser `local storage` with `localStorage.clear()`. + +```js +assert.match(code, /localStorage\.clear\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- +localStorage.removeItem("data"); + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md new file mode 100644 index 00000000000..0d6c42c989e --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md @@ -0,0 +1,404 @@ +--- +id: 64ff24b80431f62ec6b93f65 +title: Step 54 +challengeType: 0 +dashedName: step-54 +--- + +# --description-- + +Remove the `myTaskArr` array and all of the code for `localStorage` because you don't need them anymore. + +# --hints-- + +You should remove `myTaskArr` and all the code related to `localStorage` that you've just learned. + +```js +assert.notMatch(code, /const\s*myTaskArr\s*=\s*\[\s*\{\s*task:\s"Walk\s*the\s*Dog",\s*date:\s*"22-04-2022"\s*\},\s*\{\s*task:\s"Read\s*some\s*books",\s*date:\s*"02-11-2023"\s*\},\s*\{\s*task:\s"Watch\s*football",\s*date:\s*"10-08-2021"\s*\},\s*\];?\s*localStorage\.setItem\("data", JSON\.stringify\(myTaskArr\)\);\s*localStorage\.clear\(\);?\s*const\s*getTaskArr\s*=\s*localStorage\.getItem\("data"\)\s*console\.log\(getTaskArr\)\s*const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\("data"\)\);?\s*console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +--fcc-editable-region-- +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +localStorage.clear(); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md new file mode 100644 index 00000000000..cbbcd4df6e1 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md @@ -0,0 +1,404 @@ +--- +id: 65003986d17d1e1865b269c0 +title: Step 55 +challengeType: 0 +dashedName: step-55 +--- + +# --description-- + +Now you should save the task items to local storage when the user adds, updates, or removes a task. + +Inside the `addOrUpdateTask` function, use `setItem()` to save the tasks with a key of `data`, then pass the `taskData` array as its parameter. Ensure that you stringify the `taskData`. + +This would persist data once the user adds or updates tasks. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +assert.match(code, /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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; + } + --fcc-editable-region-- + + --fcc-editable-region-- + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md new file mode 100644 index 00000000000..ec3a700096a --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md @@ -0,0 +1,407 @@ +--- +id: 650046832f92c01a35834bca +title: Step 56 +challengeType: 0 +dashedName: step-56 +--- + +# --description-- + +You also want a deleted task to be removed from local storage. For this, you don't need the `removeItem()` or `clear()` methods. Since you already use `splice()` to remove the deleted task from `taskData`, all you need to do now is save `taskData` to local storage again. + +Use `setItem()` to save the `taskData` array again. Pass in `data` as the key and ensure that `taskData` is stringified before saving. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); + --fcc-editable-region-- + + --fcc-editable-region-- +} + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md new file mode 100644 index 00000000000..40cf6384c4e --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md @@ -0,0 +1,392 @@ +--- +id: 650048b0764f9c1b798200e2 +title: Step 57 +challengeType: 0 +dashedName: step-57 +--- + +# --description-- + +If you add, update, or remove a task, it should reflect in the UI. However, that's not happening now because you have yet to retrieve the tasks. To do this, you need to modify your initial `taskData` to be an empty array. + +Set `taskData` to the retrieval of `data` from local storage **or** an empty array. Make sure you parse the data coming with `JSON.parse()` because you saved it as a string. + +# --hints-- + +You should set the `taskData` variable to `JSON.parse(localStorage.getItem("data")) || [];`. + +```js +assert.match(code, /const\s*taskData\s*=\s*JSON.parse\(localStorage\.getItem\(('|")data\1\)\)\s*\|\|\s*\[\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- +const taskData = []; +--fcc-editable-region-- +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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md new file mode 100644 index 00000000000..08ced41b63f --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md @@ -0,0 +1,776 @@ +--- +id: 65004ba581d03d1d5628b41c +title: Step 58 +challengeType: 0 +dashedName: step-58 +--- + +# --description-- + +You've retrieved the task item(s) now, but they still don't reflect in the UI when the page loads. However, they appear when you add a new task. + +To fix this issue, you can check if there's a task inside `taskData`, and then call the `updateTaskContainer()`. + +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 the condition `taskData.length`. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*/) +``` + +You should call the `updateTaskContainer` function in your `if` statement. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*updateTaskContainer\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + taskForm.classList.toggle("hidden"); +} + +const reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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 + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md new file mode 100644 index 00000000000..6b21e6888ce --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md @@ -0,0 +1,314 @@ +--- +id: 650300a25b6f72964ab8aca6 +title: Step 13 +challengeType: 0 +dashedName: step-13 +--- + +# --description-- + +To make the `id` more unique, add another hyphen and use `Date.now()`. + +# --hints-- + +You should attach `-${Date.now()}` to the existing value of the `id` key. Don't forget it needs to be inside the template string. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + const taskObj = { + id: `${titleInput.value.toLowerCase().split(' ').join('-')}`, + }; + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md new file mode 100644 index 00000000000..0c9fbc152b7 --- /dev/null +++ b/curriculum/challenges/japanese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md @@ -0,0 +1,337 @@ +--- +id: 65099dbd8f137d58e5c0ff16 +title: Step 21 +challengeType: 0 +dashedName: step-21 +--- + +# --description-- + +Create one more `p` element and interpolate the `description` you destructured as the text. Also, create a `strong` element inside the paragraph with the text `Description:`. + +# --hints-- + +You should create a `p` element with `${description}` as the text. + +```js +assert.match(code, /

.*\$\{description\}<\/p>/) +``` + +You should create a `strong` element with the text `Description:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Description:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/japanese/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md b/curriculum/challenges/japanese/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md index 7cce6981cca..b4cada2af12 100644 --- a/curriculum/challenges/japanese/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md +++ b/curriculum/challenges/japanese/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md @@ -12,7 +12,7 @@ This video will help you understand the equations of simple and compound interes Here is the Colab notebook to go along with this video. -Here is an additional Colab notebook that shows you one way to put many of these interest and payment forumlas into Python functions. Also you will see an example of using some formulas to output results, notice a trend, and follow up with other formulas to analyze patterns. +Here is an additional Colab notebook that shows you one way to put many of these interest and payment formulas into Python functions. Also you will see an example of using some formulas to output results, notice a trend, and follow up with other formulas to analyze patterns. # --question-- diff --git a/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md b/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md index 39ab831dad7..144796de1b0 100644 --- a/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md +++ b/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md @@ -71,8 +71,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md b/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md index 53ae7b50350..871e1408079 100644 --- a/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md +++ b/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md @@ -73,7 +73,7 @@ substringDivisibility(5); ```js function substringDivisibility(n) { - function isSubDivisable(digits) { + function isSubDivisible(digits) { const factors = [2, 3, 5, 7, 11, 13, 17]; for (let i = 1; i < digits.length - 2; i++) { @@ -108,17 +108,17 @@ function substringDivisibility(n) { } const allowedDigits = [...new Array(n + 1).keys()]; - const divisablePandigitals = []; + const divisiblePandigitals = []; heapsPermutations( allowedDigits.length, allowedDigits, - isSubDivisable, - divisablePandigitals + isSubDivisible, + divisiblePandigitals ); let sum = 0; - for (let i = 0; i < divisablePandigitals.length; i++) { - sum += divisablePandigitals[i]; + for (let i = 0; i < divisiblePandigitals.length; i++) { + sum += divisiblePandigitals[i]; } return sum; diff --git a/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md b/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md index 7cd5bc67b14..2d583a52355 100644 --- a/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md +++ b/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md @@ -67,8 +67,8 @@ class PrimeSeive { const prime = 2 * i + 3; primes.push(prime); // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } @@ -101,10 +101,10 @@ class PrimeSeive { }; function consecutivePrimeSum(limit) { - // Initalize seive + // Initialize seive const primeSeive = new PrimeSeive(limit); - // Initalize for longest sum < 100 + // Initialize for longest sum < 100 let bestPrime = 41; let bestI = 0; let bestJ = 5; diff --git a/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md b/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md index cdbfc3e568b..5b55ddd8079 100644 --- a/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md +++ b/curriculum/challenges/japanese/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/japanese/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md b/curriculum/challenges/japanese/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md index 78c29aa7952..4fa15980a0d 100644 --- a/curriculum/challenges/japanese/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md +++ b/curriculum/challenges/japanese/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md @@ -31,10 +31,10 @@ For $1 ≤ k ≤ 200$, find $\sum{m(k)}$. # --hints-- -`efficientExponentation()` should return `1582`. +`efficientExponentiation()` should return `1582`. ```js -assert.strictEqual(efficientExponentation(), 1582); +assert.strictEqual(efficientExponentiation(), 1582); ``` # --seed-- @@ -42,12 +42,12 @@ assert.strictEqual(efficientExponentation(), 1582); ## --seed-contents-- ```js -function efficientExponentation() { +function efficientExponentiation() { return true; } -efficientExponentation(); +efficientExponentiation(); ``` # --solutions-- diff --git a/curriculum/challenges/japanese/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md b/curriculum/challenges/japanese/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md index 49d59948762..e402ff347c5 100644 --- a/curriculum/challenges/japanese/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md +++ b/curriculum/challenges/japanese/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/japanese/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md b/curriculum/challenges/japanese/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md index 596f703da28..b843f6f2be2 100644 --- a/curriculum/challenges/japanese/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md +++ b/curriculum/challenges/japanese/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md @@ -35,7 +35,7 @@ concealedSquare(); ```js // Check if n**2 matches the pattern -function squareMatchs(n) { +function squareMatches(n) { // Need BigInt due to size of values let nSquared = (BigInt(n) * BigInt(n)).toString(); @@ -55,8 +55,8 @@ function concealedSquare() { for (let x = maxSquareRoot; x >= minSquareRoot; x -= 10) { // Note: 3*3 = 9 and 7*7 = 49 are only trailing digits // that can produce 9 as trailing digit in square - if (squareMatchs(x + 3)) return (x + 3)*10; - if (squareMatchs(x + 7)) return (x + 7)*10; + if (squareMatches(x + 3)) return (x + 3)*10; + if (squareMatches(x + 7)) return (x + 7)*10; } return -1; } diff --git a/curriculum/challenges/portuguese/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md b/curriculum/challenges/portuguese/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md index 8833a450d9d..4c838c551e9 100644 --- a/curriculum/challenges/portuguese/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md +++ b/curriculum/challenges/portuguese/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md @@ -54,7 +54,24 @@ assert.match(code, /myStorage\.car\.inside/); `gloveBoxContents` ainda deve ser declarada com `const`. ```js -assert.match(code, /const\s+gloveBoxContents\s*=\s*myStorage\.car\.inside\[\s*("|')glove box\1\s*\]|const\s*{\s*('|")glove box\2:\s*gloveBoxContents\s*}\s*=\s*myStorage\.car\.inside;/); +assert.match(code, /const\s+gloveBoxContents\s*=/); +``` + +You should not change the `myStorage` object. + +```js +const expectedMyStorage = { + "car":{ + "inside":{ + "glove box":"maps", + "passenger seat":"crumbs" + }, + "outside":{ + "trunk":"jack" + } + } +}; +assert.deepStrictEqual(myStorage, expectedMyStorage); ``` # --seed-- diff --git a/curriculum/challenges/portuguese/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md b/curriculum/challenges/portuguese/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md index 680e6047c8d..4423f06422b 100644 --- a/curriculum/challenges/portuguese/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md +++ b/curriculum/challenges/portuguese/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md @@ -58,8 +58,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/portuguese/06-quality-assurance/quality-assurance-projects/issue-tracker.md b/curriculum/challenges/portuguese/06-quality-assurance/quality-assurance-projects/issue-tracker.md index a38406f4657..2bc02b6b6a8 100644 --- a/curriculum/challenges/portuguese/06-quality-assurance/quality-assurance-projects/issue-tracker.md +++ b/curriculum/challenges/portuguese/06-quality-assurance/quality-assurance-projects/issue-tracker.md @@ -231,13 +231,13 @@ async (getUserInput) => { }; const url = getUserInput('url') + '/api/issues/fcc-project'; const itemToUpdate = await $.post(url, initialData); - const updateSucccess = await $.ajax({ + const updateSuccess = await $.ajax({ url: url, type: 'PUT', data: { _id: itemToUpdate._id, issue_text: 'New Issue Text' } }); - assert.isObject(updateSucccess); - assert.deepEqual(updateSucccess, { + assert.isObject(updateSuccess); + assert.deepEqual(updateSuccess, { result: 'successfully updated', _id: itemToUpdate._id }); diff --git a/curriculum/challenges/portuguese/10-coding-interview-prep/rosetta-code/24-game.md b/curriculum/challenges/portuguese/10-coding-interview-prep/rosetta-code/24-game.md index 5bcbf1082d3..7817742a6d8 100644 --- a/curriculum/challenges/portuguese/10-coding-interview-prep/rosetta-code/24-game.md +++ b/curriculum/challenges/portuguese/10-coding-interview-prep/rosetta-code/24-game.md @@ -82,7 +82,7 @@ const OPERATORS_ = { "/": (a, b) => a / b, } -const PRECIDENCE_ = { +const PRECEDENCE_ = { "+": 1, "-": 1, "*": 2, @@ -114,7 +114,7 @@ function evaluate_(expression) { case "*": case "/": while (stack.length && - PRECIDENCE_[c] <= PRECIDENCE_[stack[stack.length-1]]) { + PRECEDENCE_[c] <= PRECEDENCE_[stack[stack.length-1]]) { postfix += stack.pop(); } stack.push(c); diff --git a/curriculum/challenges/portuguese/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md b/curriculum/challenges/portuguese/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md index 9a074669ca8..0e219a655bb 100644 --- a/curriculum/challenges/portuguese/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md +++ b/curriculum/challenges/portuguese/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md @@ -175,7 +175,7 @@ function dealFreeCell(seed) { const rng = FreeCellRNG(seed); const deck = getDeck(); - const deltCards = [[], [], [], [], [], [], []]; + const dealtCards = [[], [], [], [], [], [], []]; let currentColumn = 0; let currentRow = 0; @@ -195,7 +195,7 @@ function dealFreeCell(seed) { card = deck.pop(); // Deal this card - deltCards[currentRow].push(card); + dealtCards[currentRow].push(card); currentColumn += 1; if (currentColumn === 8) { currentColumn = 0; @@ -203,6 +203,6 @@ function dealFreeCell(seed) { } } - return deltCards; + return dealtCards; } ``` diff --git a/curriculum/challenges/portuguese/10-coding-interview-prep/rosetta-code/department-numbers.md b/curriculum/challenges/portuguese/10-coding-interview-prep/rosetta-code/department-numbers.md index 1a51909a997..99239f97805 100644 --- a/curriculum/challenges/portuguese/10-coding-interview-prep/rosetta-code/department-numbers.md +++ b/curriculum/challenges/portuguese/10-coding-interview-prep/rosetta-code/department-numbers.md @@ -101,7 +101,7 @@ function combinations(possibleNumbers, total) { function combinations(possibleNumbers, total) { let firstNumber; let secondNumber; - let thridNumber; + let thirdNumber; const allCombinations = []; for (let i = 0; i < possibleNumbers.length; i += 1) { @@ -112,10 +112,10 @@ function combinations(possibleNumbers, total) { secondNumber = possibleNumbers[j]; if (j !== i && firstNumber + secondNumber <= total) { - thridNumber = total - firstNumber - secondNumber; + thirdNumber = total - firstNumber - secondNumber; - if (thridNumber !== firstNumber && thridNumber !== secondNumber && possibleNumbers.includes(thridNumber)) { - allCombinations.push([firstNumber, secondNumber, thridNumber]); + if (thirdNumber !== firstNumber && thirdNumber !== secondNumber && possibleNumbers.includes(thirdNumber)) { + allCombinations.push([firstNumber, secondNumber, thirdNumber]); } } } diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md new file mode 100644 index 00000000000..4b042aa87ba --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md @@ -0,0 +1,310 @@ +--- +id: 64e4e4c4ec263b62ae7bf54d +title: Step 1 +challengeType: 0 +dashedName: step-1 +--- + +# --description-- + +In this project, you will learn how `localStorage` works in JavaScript by building a Todo app. LocalStorage is a web storage feature of JavaScript that lets you persist data by storing the data as a **key:value** pair. + +The HTML and CSS for this project have been provided for you. Take a look at the files to get yourself familiarized with them. + +Begin by accessing the `task-form`, `confirm-close-dialog`, and `open-task-form-btn` elements with the `getElementById()` method. Save them in the variables `taskForm`, `confirmCloseDialog`, and `openTaskFormBtn`. + +# --hints-- + +You should use `getElementById()` to access the `task-form` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should assign the `task-form` element to the variable `taskForm`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+taskForm\s*=\s*document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should use `getElementById()` to access the `confirm-close-dialog` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should assign the `confirm-close-dialog` element to the variable `confirmCloseDialog`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+confirmCloseDialog\s*=\s*document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should use `getElementById()` to access the `open-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +You should assign the `open-task-form-btn` element to the variable `openTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+openTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md new file mode 100644 index 00000000000..32310409728 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md @@ -0,0 +1,310 @@ +--- +id: 64e4e6c86954de67a3e44ee3 +title: Step 2 +challengeType: 0 +dashedName: step-2 +--- + +# --description-- + +You need to access more elements with the `getElementById()` method. This time you need the `close-task-form-btn`, `add-or-update-task-btn`, and `cancel-btn` elements. Save them in the variables `closeTaskFormBtn`, `addOrUpdateTaskBtn`, and `cancelBtn`. + +# --hints-- + +You should use `getElementById()` to access the `close-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should assign the `close-task-form-btn` element to the variable `closeTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+closeTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `add-or-update-task-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should assign the `add-or-update-task-btn` element to the variable `addOrUpdateTaskBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+addOrUpdateTaskBtn\s*=\s*document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `cancel-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +You should assign the `cancel-btn` element to the variable `cancelBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+cancelBtn\s*=\s*document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md new file mode 100644 index 00000000000..17fc6b76517 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md @@ -0,0 +1,313 @@ +--- +id: 64e4e6fe78b5aa67ef2fc3e7 +title: Step 3 +challengeType: 0 +dashedName: step-3 +--- + +# --description-- + +Next, access the `discard-btn`, `tasks-container`, and `title-input` elements using the `getElementById()` method. Save them in variables named `discardBtn`, `tasksContainer`, and `titleInput`, respectively. + +# --hints-- + +You should use `getElementById()` to access the `discard-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should assign the `discard-btn` element to the variable `discardBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+discardBtn\s*=\s*document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `tasks-container` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should assign the `tasks-container` element to the variable `tasksContainer`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+tasksContainer\s*=\s*document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should use `getElementById()` to access the `title-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +You should assign the `title-input` element to the variable `titleInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+titleInput\s*=\s*document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md new file mode 100644 index 00000000000..6e543d36acc --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md @@ -0,0 +1,304 @@ +--- +id: 64e4e7241f52bb682eeb8211 +title: Step 4 +challengeType: 0 +dashedName: step-4 +--- + +# --description-- + +The last set of elements you need to get from the HTML file are the `date-input` and `description-input` elements. Save them in the variables `dateInput` and `descriptionInput` respectively. + +# --hints-- + +You should use `getElementById()` to access the `date-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should assign the `date-input` element to the variable `dateInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+dateInput\s*=\s*document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should use `getElementById()` to access the `description-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +You should assign the `description-input` element to the variable `descriptionInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+descriptionInput\s*=\s*document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md new file mode 100644 index 00000000000..6f0cd6e9fc4 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md @@ -0,0 +1,296 @@ +--- +id: 64e4e74d0fb4f0687bf4145d +title: Step 5 +challengeType: 0 +dashedName: step-5 +--- + +# --description-- + +Create a `taskData` constant and set it to an empty array. This array will store all the tasks along with their associated data, including title, due date, and description. This storage will enable you to keep track of tasks, display them on the page, and save them to `localStorage`. + +Use `let` to create a `currentTask` variable and set it to an empty object. This variable will be used to track the state when editing and discarding tasks. + +# --hints-- + +You should create a `taskData` constant set to an empty array. + +```js +assert.match(code, /const\s+taskData\s*=\s*\[\];?/) +``` + +You should use `let` to create an empty `currentTask` object. + +```js +assert.match(code, /let\s+currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md new file mode 100644 index 00000000000..7eba2896bf4 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md @@ -0,0 +1,307 @@ +--- +id: 64e4e78a7ea4a168de4e6a38 +title: Step 6 +challengeType: 0 +dashedName: step-6 +--- + +# --description-- + +Now, you will work on opening and closing the form modal. + +Add a `click` event listener to the `openTaskFormBtn` variable, and then use `classList` to toggle the `hidden` class on the `taskForm` element. + +Now you can click on the "Add new Task" button and see the form modal. + +# --hints-- + +You should call the `addEventListener()` method on your `openTaskFormBtn` variable. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax, then use `classList` property and it's `toggle` method on the `taskForm`, to toggle the class `hidden`. Don't use curly braces. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*taskForm\.classList\.toggle\(\1hidden\1\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md new file mode 100644 index 00000000000..6a29158134e --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md @@ -0,0 +1,309 @@ +--- +id: 64e4e7bbedb22d6939001ad3 +title: Step 7 +challengeType: 0 +dashedName: step-7 +--- + +# --description-- + +Add a `click` event listener to the `closeTaskFormBtn` variable, then use the `showModal()` method on your `confirmCloseDialog` variable. This will display a modal with the `Discard` and `Cancel` buttons. + +`showModal()` is a method associated with the HTML `dialog` element. It is used to display a modal dialog box on a web page. + +# --hints-- + +You should call the `addEventListener()` method on your `closeTaskFormBtn` variable. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `showModal()` method on `confirmCloseDialog`. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(["'`]click["'`],\s*\(.*\)\s*=>\s*{?\s*confirmCloseDialog\.showModal\(\);?\s*}?\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md new file mode 100644 index 00000000000..311984043a0 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md @@ -0,0 +1,316 @@ +--- +id: 64e4eaaa9070a66aecbfe603 +title: Step 8 +challengeType: 0 +dashedName: step-8 +--- + +# --description-- + +If the user clicks the `Cancel` button, you want to cancel the process (close the modal showing the two buttons) so the user can continue editing. + +Add a `click` event listener to the `cancelBtn` element, then use the `close()` method on the `confirmCloseDialog` variable. + +`close()` is a method of the window object you can use to close the current window, or a modal you create with the `dialog` element. + +# --hints-- + +You should call the `addEventListener()` method on your `cancelBtn` variable. + +```js +assert.match(code, /cancelBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `close()` method on `confirmCloseDialog`. Don't use curly braces. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*confirmCloseDialog\.close\(\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- + +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md new file mode 100644 index 00000000000..66a40c9771b --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md @@ -0,0 +1,327 @@ +--- +id: 64e4ebc7eabc5a6babd479cd +title: Step 9 +challengeType: 0 +dashedName: step-9 +--- + +# --description-- + +If the user clicks the `Discard` button, you want to close the modal showing the `Cancel` and `Discard` buttons, then hide the form modal. + +Add a click event listener to `discardBtn`, then use the `close()` method on the `confirmCloseDialog` variable. Also, use `classList` to toggle the class `hidden` on `taskForm` so the form modal will close too. + +# --hints-- + +You should call the `addEventListener()` method on your `discardBtn` variable. + +```js +assert.match(code, /discardBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{/) +``` + +Your event listener should use the `close()` method on `confirmCloseDialog`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?/) +``` + +Your event listener should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?\s*taskForm\.classList\.toggle\(\1hidden\1\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md new file mode 100644 index 00000000000..f4002dc0937 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md @@ -0,0 +1,326 @@ +--- +id: 64e4ecd7735a566c9266a338 +title: Step 10 +challengeType: 0 +dashedName: step-10 +--- + +# --description-- + +Now that you've worked on opening and closing the modal, it's time to get the values from the input fields, save them into the `taskData` array, and display them on the page. + +To start, add a `submit` event listener to your `taskForm` element and pass in `e` as the parameter of your arrow function. Inside the curly braces, use the `preventDefault()` method to stop the browser from refreshing the page after submitting the form. + +# --hints-- + +You should call the `addEventListener()` method on your `taskForm` variable. + +```js +assert.match(code, /taskForm\.addEventListener\(/) +``` + +Your event listener should listen for a `submit` event. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces with `e` as the parameter. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{/) +``` + +You should use the `e.preventDefault()` method to stop the browser from reloading the page. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{\s*e\.preventDefault\(\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md new file mode 100644 index 00000000000..148e52f3e93 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md @@ -0,0 +1,337 @@ +--- +id: 64e4eec13546c06d61a63d59 +title: Step 11 +challengeType: 0 +dashedName: step-11 +--- + +# --description-- + +You need to determine whether the task being added already exists or not. If it doesn't exist, you will add it, and if it already exists, you will set it up for editing. You can use the `findIndex()` method to accomplish this. + +`findIndex` is an array method that lets find the index of the first element in an array that satisfies a given testing function. + +Here's an example: + +```js +const numbers = [3, 1, 5, 6, 10, 9, 8]; +const firstEvenNumIndex = numbers.findIndex((num) => num % 2 === 0); + +console.log(firstEvenNumIndex); // Output: 3 – because the first even number (6) is at index 3 +``` + +Declare a `dataArrIndex` variable using `const` and set it to the result of the `findIndex()` method applied to the `taskData` array. Utilize arrow syntax to provide a callback function with `item` as the parameter, and within the callback, check if the `id` property of `item` is equal to the `id` property of `currentTask`. + +If the task exists, this returns the index, and if it doesn't exist, it returns `-1`. + +# --hints-- + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === currentTask.id`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?\s*=>\s*item.id\s*===\s*currentTask.id\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md new file mode 100644 index 00000000000..10826de6f17 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md @@ -0,0 +1,340 @@ +--- +id: 64e4f01d6c72086e016a8626 +title: Step 12 +challengeType: 0 +dashedName: step-12 +--- + +# --description-- + +Next, retrieve the values from the input fields and store them in a `taskObj` object. Each task should also have a unique `id`. + +Create a `taskObj` object with an `id` property as the first property. For the value of the `id` property, retrieve the value of the `titleInput` field, convert it to lowercase, and then use the `split()` and `join()` methods to hyphenate it. + +Make sure all of those are in template literals because you need the `id` property value as a string. + +# --hints-- + +You should use `const` to create a `taskObj` object. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*/) +``` + +Your `taskObj` object should have a key of `id`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id/) +``` + +You should use template strings to get `titleInput.value` as the value of your `id` key and convert it to lowercase. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)/) +``` + +You should use `.split(' ')` on `titleInput.value.toLowerCase()`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)/) +``` + +You should use `.join('-')` on `titleInput.value.toLowerCase().split(' ')`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md new file mode 100644 index 00000000000..764de1b0918 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md @@ -0,0 +1,327 @@ +--- +id: 64ec89ee549ecf802de2b3e2 +title: Step 14 +challengeType: 0 +dashedName: step-14 +--- + +# --description-- + +Retrieve the values from the `titleInput`, `dateInput`, and `descriptionInput` fields, and then save them in the properties `title`, `date`, and `description` of the `taskObj` object. + +# --hints-- + +You should get the value of `titleInput` and save it in a `title` key. + +```js +assert.match(code, /title:\s*titleInput\.value,/) +``` + +You should get the value of `dateInput` and save it in a `date` key. + +```js +assert.match(code, /date:\s*dateInput\.value,/) +``` + +You should get the value of `descriptionInput` and save it in a `description` key. + +```js +assert.match(code, /description:\s*descriptionInput\.value,?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + const taskObj = { + id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`, + --fcc-editable-region-- + + --fcc-editable-region-- + }; +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md new file mode 100644 index 00000000000..3d009188d81 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md @@ -0,0 +1,332 @@ +--- +id: 64ec8f717b261e824d82d6a5 +title: Step 15 +challengeType: 0 +dashedName: step-15 +--- + +# --description-- + +Now that you have obtained the values from the input fields and generated an `id`, you want to add them to your `taskData` array to keep track of each task. However, you should only do this if the task is new. If the task already exists, you will set it up for editing. This is why you have the `dataArrIndex` variable, which provides the index of each task. + +Create an `if` statement with the condition `dataArrIndex === -1`. Within the `if` statement, use the `unshift()` method to add the `taskObj` object to the beginning of the `taskData` array. + +`unshift()` is an array method that is used to add one or more elements to the beginning of an array. + +**N.B:** Try adding a task and log `taskData` to the console to see what it looks like. + +# --hints-- + +You should create an `if` statement with the condition `dataArrIndex === -1`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{/) +``` + +Your `if` statement should have `taskData.unshift(taskObj)` in it's body. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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, + }; + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md new file mode 100644 index 00000000000..f1202bb93fe --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md @@ -0,0 +1,331 @@ +--- +id: 64ec9145e424d8835a4e0f28 +title: Step 16 +challengeType: 0 +dashedName: step-16 +--- + +# --description-- + +Now that you have saved the task in the `taskData` array, you should display the task on the page by looping through it. + +Use `forEach()` on `taskData`, then destructure `id`, `title`, `date`, `description` as the parameters. Don't return anything yet. + +# --hints-- + +You should use `forEach()` on `taskData`. + +```js +assert.match(code, /taskData\.forEach\(\s*/) +``` + +You should destructure `id`, `title`, `date`, `description` as the parameters of the callback of your `forEach()`. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md new file mode 100644 index 00000000000..06bdd7c6ae5 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md @@ -0,0 +1,336 @@ +--- +id: 64ec9282cd547785258cecf2 +title: Step 17 +challengeType: 0 +dashedName: step-17 +--- + +# --description-- + +Using arrow syntax, create an implicit return and use addition assignment to set the `innerHTML` of `tasksContainer` to empty backticks. + +# --hints-- + +You should not alter the existing `taskData.forEach()` and the values you destructured. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +You should use arrow syntax and attach `innerHTML` to `tasksContainer`. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\(\s*tasksContainer\.innerHTML\s*\))?/) +``` + +You should use addition assignment to set the `innerHTML` of `tasksContainer` to an empty pair of backticks. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\s*\(?tasksContainer\.innerHTML\s*\+=\s*`\s*`\)?)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + taskData.forEach(({id, title, date, description})); + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md new file mode 100644 index 00000000000..b5115f99a51 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md @@ -0,0 +1,333 @@ +--- +id: 64ec9343769e8f85c1e17e05 +title: Step 18 +challengeType: 0 +dashedName: step-18 +--- + +# --description-- + +Create a `div` element with the class of `task`. Utilize template strings to set the `id` attribute of the `div` to the `id` you destructured from the task data. + +# --hints-- + +You should create a `div` element with the class `task`. + +```js +assert.match(code, /\s*<\/div>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` + --fcc-editable-region-- + + --fcc-editable-region-- + `) + ); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md new file mode 100644 index 00000000000..81dd3530420 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md @@ -0,0 +1,348 @@ +--- +id: 64ec94f0de20c086e09b0fc3 +title: Step 19 +challengeType: 0 +dashedName: step-19 +--- + +# --description-- + +Create a `p` element and use template strings to set its content to the `title` you destructured. Right before the content of the `p` element, create a `strong` element with the text `Title:`. + +# --hints-- + +You should create a `p` element. + +```js +assert.match(code, /

/) +``` + +You should interpolate `${title}` as the text of your `p` element. + +```js +assert.match(code, /

.*\$\{title\}<\/p>/) +``` + +You should create a `strong` element after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)/) +``` + +Your `strong` element should have the text `Title:`. + +```js +assert.match(code, /(?<=

)Title:\s*<\/strong>\s*/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md new file mode 100644 index 00000000000..4820d100d4d --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md @@ -0,0 +1,336 @@ +--- +id: 64ec959a76336c8767f5cd4d +title: Step 20 +challengeType: 0 +dashedName: step-20 +--- + +# --description-- + +Similarly to the previous step, create another `p` element, and interpolate the `date` you destructured as the text content. Inside this paragraph, create a `strong` element with the text `Date:`. + +# --hints-- + +You should create a `p` element and interpolate `${date}` as the text. + +```js +assert.match(code, /

.*\$\{date\}<\/p>/) +``` + +You should create a `strong` element with the text `Date:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Date:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md new file mode 100644 index 00000000000..4525b0e154c --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md @@ -0,0 +1,340 @@ +--- +id: 64ec96761156a187ed32b274 +title: Step 22 +challengeType: 0 +dashedName: step-22 +--- + +# --description-- + +To allow for task management, you need to include both a delete and an edit button for each task. + +Create two `button` elements with the `type` attribute set to `button` and the `class` attribute set to `btn`. Set the text of the first button to `Edit` and the text of the second button to `Delete`. + +# --hints-- + +You should create a `button` element of type `button`, a class `btn` and `Edit` as the text, in that order. + +```js +assert.match(code, /Edit<\/button/) +``` + +You should create a `button` element of type `button` a class `btn` and `Delete` as the text, in that order. + +```js +assert.match(code, /Delete<\/button/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md new file mode 100644 index 00000000000..0081fa88e1a --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md @@ -0,0 +1,336 @@ +--- +id: 64ec9b10356c2d8aa05d9ce1 +title: Step 23 +challengeType: 0 +dashedName: step-23 +--- + +# --description-- + +After adding the task to the page, you should close the form modal to view the task. To do this, utilize `classList` to toggle the `hidden` class on the `taskForm` element. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +const splitter = code.split("taskData.forEach") +assert.match(splitter[1], /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md new file mode 100644 index 00000000000..6ae1e4dff48 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md @@ -0,0 +1,347 @@ +--- +id: 64ec9c55fdeef78bacd2fc3b +title: Step 24 +challengeType: 0 +dashedName: step-24 +--- + +# --description-- + +If you attempt to add another task now, you'll notice that the input fields retain the values you entered for the previous task. To resolve this, you need to clear the input fields after adding a task. + +Instead of clearing the input fields one by one, it's a good practice to create a function that handles clearing those fields. You can then call this function whenever you need to clear the input fields again. + +Use arrow syntax to create a `reset` function and set it to a pair of curly braces. + +# --hints-- + +You should use `const` and arrow syntax to create a `reset` function. + +```js +assert.match(code, /const\s+reset\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +Your `reset` function should be empty. + +```js +assert.match(reset.toString(), /\(\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md new file mode 100644 index 00000000000..e12fb12ab81 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md @@ -0,0 +1,366 @@ +--- +id: 64fac365aeb8ad70b69b366f +title: Step 25 +challengeType: 0 +dashedName: step-25 +--- + +# --description-- + +Inside the `reset` function, set each value of `titleInput`, `dateInput`, `descriptionInput` to an empty string. + +Also, use `classList` to toggle the class `hidden` on the `taskForm` and set `currentTask` to an empty object. That's because at this point, `currentTask` will be filled with the task the user might have added. + +# --hints-- + +You should set `titleInput.value` to an empty string. + +```js +assert.match(reset.toString(), /titleInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `dateInput.value` to an empty string. + +```js +assert.match(reset.toString(), /dateInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `descriptionInput.value` to an empty string. + +```js +assert.match(reset.toString(), /descriptionInput\.value\s*=\s*('|")\1;?/) +``` + +You should use `classList` to toggle the class `hidden` on `taskForm` + +```js +assert.match(reset.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +You should set `currentTask` to an empty object. + +```js +assert.match(reset.toString(), /currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- +const reset = () => { + +} +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md new file mode 100644 index 00000000000..5f281d0de16 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md @@ -0,0 +1,351 @@ +--- +id: 64fac4d1773e7a719b1254de +title: Step 26 +challengeType: 0 +dashedName: step-26 +--- + +# --description-- + +Remove the existing code toggling the class of `hidden` on `taskForm` and call the `reset` function instead. This would clear the input fields and also hide the form modal for the user to see the added task. + +# --hints-- + +Yous should remove the code toggling the `hidden` class on `taskForm`. + +```js +const splitter = code.split('') +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /reset\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + +--fcc-editable-region-- + taskForm.classList.toggle("hidden"); +--fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md new file mode 100644 index 00000000000..c1a7b0f8965 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md @@ -0,0 +1,351 @@ +--- +id: 64fac6a497811572b338e5e5 +title: Step 27 +challengeType: 0 +dashedName: step-27 +--- + +# --description-- + +Also, remove the existing code toggling the class `hidden` on `taskForm` inside the `discardBtn` event listener and call the `reset` function instead. That's because when you click the `Discard` button, everything in the input fields should go away. + +# --hints-- + +Yous should remove the code toggling the class `hidden` on `taskForm`. + +```js +const splitter = code.split("confirmCloseDialog.close();") +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /confirmCloseDialog\.close\(\);?\s*reset\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); +--fcc-editable-region-- + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md new file mode 100644 index 00000000000..84005e1b01a --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md @@ -0,0 +1,346 @@ +--- +id: 64faca774fd9fd74bc084cc9 +title: Step 28 +challengeType: 0 +dashedName: step-28 +--- + +# --description-- + +You should display the `Cancel` and `Discard` buttons to the user only if there is some text present in the input fields. + +To begin, within the `closeTaskFormBtn` event listener, create a `formInputsContainValues` variable to check if there is a value in the `titleInput` field **or** the `dateInput` field **or** the `descriptionInput` field. + +# --hints-- + +You should use `const` to create a variable `formInputsContainValues` with the value `titleInput.value || dateInput.value || descriptionInput.value;` + +```js +assert.match(code, /const\s*formInputsContainValues\s*=\s*titleInput\.value\s*\|\|\s*dateInput\.value\s*\|\|\s*descriptionInput\.value;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); + +}); +--fcc-editable-region-- + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md new file mode 100644 index 00000000000..07482464ebf --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md @@ -0,0 +1,362 @@ +--- +id: 64facf6180824876f70a2e86 +title: Step 29 +challengeType: 0 +dashedName: step-29 +--- + +# --description-- + +Create an `if` statement to check if `formInputsContainValues` is true. If `formInputsContainValues` is true, indicating that there are changes, use the `showModal()` method on `confirmCloseDialog`. Otherwise, if there are no changes, call the `reset()` function to clear the input fields and hide the form modal. + +# --hints-- + +You should create an `if` statement with the condition `formInputsContainValues`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{/) +``` + +The `if` block of your `if` statement should contain `confirmCloseDialog.showModal();`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?/) +``` + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{/) +``` + +You should call the `reset()` function in the `else` block of your `if` statement. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{\s*reset\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 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; +--fcc-editable-region-- + confirmCloseDialog.showModal(); +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md new file mode 100644 index 00000000000..95b71fba69f --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md @@ -0,0 +1,372 @@ +--- +id: 64fad07f43a101779cb8692a +title: Step 30 +challengeType: 0 +dashedName: step-30 +--- + +# --description-- + +You can enhance code readability and maintainability by refactoring the `submit` event listener into two separate functions. The first function can be used to add the input values to `taskData`, while the second function can be responsible for adding the tasks to the DOM. + +Use arrow syntax to create an `addOrUpdateTask` function. Then move the `dataArrIndex` variable, the `taskObj` object, and the `if` statement into the `addOrUpdateTask` function. + +# --hints-- + +You should use `const` and arrow syntax to create an `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +You should move the `dataArrIndex` variable into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?/) +``` + +You should move the `taskObj` object into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?/) +``` + +You should move the `if` statement with the condition `dataArrIndex === 1` into your `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?\s*if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md new file mode 100644 index 00000000000..8192699bb61 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md @@ -0,0 +1,358 @@ +--- +id: 64fad9cd2eeb1e7ca2ca8c8b +title: Step 31 +challengeType: 0 +dashedName: step-31 +--- + +# --description-- + +Use arrow syntax to create an `updateTaskContainer` function. Then move the `taskData.forEach()` and its content into it. + +# --hints-- + +You should use `const` and arrow syntax to create a `updateTaskContainer` function. + +```js +assert.match(code, /const\s*updateTaskContainer\s*=\s*\(\)\s*=>\s*\{/) +``` + +You should move `taskData.forEach()` and its content into the `updateTaskContainer()` function. + +```js +assert.match(code, /const\s+updateTaskContainer\s+=\s*\(\)\s*=>\s*\{\s*taskData\.forEach\(\s*\(\{\s*id,\s*title,\s*date,\s*description\s*\}\)\s*=>\s*\(tasksContainer\.innerHTML\s*\+=\s*`\s*\s*

Title:<\/strong>\s*\$\{title\}<\/p>\s*

Date:<\/strong>\s*\$\{date\}<\/p>\s*

Description:<\/strong>\s*\$\{description\}<\/p>\s*Edit<\/button>\s*Delete<\/button>\s*<\/div>\s*`\)\s*\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md new file mode 100644 index 00000000000..18b2a462de4 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md @@ -0,0 +1,360 @@ +--- +id: 64fadae4f2d51b7d5d8b98d8 +title: Step 32 +challengeType: 0 +dashedName: step-32 +--- + +# --description-- + +Inside the `addOrUpdateTask` function, call the `updateTaskContainer` and `reset` functions. + +# --hints-- + +You should call the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\)\s*/) +``` + +You should call the `reset` function after calling the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\);?\s*reset\(\);?\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + reset() +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md new file mode 100644 index 00000000000..657bb4aa05b --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md @@ -0,0 +1,355 @@ +--- +id: 64fadff23375f27ff06c6d40 +title: Step 33 +challengeType: 0 +dashedName: step-33 +--- + +# --description-- + +Now remove the `reset()` call inside the `taskForm` submit event listener and call the `addOrUpdateTask` function instead. + +# --hints-- + +You should remove `reset()` and call `addOrUpdateTask()`. + +```js +assert.match(code, /addOrUpdateTask\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + --fcc-editable-region-- + reset() + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md new file mode 100644 index 00000000000..bd759fb7e5f --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md @@ -0,0 +1,364 @@ +--- +id: 64fae068bcdc9c805bd8399e +title: Step 35 +challengeType: 0 +dashedName: step-35 +--- + +# --description-- + +To enable editing and deleting for each task, add an `onclick` attribute to both buttons. Set the value of the `onclick` attribute to `editTask(this)` for the `Edit` button and `deleteTask(this)` for the `Delete` button. The `editTask(this)` function will handle editing, while the `deleteTask(this)` function will handle deletion. + +`this` is a keyword that refers to the current context. In this case, `this` points to the element that triggers the event – the buttons. + +# --hints-- + +You should add `onclick="editTask(this)"` as the first attribute of the edit button. + +```js +assert.match(code, /Edit<\/button>/) +``` + +You should add `onclick="deleteTask(this)"` as the first attribute of the delete button. + +```js +assert.match(code, /Delete<\/button>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + + --fcc-editable-region-- +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md new file mode 100644 index 00000000000..c6fac350818 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md @@ -0,0 +1,371 @@ +--- +id: 64faedcd16a1e985c4c2dc94 +title: Step 36 +challengeType: 0 +dashedName: step-36 +--- + +# --description-- + +Create a `deleteTask` function using arrow syntax. Pass `buttonEl` as the parameter and define an empty set of curly braces for the function body. + +# --hints-- + +You should use `const` and arrow syntax to create a `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `deleteTask` function should take a `buttonEl` parameter. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `deleteTask` function should be empty. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md new file mode 100644 index 00000000000..27631c77f2f --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md @@ -0,0 +1,359 @@ +--- +id: 64faf0418e828c0114a558a7 +title: Step 34 +challengeType: 0 +dashedName: step-34 +--- + +# --description-- + +There's a problem. If you add a task, and then add another, the previous task gets duplicated. This means you need to clear out the existing contents of `tasksContainer` before adding a new task. + +Set the `innerHTML` of `taskContainer` back to an empty string. + + +# --hints-- + +You should set the `innerHTML` of `tasksContainer` to an empty string. + +```js +assert.match(code, /tasksContainer\.innerHTML\s*=\s*("|')\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + --fcc-editable-region-- + + --fcc-editable-region-- + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md new file mode 100644 index 00000000000..9cdbd7860ca --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md @@ -0,0 +1,382 @@ +--- +id: 64faf65b22ad8d07df9be14d +title: Step 37 +challengeType: 0 +dashedName: step-37 +--- + +# --description-- + +You need to find the index of the task you want to delete first. + +Create a `dataArrIndex` variable and set its value using the `findIndex()` method on the `taskData` array. Pass `item` as the parameter for the arrow callback function, and within the callback, check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. Don't use curly braces. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?\s*item\s*\)?\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\s*\);?\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md new file mode 100644 index 00000000000..0e54adadf06 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md @@ -0,0 +1,398 @@ +--- +id: 64faf874364ec308f875f636 +title: Step 38 +challengeType: 0 +dashedName: step-38 +--- + +# --description-- + +You need to remove the task from the DOM using `remove()` and from the `taskData` array using `splice()`. + +`splice()` is an array method that modifies arrays by removing, replacing, or adding elements at a specified index, while also returning the removed elements. It can take up to three parameters: the first one is the mandatory index at which to start, the second is the number of items to remove, and the third is an optional replacement element. Here's an example: + +```js +const fruits = ["mango", "date", "cherry", "banana", "apple"]; + +// Remove date and cherry from the array starting at index 1 +const removedFruits = fruits.splice(1, 2); + +console.log(fruits); // [ 'mango', 'banana', 'apple' ] +console.log(removedFruits); // [ 'date', 'cherry' ] +``` + +Use the `remove()` method to remove the `parentElement` of the `buttonEl` from the DOM. Then use `splice()` to remove the task from the `taskData` array. Pass in `dataArrIndex` and `1` as the parameters of your `splice()`. + +`dataArrIndex` is the index to start and `1` is the number of items to remove. + +# --hints-- + +You should use the `remove()` method to remove the parent element of `buttonEl`. + +```js +assert.match(deleteTask.toString(), /buttonEl\.parentElement\.remove\(\);?/) +``` + +You should use `splice()` on the `taskData` array. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(/) +``` + +The first parameter of your `splice()` method should be `dataArrIndex`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex/) +``` + +The second parameter of your `splice()` method should be `1`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex,\s*1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md new file mode 100644 index 00000000000..3f379d51764 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md @@ -0,0 +1,381 @@ +--- +id: 64fafac95328110a69bcb75f +title: Step 39 +challengeType: 0 +dashedName: step-39 +--- + +# --description-- + +Use arrow syntax to create an `editTask` function. Pass in `buttonEl` as the parameter and add empty curly braces for the body. + +# --hints-- + +You should use `const` and arrow syntax to create an `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `editTask` function should take a `buttonEl` parameter. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `editTask` function should be empty. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md new file mode 100644 index 00000000000..bc0e2a84503 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md @@ -0,0 +1,391 @@ +--- +id: 64fb0fa0968f2b113b2d90e9 +title: Step 40 +challengeType: 0 +dashedName: step-40 +--- + +# --description-- + +As you did in the `deleteTask` function, you need to find the index of the task to be edited. + +Create a `dataArrIndex` variable. For its value, utilize the `findIndex()` method on `taskData`. Pass `item` as the parameter to its callback function and check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md new file mode 100644 index 00000000000..14d527ecf40 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md @@ -0,0 +1,373 @@ +--- +id: 64fb1061ca838611ed6a7d6b +title: Step 41 +challengeType: 0 +dashedName: step-41 +--- + +# --description-- + +Use square bracket notation to retrieve the task to be edited from the `taskData` array using the `dataArrIndex`. Then, assign it to the `currentTask` object to keep track of it. + +# --hints-- + +You should assign `taskData[dataArrIndex]` to `currentTask`. + +```js +assert.match(code, /currentTask\s*=\s*taskData\[dataArrIndex\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex((item) => item.id === buttonEl.parentElement.id); + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md new file mode 100644 index 00000000000..73ef19befcc --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md @@ -0,0 +1,389 @@ +--- +id: 64fb1321e189a6136d200f77 +title: Step 42 +challengeType: 0 +dashedName: step-42 +--- + +# --description-- + +The task to be edited is now in the `currentTask` object. Stage it for editing inside the input fields by setting the value of `titleInput` to `currentTask.title`, `dateInput` to `currentTask.date`, and `descriptionInput` to `currentTask.description`. + +# --hints-- + +You should set `titleInput.value` to `currentTask.title`. + +```js +assert.match(editTask.toString(), /titleInput\.value\s*=\s*currentTask\.title;?/) +``` + +You should set `dateInput.value` to `currentTask.date`. + +```js +assert.match(editTask.toString(), /dateInput\.value\s*=\s*currentTask\.date;?/) +``` + +You should set `descriptionInput.value` to `currentTask.description`. + +```js +assert.match(editTask.toString(), /descriptionInput\.value\s*=\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + currentTask = taskData[dataArrIndex]; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md new file mode 100644 index 00000000000..c6312bf6bd0 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md @@ -0,0 +1,386 @@ +--- +id: 64fb1436adef3e145b4c3501 +title: Step 43 +challengeType: 0 +dashedName: step-43 +--- + +# --description-- + +Set the `innerText` of the `addOrUpdateTaskBtn` button to `Update Task` and its `aria-label` to `Update Task` as well. + +# --hints-- + +You should set the inner text of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.innerText\s*=\s*("|')Update Task\1;?/) +``` + +You should set the `ariaLabel` of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.ariaLabel\s*=\s*("|')Update Task\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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; + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md new file mode 100644 index 00000000000..101870d098a --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md @@ -0,0 +1,384 @@ +--- +id: 64fb14d890415c14f93069ce +title: Step 44 +challengeType: 0 +dashedName: step-44 +--- + +# --description-- + +Finally, display the `form` modal with the values of the input fields by using `classList` to toggle the `hidden` class on `taskForm`. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(editTask.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md new file mode 100644 index 00000000000..7ad62060776 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md @@ -0,0 +1,391 @@ +--- +id: 64fb154a7c48cd159924bb18 +title: Step 45 +challengeType: 0 +dashedName: step-45 +--- + +# --description-- + +At this point, editing a task won't reflect when you submit the task. To make the editing functional, go back to the `if` statement inside the `addOrUpdateTask` function. Create an `else` block and set `taskData[dataArrIndex]` to `taskObj`. + +# --hints-- + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*/) +``` + +Your `else` block should have the code `taskData[dataArrIndex] = taskObj`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*taskData\[dataArrIndex\]\s*=\s*taskObj;?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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, + }; + + --fcc-editable-region-- + if (dataArrIndex === -1) { + taskData.unshift(taskObj); + } + + --fcc-editable-region-- + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md new file mode 100644 index 00000000000..4ec33172878 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md @@ -0,0 +1,401 @@ +--- +id: 64fb1c4dc0feb219149a7c7d +title: Step 46 +challengeType: 0 +dashedName: step-46 +--- + +# --description-- + +If the user attempts to edit a task but decides not to make any changes before closing the form, there is no need to display the modal with the `Cancel` and `Discard` buttons. + +Inside the `closeTaskFormBtn` event listener, use `const` to create another variable named `formInputValuesUpdated`. Check if the user made changes while trying to edit a task by verifying that the `titleInput` value **is not equal to** `currentTask.title`, the `dateInput` value **is not equal to** `currentTask.date`, and the `descriptionInput` value **is not equal to** `currentTask.description`. + +# --hints-- + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*/) +``` + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title` or `dateInput.value` is not equal to `currentTask.date`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date/) +``` + +Your `formInputValuesUpdated` variable should have the value `titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date\s*\|\|\s*descriptionInput\.value\s*!==\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + --fcc-editable-region-- + + --fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md new file mode 100644 index 00000000000..bb8a87acf60 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md @@ -0,0 +1,390 @@ +--- +id: 64fb285637fa1e1c222033e3 +title: Step 47 +challengeType: 0 +dashedName: step-47 +--- + +# --description-- + +Now add `formInputValuesUpdated` as the second mandatory condition in the `if` statement using the `AND` operator. + +This way, the `Cancel` and `Discard` buttons in the modal won't be displayed to the user if they haven't made any changes to the input fields while attempting to edit a task. + +# --hints-- + +Your `if` should have the condition `formInputsContainValues && formInputValuesUpdated`. + +```js +assert.match(code.split("if (formInputsContainValues"), /\s*&&\s*formInputValuesUpdated/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description; + +--fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md new file mode 100644 index 00000000000..94fdc40185a --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md @@ -0,0 +1,414 @@ +--- +id: 64fb29348a60361ccd45c1e2 +title: Step 48 +challengeType: 0 +dashedName: step-48 +--- + +# --description-- + +`localStorage` offers methods for saving, retrieving, and deleting items. The items you save can be of any JavaScript data type. + +For instance, the `setItem()` method is used to save an item, and the `getItem()` method retrieves the item. To delete a specific item, you can utilize the `removeItem()` method, or if you want to delete all items in the storage, you can use `clear()`. + +Here's how you can save an item: + +```js +localStorage.setItem("key", value); // value could be string, number, or any other data type +``` + +A `myTaskArr` array has been provided for you. Use the `setItem()` method to save it with a key of `data`. + +After that, open your browser console and go to the `Applications` tab, select `Local Storage`, and the freeCodeCamp domain you see. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +Your `localStorage.setItem()` should have a key of `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*myTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md new file mode 100644 index 00000000000..2ce0b111937 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md @@ -0,0 +1,404 @@ +--- +id: 64fefebad99209211ec30537 +title: Step 49 +challengeType: 0 +dashedName: step-49 +--- + +# --description-- + +If you check the `Application` tab of your browser console, you'll notice a series of `[object Object]`. This is because everything you save in `localStorage` needs to be in string format. + +To resolve the issue, wrap the data you're saving in the `JSON.stringify()` method. Then, check local storage again to observe the results. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +You should wrap `JSON.stringify()` around `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*JSON\.stringify\(myTaskArr\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- +localStorage.setItem("data", myTaskArr); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md new file mode 100644 index 00000000000..1cb0c0c5930 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md @@ -0,0 +1,406 @@ +--- +id: 64ff0313700dad264d19dfe4 +title: Step 50 +challengeType: 0 +dashedName: step-50 +--- + +# --description-- + +Now that you have the `myTaskArr` array saved in `localStorage` correctly, you can retrieve it with `getItem()` by specifying the key you used to save the item. + +Use the `getItem()` method to retrieve the `myTaskArr` array and assign it to the variable `getTaskArr`. Then, log the `getTaskArr` variable to the console to see the result. + +# --hints-- + +You should use `const` to create a `getTaskArr` variable and assign `localStorage.getItem("data")` to it. + +```js +assert.match(code, /const\s*getTaskArr\s*=\s*localStorage\.getItem\(('|")data\1\);?/) +``` + +You should log the `getTaskArr` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md new file mode 100644 index 00000000000..608713897b5 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md @@ -0,0 +1,411 @@ +--- +id: 64ff04cc33779427a6412449 +title: Step 51 +challengeType: 0 +dashedName: step-51 +--- + +# --description-- + +The item you retrieve is a string, as you saved it with `JSON.stringify()`. To view it in its original form before saving, you need to use `JSON.parse()`. + +Use `getItem()` to retrieve the `myTaskArr` array again. This time, wrap it inside `JSON.parse()`, assign it to the variable `getTaskArrObj` and log the `getTaskArrObj` to the console. + +Check the console to see the difference between `getTaskArr` and `getTaskObj`. + +# --hints-- + +You should use `const` to create a `getTaskArrObj` variable and assign it to `JSON.parse(localStorage.getItem('data'));`. + +```js +assert.match(code, /const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\(('|")data\1\)\);?/) +``` + +You should log the `getTaskArrObj` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md new file mode 100644 index 00000000000..09bad9433fa --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md @@ -0,0 +1,406 @@ +--- +id: 64ff068e0426eb288874ed79 +title: Step 52 +challengeType: 0 +dashedName: step-52 +--- + +# --description-- + +You can use `localStorage.removeItem()` to remove a specific item and `localStorage.clear()` to clear all items in the local storage. + +Remove the `data` item from local storage and open the console to observe the result. You should see `null`. + +# --hints-- + +You should use `localStorage.removeItem()` to remove the `data` item from the browser's local storage. + +```js +assert.match(code, /localStorage\.removeItem\(('|")data\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md new file mode 100644 index 00000000000..5dc3bd6e1d1 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md @@ -0,0 +1,413 @@ +--- +id: 64ff23daf176a92de95f24dc +title: Step 53 +challengeType: 0 +dashedName: step-53 +--- + +# --description-- + +Using `localStorage.clear()` won't just delete a single item from local storage but will remove all items. + +Remove `localStorage.removeItem()` and use `localStorage.clear()` instead. You don't need to pass in anything. You should also see `null` in the console. + +# --hints-- + +You should remove `localStorage.removeItem("data")`. + +```js +assert.notMatch(code, /localStorage\.removeItem\(('|")data\1\);/) +``` + +You should remove everything from the browser `local storage` with `localStorage.clear()`. + +```js +assert.match(code, /localStorage\.clear\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- +localStorage.removeItem("data"); + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md new file mode 100644 index 00000000000..0d6c42c989e --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md @@ -0,0 +1,404 @@ +--- +id: 64ff24b80431f62ec6b93f65 +title: Step 54 +challengeType: 0 +dashedName: step-54 +--- + +# --description-- + +Remove the `myTaskArr` array and all of the code for `localStorage` because you don't need them anymore. + +# --hints-- + +You should remove `myTaskArr` and all the code related to `localStorage` that you've just learned. + +```js +assert.notMatch(code, /const\s*myTaskArr\s*=\s*\[\s*\{\s*task:\s"Walk\s*the\s*Dog",\s*date:\s*"22-04-2022"\s*\},\s*\{\s*task:\s"Read\s*some\s*books",\s*date:\s*"02-11-2023"\s*\},\s*\{\s*task:\s"Watch\s*football",\s*date:\s*"10-08-2021"\s*\},\s*\];?\s*localStorage\.setItem\("data", JSON\.stringify\(myTaskArr\)\);\s*localStorage\.clear\(\);?\s*const\s*getTaskArr\s*=\s*localStorage\.getItem\("data"\)\s*console\.log\(getTaskArr\)\s*const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\("data"\)\);?\s*console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +--fcc-editable-region-- +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +localStorage.clear(); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md new file mode 100644 index 00000000000..cbbcd4df6e1 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md @@ -0,0 +1,404 @@ +--- +id: 65003986d17d1e1865b269c0 +title: Step 55 +challengeType: 0 +dashedName: step-55 +--- + +# --description-- + +Now you should save the task items to local storage when the user adds, updates, or removes a task. + +Inside the `addOrUpdateTask` function, use `setItem()` to save the tasks with a key of `data`, then pass the `taskData` array as its parameter. Ensure that you stringify the `taskData`. + +This would persist data once the user adds or updates tasks. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +assert.match(code, /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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; + } + --fcc-editable-region-- + + --fcc-editable-region-- + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md new file mode 100644 index 00000000000..ec3a700096a --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md @@ -0,0 +1,407 @@ +--- +id: 650046832f92c01a35834bca +title: Step 56 +challengeType: 0 +dashedName: step-56 +--- + +# --description-- + +You also want a deleted task to be removed from local storage. For this, you don't need the `removeItem()` or `clear()` methods. Since you already use `splice()` to remove the deleted task from `taskData`, all you need to do now is save `taskData` to local storage again. + +Use `setItem()` to save the `taskData` array again. Pass in `data` as the key and ensure that `taskData` is stringified before saving. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); + --fcc-editable-region-- + + --fcc-editable-region-- +} + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md new file mode 100644 index 00000000000..40cf6384c4e --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md @@ -0,0 +1,392 @@ +--- +id: 650048b0764f9c1b798200e2 +title: Step 57 +challengeType: 0 +dashedName: step-57 +--- + +# --description-- + +If you add, update, or remove a task, it should reflect in the UI. However, that's not happening now because you have yet to retrieve the tasks. To do this, you need to modify your initial `taskData` to be an empty array. + +Set `taskData` to the retrieval of `data` from local storage **or** an empty array. Make sure you parse the data coming with `JSON.parse()` because you saved it as a string. + +# --hints-- + +You should set the `taskData` variable to `JSON.parse(localStorage.getItem("data")) || [];`. + +```js +assert.match(code, /const\s*taskData\s*=\s*JSON.parse\(localStorage\.getItem\(('|")data\1\)\)\s*\|\|\s*\[\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- +const taskData = []; +--fcc-editable-region-- +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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md new file mode 100644 index 00000000000..08ced41b63f --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md @@ -0,0 +1,776 @@ +--- +id: 65004ba581d03d1d5628b41c +title: Step 58 +challengeType: 0 +dashedName: step-58 +--- + +# --description-- + +You've retrieved the task item(s) now, but they still don't reflect in the UI when the page loads. However, they appear when you add a new task. + +To fix this issue, you can check if there's a task inside `taskData`, and then call the `updateTaskContainer()`. + +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 the condition `taskData.length`. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*/) +``` + +You should call the `updateTaskContainer` function in your `if` statement. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*updateTaskContainer\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + taskForm.classList.toggle("hidden"); +} + +const reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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 + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md new file mode 100644 index 00000000000..6b21e6888ce --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md @@ -0,0 +1,314 @@ +--- +id: 650300a25b6f72964ab8aca6 +title: Step 13 +challengeType: 0 +dashedName: step-13 +--- + +# --description-- + +To make the `id` more unique, add another hyphen and use `Date.now()`. + +# --hints-- + +You should attach `-${Date.now()}` to the existing value of the `id` key. Don't forget it needs to be inside the template string. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + const taskObj = { + id: `${titleInput.value.toLowerCase().split(' ').join('-')}`, + }; + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md new file mode 100644 index 00000000000..0c9fbc152b7 --- /dev/null +++ b/curriculum/challenges/portuguese/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md @@ -0,0 +1,337 @@ +--- +id: 65099dbd8f137d58e5c0ff16 +title: Step 21 +challengeType: 0 +dashedName: step-21 +--- + +# --description-- + +Create one more `p` element and interpolate the `description` you destructured as the text. Also, create a `strong` element inside the paragraph with the text `Description:`. + +# --hints-- + +You should create a `p` element with `${description}` as the text. + +```js +assert.match(code, /

.*\$\{description\}<\/p>/) +``` + +You should create a `strong` element with the text `Description:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Description:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md b/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md index 849cd1538ef..8475064fe8f 100644 --- a/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md +++ b/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md @@ -71,8 +71,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md b/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md index 0344e197793..cb5fd141084 100644 --- a/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md +++ b/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md @@ -73,7 +73,7 @@ substringDivisibility(5); ```js function substringDivisibility(n) { - function isSubDivisable(digits) { + function isSubDivisible(digits) { const factors = [2, 3, 5, 7, 11, 13, 17]; for (let i = 1; i < digits.length - 2; i++) { @@ -108,17 +108,17 @@ function substringDivisibility(n) { } const allowedDigits = [...new Array(n + 1).keys()]; - const divisablePandigitals = []; + const divisiblePandigitals = []; heapsPermutations( allowedDigits.length, allowedDigits, - isSubDivisable, - divisablePandigitals + isSubDivisible, + divisiblePandigitals ); let sum = 0; - for (let i = 0; i < divisablePandigitals.length; i++) { - sum += divisablePandigitals[i]; + for (let i = 0; i < divisiblePandigitals.length; i++) { + sum += divisiblePandigitals[i]; } return sum; diff --git a/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md b/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md index 804942e0ca8..80c9cfe496f 100644 --- a/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md +++ b/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md @@ -67,8 +67,8 @@ class PrimeSeive { const prime = 2 * i + 3; primes.push(prime); // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } @@ -101,10 +101,10 @@ class PrimeSeive { }; function consecutivePrimeSum(limit) { - // Initalize seive + // Initialize seive const primeSeive = new PrimeSeive(limit); - // Initalize for longest sum < 100 + // Initialize for longest sum < 100 let bestPrime = 41; let bestI = 0; let bestJ = 5; diff --git a/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md b/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md index 67902e726f7..c1318616698 100644 --- a/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md +++ b/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md b/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md index 416c0ce83ba..d4b28104753 100644 --- a/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md +++ b/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md @@ -31,10 +31,10 @@ Para $1 ≤ k ≤ 200$, encontre $\sum{m(k)}$. # --hints-- -`efficientExponentation()` deve retornar `1582`. +`efficientExponentiation()` deve retornar `1582`. ```js -assert.strictEqual(efficientExponentation(), 1582); +assert.strictEqual(efficientExponentiation(), 1582); ``` # --seed-- @@ -42,12 +42,12 @@ assert.strictEqual(efficientExponentation(), 1582); ## --seed-contents-- ```js -function efficientExponentation() { +function efficientExponentiation() { return true; } -efficientExponentation(); +efficientExponentiation(); ``` # --solutions-- diff --git a/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md b/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md index e3f062a4476..1cf4e28674a 100644 --- a/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md +++ b/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md b/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md index 5345442fb3a..1461cf39882 100644 --- a/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md +++ b/curriculum/challenges/portuguese/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md @@ -35,7 +35,7 @@ concealedSquare(); ```js // Check if n**2 matches the pattern -function squareMatchs(n) { +function squareMatches(n) { // Need BigInt due to size of values let nSquared = (BigInt(n) * BigInt(n)).toString(); @@ -55,8 +55,8 @@ function concealedSquare() { for (let x = maxSquareRoot; x >= minSquareRoot; x -= 10) { // Note: 3*3 = 9 and 7*7 = 49 are only trailing digits // that can produce 9 as trailing digit in square - if (squareMatchs(x + 3)) return (x + 3)*10; - if (squareMatchs(x + 7)) return (x + 7)*10; + if (squareMatches(x + 3)) return (x + 3)*10; + if (squareMatches(x + 7)) return (x + 7)*10; } return -1; } diff --git a/curriculum/challenges/swahili/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md b/curriculum/challenges/swahili/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md index e623722d560..0bc96b009ad 100644 --- a/curriculum/challenges/swahili/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md +++ b/curriculum/challenges/swahili/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md @@ -54,7 +54,24 @@ assert.match(code, /myStorage\.car\.inside/); `gloveBoxContents` should still be declared with `const`. ```js -assert.match(code, /const\s+gloveBoxContents\s*=\s*myStorage\.car\.inside\[\s*("|')glove box\1\s*\]|const\s*{\s*('|")glove box\2:\s*gloveBoxContents\s*}\s*=\s*myStorage\.car\.inside;/); +assert.match(code, /const\s+gloveBoxContents\s*=/); +``` + +You should not change the `myStorage` object. + +```js +const expectedMyStorage = { + "car":{ + "inside":{ + "glove box":"maps", + "passenger seat":"crumbs" + }, + "outside":{ + "trunk":"jack" + } + } +}; +assert.deepStrictEqual(myStorage, expectedMyStorage); ``` # --seed-- diff --git a/curriculum/challenges/swahili/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md b/curriculum/challenges/swahili/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md index 52e8b72c2d0..c0795c6ea6f 100644 --- a/curriculum/challenges/swahili/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md +++ b/curriculum/challenges/swahili/02-javascript-algorithms-and-data-structures/debugging/catch-misspelled-variable-and-function-names.md @@ -10,7 +10,7 @@ dashedName: catch-misspelled-variable-and-function-names The `console.log()` and `typeof` methods are the two primary ways to check intermediate values and types of program output. Now it's time to get into the common forms that bugs take. One syntax-level issue that fast typers can commiserate with is the humble spelling error. -Transposed, missing, or mis-capitalized characters in a variable or function name will have the browser looking for an object that doesn't exist - and complain in the form of a reference error. JavaScript variable and function names are case-sensitive. +Transposed, missing, or miscapitalized characters in a variable or function name will have the browser looking for an object that doesn't exist - and complain in the form of a reference error. JavaScript variable and function names are case-sensitive. # --instructions-- diff --git a/curriculum/challenges/swahili/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md b/curriculum/challenges/swahili/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md index f520f02317f..4b8cc9e97fe 100644 --- a/curriculum/challenges/swahili/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md +++ b/curriculum/challenges/swahili/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md @@ -58,8 +58,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/swahili/06-quality-assurance/quality-assurance-projects/issue-tracker.md b/curriculum/challenges/swahili/06-quality-assurance/quality-assurance-projects/issue-tracker.md index 406e4ebaa93..b049277a0a6 100644 --- a/curriculum/challenges/swahili/06-quality-assurance/quality-assurance-projects/issue-tracker.md +++ b/curriculum/challenges/swahili/06-quality-assurance/quality-assurance-projects/issue-tracker.md @@ -231,13 +231,13 @@ async (getUserInput) => { }; const url = getUserInput('url') + '/api/issues/fcc-project'; const itemToUpdate = await $.post(url, initialData); - const updateSucccess = await $.ajax({ + const updateSuccess = await $.ajax({ url: url, type: 'PUT', data: { _id: itemToUpdate._id, issue_text: 'New Issue Text' } }); - assert.isObject(updateSucccess); - assert.deepEqual(updateSucccess, { + assert.isObject(updateSuccess); + assert.deepEqual(updateSuccess, { result: 'successfully updated', _id: itemToUpdate._id }); diff --git a/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/24-game.md b/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/24-game.md index ac16df78509..88cdeed88e7 100644 --- a/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/24-game.md +++ b/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/24-game.md @@ -82,7 +82,7 @@ const OPERATORS_ = { "/": (a, b) => a / b, } -const PRECIDENCE_ = { +const PRECEDENCE_ = { "+": 1, "-": 1, "*": 2, @@ -114,7 +114,7 @@ function evaluate_(expression) { case "*": case "/": while (stack.length && - PRECIDENCE_[c] <= PRECIDENCE_[stack[stack.length-1]]) { + PRECEDENCE_[c] <= PRECEDENCE_[stack[stack.length-1]]) { postfix += stack.pop(); } stack.push(c); diff --git a/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md b/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md index b6a78ff636a..46b65350213 100644 --- a/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md +++ b/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md @@ -175,7 +175,7 @@ function dealFreeCell(seed) { const rng = FreeCellRNG(seed); const deck = getDeck(); - const deltCards = [[], [], [], [], [], [], []]; + const dealtCards = [[], [], [], [], [], [], []]; let currentColumn = 0; let currentRow = 0; @@ -195,7 +195,7 @@ function dealFreeCell(seed) { card = deck.pop(); // Deal this card - deltCards[currentRow].push(card); + dealtCards[currentRow].push(card); currentColumn += 1; if (currentColumn === 8) { currentColumn = 0; @@ -203,6 +203,6 @@ function dealFreeCell(seed) { } } - return deltCards; + return dealtCards; } ``` diff --git a/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/department-numbers.md b/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/department-numbers.md index 1117963ac08..5414cdcf18d 100644 --- a/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/department-numbers.md +++ b/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/department-numbers.md @@ -101,7 +101,7 @@ function combinations(possibleNumbers, total) { function combinations(possibleNumbers, total) { let firstNumber; let secondNumber; - let thridNumber; + let thirdNumber; const allCombinations = []; for (let i = 0; i < possibleNumbers.length; i += 1) { @@ -112,10 +112,10 @@ function combinations(possibleNumbers, total) { secondNumber = possibleNumbers[j]; if (j !== i && firstNumber + secondNumber <= total) { - thridNumber = total - firstNumber - secondNumber; + thirdNumber = total - firstNumber - secondNumber; - if (thridNumber !== firstNumber && thridNumber !== secondNumber && possibleNumbers.includes(thridNumber)) { - allCombinations.push([firstNumber, secondNumber, thridNumber]); + if (thirdNumber !== firstNumber && thirdNumber !== secondNumber && possibleNumbers.includes(thirdNumber)) { + allCombinations.push([firstNumber, secondNumber, thirdNumber]); } } } diff --git a/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/lu-decomposition.md b/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/lu-decomposition.md index 45759df22aa..9255204dd79 100644 --- a/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/lu-decomposition.md +++ b/curriculum/challenges/swahili/10-coding-interview-prep/rosetta-code/lu-decomposition.md @@ -80,7 +80,7 @@ $PA = LU$ # --instructions-- -The task is to implement a routine which will take a square nxn matrix $A$ and return a lower triangular matrix $L$, a upper triangular matrix $U$ and a permutation matrix $P$, so that the above equation is fullfilled. The returned value should be in the form `[L, U, P]`. +The task is to implement a routine which will take a square nxn matrix $A$ and return a lower triangular matrix $L$, a upper triangular matrix $U$ and a permutation matrix $P$, so that the above equation is fulfilled. The returned value should be in the form `[L, U, P]`. # --hints-- diff --git a/curriculum/challenges/swahili/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md b/curriculum/challenges/swahili/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md index cb0f4114095..89ad0ef94ae 100644 --- a/curriculum/challenges/swahili/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md +++ b/curriculum/challenges/swahili/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md @@ -9,7 +9,7 @@ dashedName: step-82 Mdomo na miguu ya pengwini hushiriki `color` sawa. -Unda kigezo kipya maalum cha CSS kiitwacho `--penguin-picorna`, na ubadilishe thamani zote za sifa zinazohusiana nayo. +Create a new custom CSS variable named `--penguin-picorna`, and replace all relevant property values with it. # --hints-- diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md index c335f2d7bc9..a1c1c26f120 100644 --- a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md @@ -11,7 +11,7 @@ Inside `pick`, use `let` to initialize a variable named `numbers` and set it to # --hints-- -Your `pick` function should initalize `numbers` to an empty array `[]`. +Your `pick` function should initialize `numbers` to an empty array `[]`. ```js assert.match(pick.toString(), /numbers\s*=\s*\[\]/); diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md new file mode 100644 index 00000000000..4b042aa87ba --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md @@ -0,0 +1,310 @@ +--- +id: 64e4e4c4ec263b62ae7bf54d +title: Step 1 +challengeType: 0 +dashedName: step-1 +--- + +# --description-- + +In this project, you will learn how `localStorage` works in JavaScript by building a Todo app. LocalStorage is a web storage feature of JavaScript that lets you persist data by storing the data as a **key:value** pair. + +The HTML and CSS for this project have been provided for you. Take a look at the files to get yourself familiarized with them. + +Begin by accessing the `task-form`, `confirm-close-dialog`, and `open-task-form-btn` elements with the `getElementById()` method. Save them in the variables `taskForm`, `confirmCloseDialog`, and `openTaskFormBtn`. + +# --hints-- + +You should use `getElementById()` to access the `task-form` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should assign the `task-form` element to the variable `taskForm`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+taskForm\s*=\s*document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should use `getElementById()` to access the `confirm-close-dialog` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should assign the `confirm-close-dialog` element to the variable `confirmCloseDialog`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+confirmCloseDialog\s*=\s*document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should use `getElementById()` to access the `open-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +You should assign the `open-task-form-btn` element to the variable `openTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+openTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md new file mode 100644 index 00000000000..32310409728 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md @@ -0,0 +1,310 @@ +--- +id: 64e4e6c86954de67a3e44ee3 +title: Step 2 +challengeType: 0 +dashedName: step-2 +--- + +# --description-- + +You need to access more elements with the `getElementById()` method. This time you need the `close-task-form-btn`, `add-or-update-task-btn`, and `cancel-btn` elements. Save them in the variables `closeTaskFormBtn`, `addOrUpdateTaskBtn`, and `cancelBtn`. + +# --hints-- + +You should use `getElementById()` to access the `close-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should assign the `close-task-form-btn` element to the variable `closeTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+closeTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `add-or-update-task-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should assign the `add-or-update-task-btn` element to the variable `addOrUpdateTaskBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+addOrUpdateTaskBtn\s*=\s*document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `cancel-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +You should assign the `cancel-btn` element to the variable `cancelBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+cancelBtn\s*=\s*document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md new file mode 100644 index 00000000000..17fc6b76517 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md @@ -0,0 +1,313 @@ +--- +id: 64e4e6fe78b5aa67ef2fc3e7 +title: Step 3 +challengeType: 0 +dashedName: step-3 +--- + +# --description-- + +Next, access the `discard-btn`, `tasks-container`, and `title-input` elements using the `getElementById()` method. Save them in variables named `discardBtn`, `tasksContainer`, and `titleInput`, respectively. + +# --hints-- + +You should use `getElementById()` to access the `discard-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should assign the `discard-btn` element to the variable `discardBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+discardBtn\s*=\s*document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `tasks-container` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should assign the `tasks-container` element to the variable `tasksContainer`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+tasksContainer\s*=\s*document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should use `getElementById()` to access the `title-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +You should assign the `title-input` element to the variable `titleInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+titleInput\s*=\s*document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md new file mode 100644 index 00000000000..6e543d36acc --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md @@ -0,0 +1,304 @@ +--- +id: 64e4e7241f52bb682eeb8211 +title: Step 4 +challengeType: 0 +dashedName: step-4 +--- + +# --description-- + +The last set of elements you need to get from the HTML file are the `date-input` and `description-input` elements. Save them in the variables `dateInput` and `descriptionInput` respectively. + +# --hints-- + +You should use `getElementById()` to access the `date-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should assign the `date-input` element to the variable `dateInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+dateInput\s*=\s*document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should use `getElementById()` to access the `description-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +You should assign the `description-input` element to the variable `descriptionInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+descriptionInput\s*=\s*document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md new file mode 100644 index 00000000000..6f0cd6e9fc4 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md @@ -0,0 +1,296 @@ +--- +id: 64e4e74d0fb4f0687bf4145d +title: Step 5 +challengeType: 0 +dashedName: step-5 +--- + +# --description-- + +Create a `taskData` constant and set it to an empty array. This array will store all the tasks along with their associated data, including title, due date, and description. This storage will enable you to keep track of tasks, display them on the page, and save them to `localStorage`. + +Use `let` to create a `currentTask` variable and set it to an empty object. This variable will be used to track the state when editing and discarding tasks. + +# --hints-- + +You should create a `taskData` constant set to an empty array. + +```js +assert.match(code, /const\s+taskData\s*=\s*\[\];?/) +``` + +You should use `let` to create an empty `currentTask` object. + +```js +assert.match(code, /let\s+currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md new file mode 100644 index 00000000000..7eba2896bf4 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md @@ -0,0 +1,307 @@ +--- +id: 64e4e78a7ea4a168de4e6a38 +title: Step 6 +challengeType: 0 +dashedName: step-6 +--- + +# --description-- + +Now, you will work on opening and closing the form modal. + +Add a `click` event listener to the `openTaskFormBtn` variable, and then use `classList` to toggle the `hidden` class on the `taskForm` element. + +Now you can click on the "Add new Task" button and see the form modal. + +# --hints-- + +You should call the `addEventListener()` method on your `openTaskFormBtn` variable. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax, then use `classList` property and it's `toggle` method on the `taskForm`, to toggle the class `hidden`. Don't use curly braces. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*taskForm\.classList\.toggle\(\1hidden\1\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md new file mode 100644 index 00000000000..6a29158134e --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md @@ -0,0 +1,309 @@ +--- +id: 64e4e7bbedb22d6939001ad3 +title: Step 7 +challengeType: 0 +dashedName: step-7 +--- + +# --description-- + +Add a `click` event listener to the `closeTaskFormBtn` variable, then use the `showModal()` method on your `confirmCloseDialog` variable. This will display a modal with the `Discard` and `Cancel` buttons. + +`showModal()` is a method associated with the HTML `dialog` element. It is used to display a modal dialog box on a web page. + +# --hints-- + +You should call the `addEventListener()` method on your `closeTaskFormBtn` variable. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `showModal()` method on `confirmCloseDialog`. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(["'`]click["'`],\s*\(.*\)\s*=>\s*{?\s*confirmCloseDialog\.showModal\(\);?\s*}?\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md new file mode 100644 index 00000000000..311984043a0 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md @@ -0,0 +1,316 @@ +--- +id: 64e4eaaa9070a66aecbfe603 +title: Step 8 +challengeType: 0 +dashedName: step-8 +--- + +# --description-- + +If the user clicks the `Cancel` button, you want to cancel the process (close the modal showing the two buttons) so the user can continue editing. + +Add a `click` event listener to the `cancelBtn` element, then use the `close()` method on the `confirmCloseDialog` variable. + +`close()` is a method of the window object you can use to close the current window, or a modal you create with the `dialog` element. + +# --hints-- + +You should call the `addEventListener()` method on your `cancelBtn` variable. + +```js +assert.match(code, /cancelBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `close()` method on `confirmCloseDialog`. Don't use curly braces. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*confirmCloseDialog\.close\(\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- + +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md new file mode 100644 index 00000000000..66a40c9771b --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md @@ -0,0 +1,327 @@ +--- +id: 64e4ebc7eabc5a6babd479cd +title: Step 9 +challengeType: 0 +dashedName: step-9 +--- + +# --description-- + +If the user clicks the `Discard` button, you want to close the modal showing the `Cancel` and `Discard` buttons, then hide the form modal. + +Add a click event listener to `discardBtn`, then use the `close()` method on the `confirmCloseDialog` variable. Also, use `classList` to toggle the class `hidden` on `taskForm` so the form modal will close too. + +# --hints-- + +You should call the `addEventListener()` method on your `discardBtn` variable. + +```js +assert.match(code, /discardBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{/) +``` + +Your event listener should use the `close()` method on `confirmCloseDialog`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?/) +``` + +Your event listener should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?\s*taskForm\.classList\.toggle\(\1hidden\1\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md new file mode 100644 index 00000000000..f4002dc0937 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md @@ -0,0 +1,326 @@ +--- +id: 64e4ecd7735a566c9266a338 +title: Step 10 +challengeType: 0 +dashedName: step-10 +--- + +# --description-- + +Now that you've worked on opening and closing the modal, it's time to get the values from the input fields, save them into the `taskData` array, and display them on the page. + +To start, add a `submit` event listener to your `taskForm` element and pass in `e` as the parameter of your arrow function. Inside the curly braces, use the `preventDefault()` method to stop the browser from refreshing the page after submitting the form. + +# --hints-- + +You should call the `addEventListener()` method on your `taskForm` variable. + +```js +assert.match(code, /taskForm\.addEventListener\(/) +``` + +Your event listener should listen for a `submit` event. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces with `e` as the parameter. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{/) +``` + +You should use the `e.preventDefault()` method to stop the browser from reloading the page. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{\s*e\.preventDefault\(\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md new file mode 100644 index 00000000000..148e52f3e93 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md @@ -0,0 +1,337 @@ +--- +id: 64e4eec13546c06d61a63d59 +title: Step 11 +challengeType: 0 +dashedName: step-11 +--- + +# --description-- + +You need to determine whether the task being added already exists or not. If it doesn't exist, you will add it, and if it already exists, you will set it up for editing. You can use the `findIndex()` method to accomplish this. + +`findIndex` is an array method that lets find the index of the first element in an array that satisfies a given testing function. + +Here's an example: + +```js +const numbers = [3, 1, 5, 6, 10, 9, 8]; +const firstEvenNumIndex = numbers.findIndex((num) => num % 2 === 0); + +console.log(firstEvenNumIndex); // Output: 3 – because the first even number (6) is at index 3 +``` + +Declare a `dataArrIndex` variable using `const` and set it to the result of the `findIndex()` method applied to the `taskData` array. Utilize arrow syntax to provide a callback function with `item` as the parameter, and within the callback, check if the `id` property of `item` is equal to the `id` property of `currentTask`. + +If the task exists, this returns the index, and if it doesn't exist, it returns `-1`. + +# --hints-- + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === currentTask.id`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?\s*=>\s*item.id\s*===\s*currentTask.id\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md new file mode 100644 index 00000000000..10826de6f17 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md @@ -0,0 +1,340 @@ +--- +id: 64e4f01d6c72086e016a8626 +title: Step 12 +challengeType: 0 +dashedName: step-12 +--- + +# --description-- + +Next, retrieve the values from the input fields and store them in a `taskObj` object. Each task should also have a unique `id`. + +Create a `taskObj` object with an `id` property as the first property. For the value of the `id` property, retrieve the value of the `titleInput` field, convert it to lowercase, and then use the `split()` and `join()` methods to hyphenate it. + +Make sure all of those are in template literals because you need the `id` property value as a string. + +# --hints-- + +You should use `const` to create a `taskObj` object. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*/) +``` + +Your `taskObj` object should have a key of `id`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id/) +``` + +You should use template strings to get `titleInput.value` as the value of your `id` key and convert it to lowercase. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)/) +``` + +You should use `.split(' ')` on `titleInput.value.toLowerCase()`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)/) +``` + +You should use `.join('-')` on `titleInput.value.toLowerCase().split(' ')`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md new file mode 100644 index 00000000000..764de1b0918 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md @@ -0,0 +1,327 @@ +--- +id: 64ec89ee549ecf802de2b3e2 +title: Step 14 +challengeType: 0 +dashedName: step-14 +--- + +# --description-- + +Retrieve the values from the `titleInput`, `dateInput`, and `descriptionInput` fields, and then save them in the properties `title`, `date`, and `description` of the `taskObj` object. + +# --hints-- + +You should get the value of `titleInput` and save it in a `title` key. + +```js +assert.match(code, /title:\s*titleInput\.value,/) +``` + +You should get the value of `dateInput` and save it in a `date` key. + +```js +assert.match(code, /date:\s*dateInput\.value,/) +``` + +You should get the value of `descriptionInput` and save it in a `description` key. + +```js +assert.match(code, /description:\s*descriptionInput\.value,?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + const taskObj = { + id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`, + --fcc-editable-region-- + + --fcc-editable-region-- + }; +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md new file mode 100644 index 00000000000..3d009188d81 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md @@ -0,0 +1,332 @@ +--- +id: 64ec8f717b261e824d82d6a5 +title: Step 15 +challengeType: 0 +dashedName: step-15 +--- + +# --description-- + +Now that you have obtained the values from the input fields and generated an `id`, you want to add them to your `taskData` array to keep track of each task. However, you should only do this if the task is new. If the task already exists, you will set it up for editing. This is why you have the `dataArrIndex` variable, which provides the index of each task. + +Create an `if` statement with the condition `dataArrIndex === -1`. Within the `if` statement, use the `unshift()` method to add the `taskObj` object to the beginning of the `taskData` array. + +`unshift()` is an array method that is used to add one or more elements to the beginning of an array. + +**N.B:** Try adding a task and log `taskData` to the console to see what it looks like. + +# --hints-- + +You should create an `if` statement with the condition `dataArrIndex === -1`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{/) +``` + +Your `if` statement should have `taskData.unshift(taskObj)` in it's body. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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, + }; + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md new file mode 100644 index 00000000000..f1202bb93fe --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md @@ -0,0 +1,331 @@ +--- +id: 64ec9145e424d8835a4e0f28 +title: Step 16 +challengeType: 0 +dashedName: step-16 +--- + +# --description-- + +Now that you have saved the task in the `taskData` array, you should display the task on the page by looping through it. + +Use `forEach()` on `taskData`, then destructure `id`, `title`, `date`, `description` as the parameters. Don't return anything yet. + +# --hints-- + +You should use `forEach()` on `taskData`. + +```js +assert.match(code, /taskData\.forEach\(\s*/) +``` + +You should destructure `id`, `title`, `date`, `description` as the parameters of the callback of your `forEach()`. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md new file mode 100644 index 00000000000..06bdd7c6ae5 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md @@ -0,0 +1,336 @@ +--- +id: 64ec9282cd547785258cecf2 +title: Step 17 +challengeType: 0 +dashedName: step-17 +--- + +# --description-- + +Using arrow syntax, create an implicit return and use addition assignment to set the `innerHTML` of `tasksContainer` to empty backticks. + +# --hints-- + +You should not alter the existing `taskData.forEach()` and the values you destructured. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +You should use arrow syntax and attach `innerHTML` to `tasksContainer`. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\(\s*tasksContainer\.innerHTML\s*\))?/) +``` + +You should use addition assignment to set the `innerHTML` of `tasksContainer` to an empty pair of backticks. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\s*\(?tasksContainer\.innerHTML\s*\+=\s*`\s*`\)?)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + taskData.forEach(({id, title, date, description})); + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md new file mode 100644 index 00000000000..b5115f99a51 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md @@ -0,0 +1,333 @@ +--- +id: 64ec9343769e8f85c1e17e05 +title: Step 18 +challengeType: 0 +dashedName: step-18 +--- + +# --description-- + +Create a `div` element with the class of `task`. Utilize template strings to set the `id` attribute of the `div` to the `id` you destructured from the task data. + +# --hints-- + +You should create a `div` element with the class `task`. + +```js +assert.match(code, /\s*<\/div>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` + --fcc-editable-region-- + + --fcc-editable-region-- + `) + ); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md new file mode 100644 index 00000000000..81dd3530420 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md @@ -0,0 +1,348 @@ +--- +id: 64ec94f0de20c086e09b0fc3 +title: Step 19 +challengeType: 0 +dashedName: step-19 +--- + +# --description-- + +Create a `p` element and use template strings to set its content to the `title` you destructured. Right before the content of the `p` element, create a `strong` element with the text `Title:`. + +# --hints-- + +You should create a `p` element. + +```js +assert.match(code, /

/) +``` + +You should interpolate `${title}` as the text of your `p` element. + +```js +assert.match(code, /

.*\$\{title\}<\/p>/) +``` + +You should create a `strong` element after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)/) +``` + +Your `strong` element should have the text `Title:`. + +```js +assert.match(code, /(?<=

)Title:\s*<\/strong>\s*/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md new file mode 100644 index 00000000000..4820d100d4d --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md @@ -0,0 +1,336 @@ +--- +id: 64ec959a76336c8767f5cd4d +title: Step 20 +challengeType: 0 +dashedName: step-20 +--- + +# --description-- + +Similarly to the previous step, create another `p` element, and interpolate the `date` you destructured as the text content. Inside this paragraph, create a `strong` element with the text `Date:`. + +# --hints-- + +You should create a `p` element and interpolate `${date}` as the text. + +```js +assert.match(code, /

.*\$\{date\}<\/p>/) +``` + +You should create a `strong` element with the text `Date:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Date:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md new file mode 100644 index 00000000000..4525b0e154c --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md @@ -0,0 +1,340 @@ +--- +id: 64ec96761156a187ed32b274 +title: Step 22 +challengeType: 0 +dashedName: step-22 +--- + +# --description-- + +To allow for task management, you need to include both a delete and an edit button for each task. + +Create two `button` elements with the `type` attribute set to `button` and the `class` attribute set to `btn`. Set the text of the first button to `Edit` and the text of the second button to `Delete`. + +# --hints-- + +You should create a `button` element of type `button`, a class `btn` and `Edit` as the text, in that order. + +```js +assert.match(code, /Edit<\/button/) +``` + +You should create a `button` element of type `button` a class `btn` and `Delete` as the text, in that order. + +```js +assert.match(code, /Delete<\/button/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md new file mode 100644 index 00000000000..0081fa88e1a --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md @@ -0,0 +1,336 @@ +--- +id: 64ec9b10356c2d8aa05d9ce1 +title: Step 23 +challengeType: 0 +dashedName: step-23 +--- + +# --description-- + +After adding the task to the page, you should close the form modal to view the task. To do this, utilize `classList` to toggle the `hidden` class on the `taskForm` element. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +const splitter = code.split("taskData.forEach") +assert.match(splitter[1], /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md new file mode 100644 index 00000000000..6ae1e4dff48 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md @@ -0,0 +1,347 @@ +--- +id: 64ec9c55fdeef78bacd2fc3b +title: Step 24 +challengeType: 0 +dashedName: step-24 +--- + +# --description-- + +If you attempt to add another task now, you'll notice that the input fields retain the values you entered for the previous task. To resolve this, you need to clear the input fields after adding a task. + +Instead of clearing the input fields one by one, it's a good practice to create a function that handles clearing those fields. You can then call this function whenever you need to clear the input fields again. + +Use arrow syntax to create a `reset` function and set it to a pair of curly braces. + +# --hints-- + +You should use `const` and arrow syntax to create a `reset` function. + +```js +assert.match(code, /const\s+reset\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +Your `reset` function should be empty. + +```js +assert.match(reset.toString(), /\(\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md new file mode 100644 index 00000000000..e12fb12ab81 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md @@ -0,0 +1,366 @@ +--- +id: 64fac365aeb8ad70b69b366f +title: Step 25 +challengeType: 0 +dashedName: step-25 +--- + +# --description-- + +Inside the `reset` function, set each value of `titleInput`, `dateInput`, `descriptionInput` to an empty string. + +Also, use `classList` to toggle the class `hidden` on the `taskForm` and set `currentTask` to an empty object. That's because at this point, `currentTask` will be filled with the task the user might have added. + +# --hints-- + +You should set `titleInput.value` to an empty string. + +```js +assert.match(reset.toString(), /titleInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `dateInput.value` to an empty string. + +```js +assert.match(reset.toString(), /dateInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `descriptionInput.value` to an empty string. + +```js +assert.match(reset.toString(), /descriptionInput\.value\s*=\s*('|")\1;?/) +``` + +You should use `classList` to toggle the class `hidden` on `taskForm` + +```js +assert.match(reset.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +You should set `currentTask` to an empty object. + +```js +assert.match(reset.toString(), /currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- +const reset = () => { + +} +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md new file mode 100644 index 00000000000..5f281d0de16 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md @@ -0,0 +1,351 @@ +--- +id: 64fac4d1773e7a719b1254de +title: Step 26 +challengeType: 0 +dashedName: step-26 +--- + +# --description-- + +Remove the existing code toggling the class of `hidden` on `taskForm` and call the `reset` function instead. This would clear the input fields and also hide the form modal for the user to see the added task. + +# --hints-- + +Yous should remove the code toggling the `hidden` class on `taskForm`. + +```js +const splitter = code.split('') +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /reset\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + +--fcc-editable-region-- + taskForm.classList.toggle("hidden"); +--fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md new file mode 100644 index 00000000000..c1a7b0f8965 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md @@ -0,0 +1,351 @@ +--- +id: 64fac6a497811572b338e5e5 +title: Step 27 +challengeType: 0 +dashedName: step-27 +--- + +# --description-- + +Also, remove the existing code toggling the class `hidden` on `taskForm` inside the `discardBtn` event listener and call the `reset` function instead. That's because when you click the `Discard` button, everything in the input fields should go away. + +# --hints-- + +Yous should remove the code toggling the class `hidden` on `taskForm`. + +```js +const splitter = code.split("confirmCloseDialog.close();") +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /confirmCloseDialog\.close\(\);?\s*reset\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); +--fcc-editable-region-- + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md new file mode 100644 index 00000000000..84005e1b01a --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md @@ -0,0 +1,346 @@ +--- +id: 64faca774fd9fd74bc084cc9 +title: Step 28 +challengeType: 0 +dashedName: step-28 +--- + +# --description-- + +You should display the `Cancel` and `Discard` buttons to the user only if there is some text present in the input fields. + +To begin, within the `closeTaskFormBtn` event listener, create a `formInputsContainValues` variable to check if there is a value in the `titleInput` field **or** the `dateInput` field **or** the `descriptionInput` field. + +# --hints-- + +You should use `const` to create a variable `formInputsContainValues` with the value `titleInput.value || dateInput.value || descriptionInput.value;` + +```js +assert.match(code, /const\s*formInputsContainValues\s*=\s*titleInput\.value\s*\|\|\s*dateInput\.value\s*\|\|\s*descriptionInput\.value;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); + +}); +--fcc-editable-region-- + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md new file mode 100644 index 00000000000..07482464ebf --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md @@ -0,0 +1,362 @@ +--- +id: 64facf6180824876f70a2e86 +title: Step 29 +challengeType: 0 +dashedName: step-29 +--- + +# --description-- + +Create an `if` statement to check if `formInputsContainValues` is true. If `formInputsContainValues` is true, indicating that there are changes, use the `showModal()` method on `confirmCloseDialog`. Otherwise, if there are no changes, call the `reset()` function to clear the input fields and hide the form modal. + +# --hints-- + +You should create an `if` statement with the condition `formInputsContainValues`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{/) +``` + +The `if` block of your `if` statement should contain `confirmCloseDialog.showModal();`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?/) +``` + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{/) +``` + +You should call the `reset()` function in the `else` block of your `if` statement. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{\s*reset\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 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; +--fcc-editable-region-- + confirmCloseDialog.showModal(); +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md new file mode 100644 index 00000000000..95b71fba69f --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md @@ -0,0 +1,372 @@ +--- +id: 64fad07f43a101779cb8692a +title: Step 30 +challengeType: 0 +dashedName: step-30 +--- + +# --description-- + +You can enhance code readability and maintainability by refactoring the `submit` event listener into two separate functions. The first function can be used to add the input values to `taskData`, while the second function can be responsible for adding the tasks to the DOM. + +Use arrow syntax to create an `addOrUpdateTask` function. Then move the `dataArrIndex` variable, the `taskObj` object, and the `if` statement into the `addOrUpdateTask` function. + +# --hints-- + +You should use `const` and arrow syntax to create an `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +You should move the `dataArrIndex` variable into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?/) +``` + +You should move the `taskObj` object into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?/) +``` + +You should move the `if` statement with the condition `dataArrIndex === 1` into your `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?\s*if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md new file mode 100644 index 00000000000..8192699bb61 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md @@ -0,0 +1,358 @@ +--- +id: 64fad9cd2eeb1e7ca2ca8c8b +title: Step 31 +challengeType: 0 +dashedName: step-31 +--- + +# --description-- + +Use arrow syntax to create an `updateTaskContainer` function. Then move the `taskData.forEach()` and its content into it. + +# --hints-- + +You should use `const` and arrow syntax to create a `updateTaskContainer` function. + +```js +assert.match(code, /const\s*updateTaskContainer\s*=\s*\(\)\s*=>\s*\{/) +``` + +You should move `taskData.forEach()` and its content into the `updateTaskContainer()` function. + +```js +assert.match(code, /const\s+updateTaskContainer\s+=\s*\(\)\s*=>\s*\{\s*taskData\.forEach\(\s*\(\{\s*id,\s*title,\s*date,\s*description\s*\}\)\s*=>\s*\(tasksContainer\.innerHTML\s*\+=\s*`\s*\s*

Title:<\/strong>\s*\$\{title\}<\/p>\s*

Date:<\/strong>\s*\$\{date\}<\/p>\s*

Description:<\/strong>\s*\$\{description\}<\/p>\s*Edit<\/button>\s*Delete<\/button>\s*<\/div>\s*`\)\s*\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md new file mode 100644 index 00000000000..18b2a462de4 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md @@ -0,0 +1,360 @@ +--- +id: 64fadae4f2d51b7d5d8b98d8 +title: Step 32 +challengeType: 0 +dashedName: step-32 +--- + +# --description-- + +Inside the `addOrUpdateTask` function, call the `updateTaskContainer` and `reset` functions. + +# --hints-- + +You should call the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\)\s*/) +``` + +You should call the `reset` function after calling the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\);?\s*reset\(\);?\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + reset() +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md new file mode 100644 index 00000000000..657bb4aa05b --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md @@ -0,0 +1,355 @@ +--- +id: 64fadff23375f27ff06c6d40 +title: Step 33 +challengeType: 0 +dashedName: step-33 +--- + +# --description-- + +Now remove the `reset()` call inside the `taskForm` submit event listener and call the `addOrUpdateTask` function instead. + +# --hints-- + +You should remove `reset()` and call `addOrUpdateTask()`. + +```js +assert.match(code, /addOrUpdateTask\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + --fcc-editable-region-- + reset() + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md new file mode 100644 index 00000000000..bd759fb7e5f --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md @@ -0,0 +1,364 @@ +--- +id: 64fae068bcdc9c805bd8399e +title: Step 35 +challengeType: 0 +dashedName: step-35 +--- + +# --description-- + +To enable editing and deleting for each task, add an `onclick` attribute to both buttons. Set the value of the `onclick` attribute to `editTask(this)` for the `Edit` button and `deleteTask(this)` for the `Delete` button. The `editTask(this)` function will handle editing, while the `deleteTask(this)` function will handle deletion. + +`this` is a keyword that refers to the current context. In this case, `this` points to the element that triggers the event – the buttons. + +# --hints-- + +You should add `onclick="editTask(this)"` as the first attribute of the edit button. + +```js +assert.match(code, /Edit<\/button>/) +``` + +You should add `onclick="deleteTask(this)"` as the first attribute of the delete button. + +```js +assert.match(code, /Delete<\/button>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + + --fcc-editable-region-- +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md new file mode 100644 index 00000000000..c6fac350818 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md @@ -0,0 +1,371 @@ +--- +id: 64faedcd16a1e985c4c2dc94 +title: Step 36 +challengeType: 0 +dashedName: step-36 +--- + +# --description-- + +Create a `deleteTask` function using arrow syntax. Pass `buttonEl` as the parameter and define an empty set of curly braces for the function body. + +# --hints-- + +You should use `const` and arrow syntax to create a `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `deleteTask` function should take a `buttonEl` parameter. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `deleteTask` function should be empty. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md new file mode 100644 index 00000000000..27631c77f2f --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md @@ -0,0 +1,359 @@ +--- +id: 64faf0418e828c0114a558a7 +title: Step 34 +challengeType: 0 +dashedName: step-34 +--- + +# --description-- + +There's a problem. If you add a task, and then add another, the previous task gets duplicated. This means you need to clear out the existing contents of `tasksContainer` before adding a new task. + +Set the `innerHTML` of `taskContainer` back to an empty string. + + +# --hints-- + +You should set the `innerHTML` of `tasksContainer` to an empty string. + +```js +assert.match(code, /tasksContainer\.innerHTML\s*=\s*("|')\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + --fcc-editable-region-- + + --fcc-editable-region-- + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md new file mode 100644 index 00000000000..9cdbd7860ca --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md @@ -0,0 +1,382 @@ +--- +id: 64faf65b22ad8d07df9be14d +title: Step 37 +challengeType: 0 +dashedName: step-37 +--- + +# --description-- + +You need to find the index of the task you want to delete first. + +Create a `dataArrIndex` variable and set its value using the `findIndex()` method on the `taskData` array. Pass `item` as the parameter for the arrow callback function, and within the callback, check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. Don't use curly braces. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?\s*item\s*\)?\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\s*\);?\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md new file mode 100644 index 00000000000..0e54adadf06 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md @@ -0,0 +1,398 @@ +--- +id: 64faf874364ec308f875f636 +title: Step 38 +challengeType: 0 +dashedName: step-38 +--- + +# --description-- + +You need to remove the task from the DOM using `remove()` and from the `taskData` array using `splice()`. + +`splice()` is an array method that modifies arrays by removing, replacing, or adding elements at a specified index, while also returning the removed elements. It can take up to three parameters: the first one is the mandatory index at which to start, the second is the number of items to remove, and the third is an optional replacement element. Here's an example: + +```js +const fruits = ["mango", "date", "cherry", "banana", "apple"]; + +// Remove date and cherry from the array starting at index 1 +const removedFruits = fruits.splice(1, 2); + +console.log(fruits); // [ 'mango', 'banana', 'apple' ] +console.log(removedFruits); // [ 'date', 'cherry' ] +``` + +Use the `remove()` method to remove the `parentElement` of the `buttonEl` from the DOM. Then use `splice()` to remove the task from the `taskData` array. Pass in `dataArrIndex` and `1` as the parameters of your `splice()`. + +`dataArrIndex` is the index to start and `1` is the number of items to remove. + +# --hints-- + +You should use the `remove()` method to remove the parent element of `buttonEl`. + +```js +assert.match(deleteTask.toString(), /buttonEl\.parentElement\.remove\(\);?/) +``` + +You should use `splice()` on the `taskData` array. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(/) +``` + +The first parameter of your `splice()` method should be `dataArrIndex`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex/) +``` + +The second parameter of your `splice()` method should be `1`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex,\s*1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md new file mode 100644 index 00000000000..3f379d51764 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md @@ -0,0 +1,381 @@ +--- +id: 64fafac95328110a69bcb75f +title: Step 39 +challengeType: 0 +dashedName: step-39 +--- + +# --description-- + +Use arrow syntax to create an `editTask` function. Pass in `buttonEl` as the parameter and add empty curly braces for the body. + +# --hints-- + +You should use `const` and arrow syntax to create an `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `editTask` function should take a `buttonEl` parameter. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `editTask` function should be empty. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md new file mode 100644 index 00000000000..bc0e2a84503 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md @@ -0,0 +1,391 @@ +--- +id: 64fb0fa0968f2b113b2d90e9 +title: Step 40 +challengeType: 0 +dashedName: step-40 +--- + +# --description-- + +As you did in the `deleteTask` function, you need to find the index of the task to be edited. + +Create a `dataArrIndex` variable. For its value, utilize the `findIndex()` method on `taskData`. Pass `item` as the parameter to its callback function and check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md new file mode 100644 index 00000000000..14d527ecf40 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md @@ -0,0 +1,373 @@ +--- +id: 64fb1061ca838611ed6a7d6b +title: Step 41 +challengeType: 0 +dashedName: step-41 +--- + +# --description-- + +Use square bracket notation to retrieve the task to be edited from the `taskData` array using the `dataArrIndex`. Then, assign it to the `currentTask` object to keep track of it. + +# --hints-- + +You should assign `taskData[dataArrIndex]` to `currentTask`. + +```js +assert.match(code, /currentTask\s*=\s*taskData\[dataArrIndex\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex((item) => item.id === buttonEl.parentElement.id); + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md new file mode 100644 index 00000000000..73ef19befcc --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md @@ -0,0 +1,389 @@ +--- +id: 64fb1321e189a6136d200f77 +title: Step 42 +challengeType: 0 +dashedName: step-42 +--- + +# --description-- + +The task to be edited is now in the `currentTask` object. Stage it for editing inside the input fields by setting the value of `titleInput` to `currentTask.title`, `dateInput` to `currentTask.date`, and `descriptionInput` to `currentTask.description`. + +# --hints-- + +You should set `titleInput.value` to `currentTask.title`. + +```js +assert.match(editTask.toString(), /titleInput\.value\s*=\s*currentTask\.title;?/) +``` + +You should set `dateInput.value` to `currentTask.date`. + +```js +assert.match(editTask.toString(), /dateInput\.value\s*=\s*currentTask\.date;?/) +``` + +You should set `descriptionInput.value` to `currentTask.description`. + +```js +assert.match(editTask.toString(), /descriptionInput\.value\s*=\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + currentTask = taskData[dataArrIndex]; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md new file mode 100644 index 00000000000..c6312bf6bd0 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md @@ -0,0 +1,386 @@ +--- +id: 64fb1436adef3e145b4c3501 +title: Step 43 +challengeType: 0 +dashedName: step-43 +--- + +# --description-- + +Set the `innerText` of the `addOrUpdateTaskBtn` button to `Update Task` and its `aria-label` to `Update Task` as well. + +# --hints-- + +You should set the inner text of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.innerText\s*=\s*("|')Update Task\1;?/) +``` + +You should set the `ariaLabel` of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.ariaLabel\s*=\s*("|')Update Task\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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; + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md new file mode 100644 index 00000000000..101870d098a --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md @@ -0,0 +1,384 @@ +--- +id: 64fb14d890415c14f93069ce +title: Step 44 +challengeType: 0 +dashedName: step-44 +--- + +# --description-- + +Finally, display the `form` modal with the values of the input fields by using `classList` to toggle the `hidden` class on `taskForm`. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(editTask.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md new file mode 100644 index 00000000000..7ad62060776 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md @@ -0,0 +1,391 @@ +--- +id: 64fb154a7c48cd159924bb18 +title: Step 45 +challengeType: 0 +dashedName: step-45 +--- + +# --description-- + +At this point, editing a task won't reflect when you submit the task. To make the editing functional, go back to the `if` statement inside the `addOrUpdateTask` function. Create an `else` block and set `taskData[dataArrIndex]` to `taskObj`. + +# --hints-- + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*/) +``` + +Your `else` block should have the code `taskData[dataArrIndex] = taskObj`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*taskData\[dataArrIndex\]\s*=\s*taskObj;?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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, + }; + + --fcc-editable-region-- + if (dataArrIndex === -1) { + taskData.unshift(taskObj); + } + + --fcc-editable-region-- + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md new file mode 100644 index 00000000000..4ec33172878 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md @@ -0,0 +1,401 @@ +--- +id: 64fb1c4dc0feb219149a7c7d +title: Step 46 +challengeType: 0 +dashedName: step-46 +--- + +# --description-- + +If the user attempts to edit a task but decides not to make any changes before closing the form, there is no need to display the modal with the `Cancel` and `Discard` buttons. + +Inside the `closeTaskFormBtn` event listener, use `const` to create another variable named `formInputValuesUpdated`. Check if the user made changes while trying to edit a task by verifying that the `titleInput` value **is not equal to** `currentTask.title`, the `dateInput` value **is not equal to** `currentTask.date`, and the `descriptionInput` value **is not equal to** `currentTask.description`. + +# --hints-- + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*/) +``` + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title` or `dateInput.value` is not equal to `currentTask.date`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date/) +``` + +Your `formInputValuesUpdated` variable should have the value `titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date\s*\|\|\s*descriptionInput\.value\s*!==\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + --fcc-editable-region-- + + --fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md new file mode 100644 index 00000000000..bb8a87acf60 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md @@ -0,0 +1,390 @@ +--- +id: 64fb285637fa1e1c222033e3 +title: Step 47 +challengeType: 0 +dashedName: step-47 +--- + +# --description-- + +Now add `formInputValuesUpdated` as the second mandatory condition in the `if` statement using the `AND` operator. + +This way, the `Cancel` and `Discard` buttons in the modal won't be displayed to the user if they haven't made any changes to the input fields while attempting to edit a task. + +# --hints-- + +Your `if` should have the condition `formInputsContainValues && formInputValuesUpdated`. + +```js +assert.match(code.split("if (formInputsContainValues"), /\s*&&\s*formInputValuesUpdated/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description; + +--fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md new file mode 100644 index 00000000000..94fdc40185a --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md @@ -0,0 +1,414 @@ +--- +id: 64fb29348a60361ccd45c1e2 +title: Step 48 +challengeType: 0 +dashedName: step-48 +--- + +# --description-- + +`localStorage` offers methods for saving, retrieving, and deleting items. The items you save can be of any JavaScript data type. + +For instance, the `setItem()` method is used to save an item, and the `getItem()` method retrieves the item. To delete a specific item, you can utilize the `removeItem()` method, or if you want to delete all items in the storage, you can use `clear()`. + +Here's how you can save an item: + +```js +localStorage.setItem("key", value); // value could be string, number, or any other data type +``` + +A `myTaskArr` array has been provided for you. Use the `setItem()` method to save it with a key of `data`. + +After that, open your browser console and go to the `Applications` tab, select `Local Storage`, and the freeCodeCamp domain you see. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +Your `localStorage.setItem()` should have a key of `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*myTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md new file mode 100644 index 00000000000..2ce0b111937 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md @@ -0,0 +1,404 @@ +--- +id: 64fefebad99209211ec30537 +title: Step 49 +challengeType: 0 +dashedName: step-49 +--- + +# --description-- + +If you check the `Application` tab of your browser console, you'll notice a series of `[object Object]`. This is because everything you save in `localStorage` needs to be in string format. + +To resolve the issue, wrap the data you're saving in the `JSON.stringify()` method. Then, check local storage again to observe the results. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +You should wrap `JSON.stringify()` around `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*JSON\.stringify\(myTaskArr\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- +localStorage.setItem("data", myTaskArr); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md new file mode 100644 index 00000000000..1cb0c0c5930 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md @@ -0,0 +1,406 @@ +--- +id: 64ff0313700dad264d19dfe4 +title: Step 50 +challengeType: 0 +dashedName: step-50 +--- + +# --description-- + +Now that you have the `myTaskArr` array saved in `localStorage` correctly, you can retrieve it with `getItem()` by specifying the key you used to save the item. + +Use the `getItem()` method to retrieve the `myTaskArr` array and assign it to the variable `getTaskArr`. Then, log the `getTaskArr` variable to the console to see the result. + +# --hints-- + +You should use `const` to create a `getTaskArr` variable and assign `localStorage.getItem("data")` to it. + +```js +assert.match(code, /const\s*getTaskArr\s*=\s*localStorage\.getItem\(('|")data\1\);?/) +``` + +You should log the `getTaskArr` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md new file mode 100644 index 00000000000..608713897b5 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md @@ -0,0 +1,411 @@ +--- +id: 64ff04cc33779427a6412449 +title: Step 51 +challengeType: 0 +dashedName: step-51 +--- + +# --description-- + +The item you retrieve is a string, as you saved it with `JSON.stringify()`. To view it in its original form before saving, you need to use `JSON.parse()`. + +Use `getItem()` to retrieve the `myTaskArr` array again. This time, wrap it inside `JSON.parse()`, assign it to the variable `getTaskArrObj` and log the `getTaskArrObj` to the console. + +Check the console to see the difference between `getTaskArr` and `getTaskObj`. + +# --hints-- + +You should use `const` to create a `getTaskArrObj` variable and assign it to `JSON.parse(localStorage.getItem('data'));`. + +```js +assert.match(code, /const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\(('|")data\1\)\);?/) +``` + +You should log the `getTaskArrObj` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md new file mode 100644 index 00000000000..09bad9433fa --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md @@ -0,0 +1,406 @@ +--- +id: 64ff068e0426eb288874ed79 +title: Step 52 +challengeType: 0 +dashedName: step-52 +--- + +# --description-- + +You can use `localStorage.removeItem()` to remove a specific item and `localStorage.clear()` to clear all items in the local storage. + +Remove the `data` item from local storage and open the console to observe the result. You should see `null`. + +# --hints-- + +You should use `localStorage.removeItem()` to remove the `data` item from the browser's local storage. + +```js +assert.match(code, /localStorage\.removeItem\(('|")data\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md new file mode 100644 index 00000000000..5dc3bd6e1d1 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md @@ -0,0 +1,413 @@ +--- +id: 64ff23daf176a92de95f24dc +title: Step 53 +challengeType: 0 +dashedName: step-53 +--- + +# --description-- + +Using `localStorage.clear()` won't just delete a single item from local storage but will remove all items. + +Remove `localStorage.removeItem()` and use `localStorage.clear()` instead. You don't need to pass in anything. You should also see `null` in the console. + +# --hints-- + +You should remove `localStorage.removeItem("data")`. + +```js +assert.notMatch(code, /localStorage\.removeItem\(('|")data\1\);/) +``` + +You should remove everything from the browser `local storage` with `localStorage.clear()`. + +```js +assert.match(code, /localStorage\.clear\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- +localStorage.removeItem("data"); + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md new file mode 100644 index 00000000000..0d6c42c989e --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md @@ -0,0 +1,404 @@ +--- +id: 64ff24b80431f62ec6b93f65 +title: Step 54 +challengeType: 0 +dashedName: step-54 +--- + +# --description-- + +Remove the `myTaskArr` array and all of the code for `localStorage` because you don't need them anymore. + +# --hints-- + +You should remove `myTaskArr` and all the code related to `localStorage` that you've just learned. + +```js +assert.notMatch(code, /const\s*myTaskArr\s*=\s*\[\s*\{\s*task:\s"Walk\s*the\s*Dog",\s*date:\s*"22-04-2022"\s*\},\s*\{\s*task:\s"Read\s*some\s*books",\s*date:\s*"02-11-2023"\s*\},\s*\{\s*task:\s"Watch\s*football",\s*date:\s*"10-08-2021"\s*\},\s*\];?\s*localStorage\.setItem\("data", JSON\.stringify\(myTaskArr\)\);\s*localStorage\.clear\(\);?\s*const\s*getTaskArr\s*=\s*localStorage\.getItem\("data"\)\s*console\.log\(getTaskArr\)\s*const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\("data"\)\);?\s*console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +--fcc-editable-region-- +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +localStorage.clear(); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md new file mode 100644 index 00000000000..cbbcd4df6e1 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md @@ -0,0 +1,404 @@ +--- +id: 65003986d17d1e1865b269c0 +title: Step 55 +challengeType: 0 +dashedName: step-55 +--- + +# --description-- + +Now you should save the task items to local storage when the user adds, updates, or removes a task. + +Inside the `addOrUpdateTask` function, use `setItem()` to save the tasks with a key of `data`, then pass the `taskData` array as its parameter. Ensure that you stringify the `taskData`. + +This would persist data once the user adds or updates tasks. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +assert.match(code, /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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; + } + --fcc-editable-region-- + + --fcc-editable-region-- + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md new file mode 100644 index 00000000000..ec3a700096a --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md @@ -0,0 +1,407 @@ +--- +id: 650046832f92c01a35834bca +title: Step 56 +challengeType: 0 +dashedName: step-56 +--- + +# --description-- + +You also want a deleted task to be removed from local storage. For this, you don't need the `removeItem()` or `clear()` methods. Since you already use `splice()` to remove the deleted task from `taskData`, all you need to do now is save `taskData` to local storage again. + +Use `setItem()` to save the `taskData` array again. Pass in `data` as the key and ensure that `taskData` is stringified before saving. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); + --fcc-editable-region-- + + --fcc-editable-region-- +} + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md new file mode 100644 index 00000000000..40cf6384c4e --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md @@ -0,0 +1,392 @@ +--- +id: 650048b0764f9c1b798200e2 +title: Step 57 +challengeType: 0 +dashedName: step-57 +--- + +# --description-- + +If you add, update, or remove a task, it should reflect in the UI. However, that's not happening now because you have yet to retrieve the tasks. To do this, you need to modify your initial `taskData` to be an empty array. + +Set `taskData` to the retrieval of `data` from local storage **or** an empty array. Make sure you parse the data coming with `JSON.parse()` because you saved it as a string. + +# --hints-- + +You should set the `taskData` variable to `JSON.parse(localStorage.getItem("data")) || [];`. + +```js +assert.match(code, /const\s*taskData\s*=\s*JSON.parse\(localStorage\.getItem\(('|")data\1\)\)\s*\|\|\s*\[\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- +const taskData = []; +--fcc-editable-region-- +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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md new file mode 100644 index 00000000000..08ced41b63f --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md @@ -0,0 +1,776 @@ +--- +id: 65004ba581d03d1d5628b41c +title: Step 58 +challengeType: 0 +dashedName: step-58 +--- + +# --description-- + +You've retrieved the task item(s) now, but they still don't reflect in the UI when the page loads. However, they appear when you add a new task. + +To fix this issue, you can check if there's a task inside `taskData`, and then call the `updateTaskContainer()`. + +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 the condition `taskData.length`. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*/) +``` + +You should call the `updateTaskContainer` function in your `if` statement. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*updateTaskContainer\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + taskForm.classList.toggle("hidden"); +} + +const reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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 + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md new file mode 100644 index 00000000000..6b21e6888ce --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md @@ -0,0 +1,314 @@ +--- +id: 650300a25b6f72964ab8aca6 +title: Step 13 +challengeType: 0 +dashedName: step-13 +--- + +# --description-- + +To make the `id` more unique, add another hyphen and use `Date.now()`. + +# --hints-- + +You should attach `-${Date.now()}` to the existing value of the `id` key. Don't forget it needs to be inside the template string. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + const taskObj = { + id: `${titleInput.value.toLowerCase().split(' ').join('-')}`, + }; + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md new file mode 100644 index 00000000000..0c9fbc152b7 --- /dev/null +++ b/curriculum/challenges/swahili/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md @@ -0,0 +1,337 @@ +--- +id: 65099dbd8f137d58e5c0ff16 +title: Step 21 +challengeType: 0 +dashedName: step-21 +--- + +# --description-- + +Create one more `p` element and interpolate the `description` you destructured as the text. Also, create a `strong` element inside the paragraph with the text `Description:`. + +# --hints-- + +You should create a `p` element with `${description}` as the text. + +```js +assert.match(code, /

.*\$\{description\}<\/p>/) +``` + +You should create a `strong` element with the text `Description:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Description:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/swahili/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md b/curriculum/challenges/swahili/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md index 7cce6981cca..b4cada2af12 100644 --- a/curriculum/challenges/swahili/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md +++ b/curriculum/challenges/swahili/17-college-algebra-with-python/learn-simple-and-compound-interest/simple-and-compound-interest.md @@ -12,7 +12,7 @@ This video will help you understand the equations of simple and compound interes Here is the Colab notebook to go along with this video. -Here is an additional Colab notebook that shows you one way to put many of these interest and payment forumlas into Python functions. Also you will see an example of using some formulas to output results, notice a trend, and follow up with other formulas to analyze patterns. +Here is an additional Colab notebook that shows you one way to put many of these interest and payment formulas into Python functions. Also you will see an example of using some formulas to output results, notice a trend, and follow up with other formulas to analyze patterns. # --question-- diff --git a/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md b/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md index 39ab831dad7..144796de1b0 100644 --- a/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md +++ b/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md @@ -71,8 +71,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md b/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md index 53ae7b50350..871e1408079 100644 --- a/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md +++ b/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md @@ -73,7 +73,7 @@ substringDivisibility(5); ```js function substringDivisibility(n) { - function isSubDivisable(digits) { + function isSubDivisible(digits) { const factors = [2, 3, 5, 7, 11, 13, 17]; for (let i = 1; i < digits.length - 2; i++) { @@ -108,17 +108,17 @@ function substringDivisibility(n) { } const allowedDigits = [...new Array(n + 1).keys()]; - const divisablePandigitals = []; + const divisiblePandigitals = []; heapsPermutations( allowedDigits.length, allowedDigits, - isSubDivisable, - divisablePandigitals + isSubDivisible, + divisiblePandigitals ); let sum = 0; - for (let i = 0; i < divisablePandigitals.length; i++) { - sum += divisablePandigitals[i]; + for (let i = 0; i < divisiblePandigitals.length; i++) { + sum += divisiblePandigitals[i]; } return sum; diff --git a/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md b/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md index 7cd5bc67b14..2d583a52355 100644 --- a/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md +++ b/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md @@ -67,8 +67,8 @@ class PrimeSeive { const prime = 2 * i + 3; primes.push(prime); // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } @@ -101,10 +101,10 @@ class PrimeSeive { }; function consecutivePrimeSum(limit) { - // Initalize seive + // Initialize seive const primeSeive = new PrimeSeive(limit); - // Initalize for longest sum < 100 + // Initialize for longest sum < 100 let bestPrime = 41; let bestI = 0; let bestJ = 5; diff --git a/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md b/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md index cdbfc3e568b..5b55ddd8079 100644 --- a/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md +++ b/curriculum/challenges/swahili/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/swahili/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md b/curriculum/challenges/swahili/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md index 78c29aa7952..4fa15980a0d 100644 --- a/curriculum/challenges/swahili/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md +++ b/curriculum/challenges/swahili/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md @@ -31,10 +31,10 @@ For $1 ≤ k ≤ 200$, find $\sum{m(k)}$. # --hints-- -`efficientExponentation()` should return `1582`. +`efficientExponentiation()` should return `1582`. ```js -assert.strictEqual(efficientExponentation(), 1582); +assert.strictEqual(efficientExponentiation(), 1582); ``` # --seed-- @@ -42,12 +42,12 @@ assert.strictEqual(efficientExponentation(), 1582); ## --seed-contents-- ```js -function efficientExponentation() { +function efficientExponentiation() { return true; } -efficientExponentation(); +efficientExponentiation(); ``` # --solutions-- diff --git a/curriculum/challenges/swahili/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md b/curriculum/challenges/swahili/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md index 49d59948762..e402ff347c5 100644 --- a/curriculum/challenges/swahili/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md +++ b/curriculum/challenges/swahili/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/swahili/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md b/curriculum/challenges/swahili/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md index 596f703da28..b843f6f2be2 100644 --- a/curriculum/challenges/swahili/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md +++ b/curriculum/challenges/swahili/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md @@ -35,7 +35,7 @@ concealedSquare(); ```js // Check if n**2 matches the pattern -function squareMatchs(n) { +function squareMatches(n) { // Need BigInt due to size of values let nSquared = (BigInt(n) * BigInt(n)).toString(); @@ -55,8 +55,8 @@ function concealedSquare() { for (let x = maxSquareRoot; x >= minSquareRoot; x -= 10) { // Note: 3*3 = 9 and 7*7 = 49 are only trailing digits // that can produce 9 as trailing digit in square - if (squareMatchs(x + 3)) return (x + 3)*10; - if (squareMatchs(x + 7)) return (x + 7)*10; + if (squareMatches(x + 3)) return (x + 3)*10; + if (squareMatches(x + 7)) return (x + 7)*10; } return -1; } diff --git a/curriculum/challenges/ukrainian/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md b/curriculum/challenges/ukrainian/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md index 736ed6660a9..c007c1dbacb 100644 --- a/curriculum/challenges/ukrainian/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md +++ b/curriculum/challenges/ukrainian/02-javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects.md @@ -54,7 +54,24 @@ assert.match(code, /myStorage\.car\.inside/); `gloveBoxContents` досі має бути оголошено з `const`. ```js -assert.match(code, /const\s+gloveBoxContents\s*=\s*myStorage\.car\.inside\[\s*("|')glove box\1\s*\]|const\s*{\s*('|")glove box\2:\s*gloveBoxContents\s*}\s*=\s*myStorage\.car\.inside;/); +assert.match(code, /const\s+gloveBoxContents\s*=/); +``` + +You should not change the `myStorage` object. + +```js +const expectedMyStorage = { + "car":{ + "inside":{ + "glove box":"maps", + "passenger seat":"crumbs" + }, + "outside":{ + "trunk":"jack" + } + } +}; +assert.deepStrictEqual(myStorage, expectedMyStorage); ``` # --seed-- diff --git a/curriculum/challenges/ukrainian/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md b/curriculum/challenges/ukrainian/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md index 244b52ab450..b71e4b3cced 100644 --- a/curriculum/challenges/ukrainian/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md +++ b/curriculum/challenges/ukrainian/02-javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/sum-all-primes.md @@ -58,8 +58,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/ukrainian/06-quality-assurance/quality-assurance-projects/issue-tracker.md b/curriculum/challenges/ukrainian/06-quality-assurance/quality-assurance-projects/issue-tracker.md index 24a2b8a2fe7..2e4e70fbcd1 100644 --- a/curriculum/challenges/ukrainian/06-quality-assurance/quality-assurance-projects/issue-tracker.md +++ b/curriculum/challenges/ukrainian/06-quality-assurance/quality-assurance-projects/issue-tracker.md @@ -11,7 +11,7 @@ dashedName: issue-tracker Створіть повний пакет застосунку JavaScript, який функціонально схожий до https://issue-tracker.freecodecamp.rocks/. Робота над цим проєктом передбачає написання коду за допомогою одного з наступних методів: - Клонуйте цей репозиторій GitHub та виконайте свій проєкт локально. -- Use our Replit starter project to complete your project. +- Використайте наш стартовий проєкт Replit для виконання свого проєкту. - Для виконання проєкту використайте конструктор сайту на власний вибір. Переконайтеся, що приєднали усі файли з нашого репозиторію GitHub. Якщо ви використовуєте Replit, виконайте наступні кроки для налаштування проєкту: @@ -231,13 +231,13 @@ async (getUserInput) => { }; const url = getUserInput('url') + '/api/issues/fcc-project'; const itemToUpdate = await $.post(url, initialData); - const updateSucccess = await $.ajax({ + const updateSuccess = await $.ajax({ url: url, type: 'PUT', data: { _id: itemToUpdate._id, issue_text: 'New Issue Text' } }); - assert.isObject(updateSucccess); - assert.deepEqual(updateSucccess, { + assert.isObject(updateSuccess); + assert.deepEqual(updateSuccess, { result: 'successfully updated', _id: itemToUpdate._id }); diff --git a/curriculum/challenges/ukrainian/07-scientific-computing-with-python/scientific-computing-with-python-projects/arithmetic-formatter.md b/curriculum/challenges/ukrainian/07-scientific-computing-with-python/scientific-computing-with-python-projects/arithmetic-formatter.md index 49b6d0978b3..bd7bf22c3b4 100644 --- a/curriculum/challenges/ukrainian/07-scientific-computing-with-python/scientific-computing-with-python-projects/arithmetic-formatter.md +++ b/curriculum/challenges/ukrainian/07-scientific-computing-with-python/scientific-computing-with-python-projects/arithmetic-formatter.md @@ -84,7 +84,7 @@ arithmetic_arranger(["32 + 8", "1 - 3801", "9999 + 9999", "523 - 49"], True) ## Надсилання -Copy your project's URL and submit it to freeCodeCamp. +Скопіюйте URL-адресу свого проєкту та відправте її до freeCodeCamp. # --hints-- diff --git a/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/mean-variance-standard-deviation-calculator.md b/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/mean-variance-standard-deviation-calculator.md index 32084bf3b13..b14434418d6 100644 --- a/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/mean-variance-standard-deviation-calculator.md +++ b/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/mean-variance-standard-deviation-calculator.md @@ -17,7 +17,7 @@ dashedName: mean-variance-standard-deviation-calculator Ми досі розробляємо інтерактивну частину навчальної програми з Python. Наразі є декілька відео на ютуб-каналі freeCodeCamp.org, які навчать всього необхідного для виконання цього проєкту: -- Python for Everybody Video Course (14 hours) +- Python for Everybody Video Course (14 годин) - How to Analyze Data with Python Pandas (10 годин) diff --git a/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/medical-data-visualizer.md b/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/medical-data-visualizer.md index be86035c85a..c54ade818a3 100644 --- a/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/medical-data-visualizer.md +++ b/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/medical-data-visualizer.md @@ -17,7 +17,7 @@ dashedName: medical-data-visualizer Ми досі розробляємо інтерактивну частину навчальної програми з Python. Наразі є декілька відео на ютуб-каналі freeCodeCamp.org, які навчать всього необхідного для виконання цього проєкту: -- Python for Everybody Video Course (14 hours) +- Python for Everybody Video Course (14 годин) - How to Analyze Data with Python Pandas (10 годин) diff --git a/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/page-view-time-series-visualizer.md b/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/page-view-time-series-visualizer.md index af6a29b0d05..71002b1c6b4 100644 --- a/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/page-view-time-series-visualizer.md +++ b/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/page-view-time-series-visualizer.md @@ -17,7 +17,7 @@ dashedName: page-view-time-series-visualizer Ми досі розробляємо інтерактивну частину навчальної програми з Python. Наразі є декілька відео на ютуб-каналі freeCodeCamp.org, які навчать всього необхідного для виконання цього проєкту: -- Python for Everybody Video Course (14 hours) +- Python for Everybody Video Course (14 годин) - How to Analyze Data with Python Pandas (10 годин) diff --git a/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/sea-level-predictor.md b/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/sea-level-predictor.md index 34df52c483c..b58b7e8006f 100644 --- a/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/sea-level-predictor.md +++ b/curriculum/challenges/ukrainian/08-data-analysis-with-python/data-analysis-with-python-projects/sea-level-predictor.md @@ -17,7 +17,7 @@ dashedName: sea-level-predictor Ми досі розробляємо інтерактивну частину навчальної програми з Python. Наразі є декілька відео на ютуб-каналі freeCodeCamp.org, які навчать всього необхідного для виконання цього проєкту: -- Python for Everybody Video Course (14 hours) +- Python for Everybody Video Course (14 годин) - How to Analyze Data with Python Pandas (10 годин) diff --git a/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/24-game.md b/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/24-game.md index b81ce70d8ef..16de46241a0 100644 --- a/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/24-game.md +++ b/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/24-game.md @@ -82,7 +82,7 @@ const OPERATORS_ = { "/": (a, b) => a / b, } -const PRECIDENCE_ = { +const PRECEDENCE_ = { "+": 1, "-": 1, "*": 2, @@ -114,7 +114,7 @@ function evaluate_(expression) { case "*": case "/": while (stack.length && - PRECIDENCE_[c] <= PRECIDENCE_[stack[stack.length-1]]) { + PRECEDENCE_[c] <= PRECEDENCE_[stack[stack.length-1]]) { postfix += stack.pop(); } stack.push(c); diff --git a/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md b/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md index c520f434f78..f6347018d9d 100644 --- a/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md +++ b/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/deal-cards-for-freecell.md @@ -175,7 +175,7 @@ function dealFreeCell(seed) { const rng = FreeCellRNG(seed); const deck = getDeck(); - const deltCards = [[], [], [], [], [], [], []]; + const dealtCards = [[], [], [], [], [], [], []]; let currentColumn = 0; let currentRow = 0; @@ -195,7 +195,7 @@ function dealFreeCell(seed) { card = deck.pop(); // Deal this card - deltCards[currentRow].push(card); + dealtCards[currentRow].push(card); currentColumn += 1; if (currentColumn === 8) { currentColumn = 0; @@ -203,6 +203,6 @@ function dealFreeCell(seed) { } } - return deltCards; + return dealtCards; } ``` diff --git a/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/department-numbers.md b/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/department-numbers.md index 3f657cf1290..6fb9d4dab1b 100644 --- a/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/department-numbers.md +++ b/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/department-numbers.md @@ -101,7 +101,7 @@ function combinations(possibleNumbers, total) { function combinations(possibleNumbers, total) { let firstNumber; let secondNumber; - let thridNumber; + let thirdNumber; const allCombinations = []; for (let i = 0; i < possibleNumbers.length; i += 1) { @@ -112,10 +112,10 @@ function combinations(possibleNumbers, total) { secondNumber = possibleNumbers[j]; if (j !== i && firstNumber + secondNumber <= total) { - thridNumber = total - firstNumber - secondNumber; + thirdNumber = total - firstNumber - secondNumber; - if (thridNumber !== firstNumber && thridNumber !== secondNumber && possibleNumbers.includes(thridNumber)) { - allCombinations.push([firstNumber, secondNumber, thridNumber]); + if (thirdNumber !== firstNumber && thirdNumber !== secondNumber && possibleNumbers.includes(thirdNumber)) { + allCombinations.push([firstNumber, secondNumber, thirdNumber]); } } } diff --git a/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/lu-decomposition.md b/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/lu-decomposition.md index d2d5afa191c..6f68d8f3124 100644 --- a/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/lu-decomposition.md +++ b/curriculum/challenges/ukrainian/10-coding-interview-prep/rosetta-code/lu-decomposition.md @@ -80,7 +80,7 @@ $PA = LU$ # --instructions-- -Завдання полягає в тому, щоб реалізувати процедуру, яка знайде матрицю nxn $A$ і поверне нижчу трикутну матрицю $L$, верхню трикутну матрицю $U$ і матрицю перестановки $P$, так, щоб розв'язати вищезгадане рівняння. Повернене значення має бути у вигляді `[L, U, P]`. +The task is to implement a routine which will take a square nxn matrix $A$ and return a lower triangular matrix $L$, a upper triangular matrix $U$ and a permutation matrix $P$, so that the above equation is fulfilled. Повернене значення має бути у вигляді `[L, U, P]`. # --hints-- diff --git a/curriculum/challenges/ukrainian/11-machine-learning-with-python/machine-learning-with-python-projects/rock-paper-scissors.md b/curriculum/challenges/ukrainian/11-machine-learning-with-python/machine-learning-with-python-projects/rock-paper-scissors.md index b8c0e3c9407..dcd0fafb3b1 100644 --- a/curriculum/challenges/ukrainian/11-machine-learning-with-python/machine-learning-with-python-projects/rock-paper-scissors.md +++ b/curriculum/challenges/ukrainian/11-machine-learning-with-python/machine-learning-with-python-projects/rock-paper-scissors.md @@ -58,7 +58,7 @@ play(player, quincy, 1000, verbose=True) ## Надсилання -Copy your project's URL and submit it to freeCodeCamp. +Скопіюйте URL-адресу свого проєкту та відправте її до freeCodeCamp. # --hints-- diff --git a/curriculum/challenges/ukrainian/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md b/curriculum/challenges/ukrainian/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md index 896b6452cb4..689fb5fcd7f 100644 --- a/curriculum/challenges/ukrainian/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md +++ b/curriculum/challenges/ukrainian/14-responsive-web-design-22/learn-css-transforms-by-building-a-penguin/619d20b12996101f430920fb.md @@ -9,7 +9,7 @@ dashedName: step-82 Дзьоб і лапки пінгвіна мають однаковий `color`. -Створіть нову спеціальну CSS-змінну з назвою `--penguin-picorna` та замініть нею всі відповідні значення властивостей. +Create a new custom CSS variable named `--penguin-picorna`, and replace all relevant property values with it. # --hints-- diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md index 86b8a8d44ca..02cabfb4c73 100644 --- a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-basic-javascript-by-building-a-role-playing-game/62aa2626c3c10244b94c787b.md @@ -11,7 +11,7 @@ dashedName: step-161 # --hints-- -Ваша функція `pick` повинна ініціалізувати `numbers` на порожній масив `[]`. +Your `pick` function should initialize `numbers` to an empty array `[]`. ```js assert.match(pick.toString(), /numbers\s*=\s*\[\]/); diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md new file mode 100644 index 00000000000..4b042aa87ba --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e4c4ec263b62ae7bf54d.md @@ -0,0 +1,310 @@ +--- +id: 64e4e4c4ec263b62ae7bf54d +title: Step 1 +challengeType: 0 +dashedName: step-1 +--- + +# --description-- + +In this project, you will learn how `localStorage` works in JavaScript by building a Todo app. LocalStorage is a web storage feature of JavaScript that lets you persist data by storing the data as a **key:value** pair. + +The HTML and CSS for this project have been provided for you. Take a look at the files to get yourself familiarized with them. + +Begin by accessing the `task-form`, `confirm-close-dialog`, and `open-task-form-btn` elements with the `getElementById()` method. Save them in the variables `taskForm`, `confirmCloseDialog`, and `openTaskFormBtn`. + +# --hints-- + +You should use `getElementById()` to access the `task-form` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should assign the `task-form` element to the variable `taskForm`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+taskForm\s*=\s*document\.getElementById\(\s*('|"|`)task\-form\1\);?/) +``` + +You should use `getElementById()` to access the `confirm-close-dialog` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should assign the `confirm-close-dialog` element to the variable `confirmCloseDialog`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+confirmCloseDialog\s*=\s*document\.getElementById\(\s*('|"|`)confirm\-close\-dialog\1\);?/) +``` + +You should use `getElementById()` to access the `open-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +You should assign the `open-task-form-btn` element to the variable `openTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+openTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)open\-task\-form\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md new file mode 100644 index 00000000000..32310409728 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6c86954de67a3e44ee3.md @@ -0,0 +1,310 @@ +--- +id: 64e4e6c86954de67a3e44ee3 +title: Step 2 +challengeType: 0 +dashedName: step-2 +--- + +# --description-- + +You need to access more elements with the `getElementById()` method. This time you need the `close-task-form-btn`, `add-or-update-task-btn`, and `cancel-btn` elements. Save them in the variables `closeTaskFormBtn`, `addOrUpdateTaskBtn`, and `cancelBtn`. + +# --hints-- + +You should use `getElementById()` to access the `close-task-form-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should assign the `close-task-form-btn` element to the variable `closeTaskFormBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+closeTaskFormBtn\s*=\s*document\.getElementById\(\s*('|"|`)close\-task\-form\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `add-or-update-task-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should assign the `add-or-update-task-btn` element to the variable `addOrUpdateTaskBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+addOrUpdateTaskBtn\s*=\s*document\.getElementById\(\s*('|"|`)add\-or\-update\-task\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `cancel-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +You should assign the `cancel-btn` element to the variable `cancelBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+cancelBtn\s*=\s*document\.getElementById\(\s*('|"|`)cancel\-btn\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md new file mode 100644 index 00000000000..17fc6b76517 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e6fe78b5aa67ef2fc3e7.md @@ -0,0 +1,313 @@ +--- +id: 64e4e6fe78b5aa67ef2fc3e7 +title: Step 3 +challengeType: 0 +dashedName: step-3 +--- + +# --description-- + +Next, access the `discard-btn`, `tasks-container`, and `title-input` elements using the `getElementById()` method. Save them in variables named `discardBtn`, `tasksContainer`, and `titleInput`, respectively. + +# --hints-- + +You should use `getElementById()` to access the `discard-btn` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should assign the `discard-btn` element to the variable `discardBtn`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+discardBtn\s*=\s*document\.getElementById\(\s*('|"|`)discard\-btn\1\);?/) +``` + +You should use `getElementById()` to access the `tasks-container` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should assign the `tasks-container` element to the variable `tasksContainer`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+tasksContainer\s*=\s*document\.getElementById\(\s*('|"|`)tasks\-container\1\);?/) +``` + +You should use `getElementById()` to access the `title-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +You should assign the `title-input` element to the variable `titleInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+titleInput\s*=\s*document\.getElementById\(\s*('|"|`)title\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md new file mode 100644 index 00000000000..6e543d36acc --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7241f52bb682eeb8211.md @@ -0,0 +1,304 @@ +--- +id: 64e4e7241f52bb682eeb8211 +title: Step 4 +challengeType: 0 +dashedName: step-4 +--- + +# --description-- + +The last set of elements you need to get from the HTML file are the `date-input` and `description-input` elements. Save them in the variables `dateInput` and `descriptionInput` respectively. + +# --hints-- + +You should use `getElementById()` to access the `date-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should assign the `date-input` element to the variable `dateInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+dateInput\s*=\s*document\.getElementById\(\s*('|"|`)date\-input\1\);?/) +``` + +You should use `getElementById()` to access the `description-input` element. + +```js +assert.match(code, /document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +You should assign the `description-input` element to the variable `descriptionInput`. Don't forget to use `const` to declare the variable. + +```js +assert.match(code, /const\s+descriptionInput\s*=\s*document\.getElementById\(\s*('|"|`)description\-input\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md new file mode 100644 index 00000000000..6f0cd6e9fc4 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e74d0fb4f0687bf4145d.md @@ -0,0 +1,296 @@ +--- +id: 64e4e74d0fb4f0687bf4145d +title: Step 5 +challengeType: 0 +dashedName: step-5 +--- + +# --description-- + +Create a `taskData` constant and set it to an empty array. This array will store all the tasks along with their associated data, including title, due date, and description. This storage will enable you to keep track of tasks, display them on the page, and save them to `localStorage`. + +Use `let` to create a `currentTask` variable and set it to an empty object. This variable will be used to track the state when editing and discarding tasks. + +# --hints-- + +You should create a `taskData` constant set to an empty array. + +```js +assert.match(code, /const\s+taskData\s*=\s*\[\];?/) +``` + +You should use `let` to create an empty `currentTask` object. + +```js +assert.match(code, /let\s+currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md new file mode 100644 index 00000000000..7eba2896bf4 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e78a7ea4a168de4e6a38.md @@ -0,0 +1,307 @@ +--- +id: 64e4e78a7ea4a168de4e6a38 +title: Step 6 +challengeType: 0 +dashedName: step-6 +--- + +# --description-- + +Now, you will work on opening and closing the form modal. + +Add a `click` event listener to the `openTaskFormBtn` variable, and then use `classList` to toggle the `hidden` class on the `taskForm` element. + +Now you can click on the "Add new Task" button and see the form modal. + +# --hints-- + +You should call the `addEventListener()` method on your `openTaskFormBtn` variable. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax, then use `classList` property and it's `toggle` method on the `taskForm`, to toggle the class `hidden`. Don't use curly braces. + +```js +assert.match(code, /openTaskFormBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*taskForm\.classList\.toggle\(\1hidden\1\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md new file mode 100644 index 00000000000..6a29158134e --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4e7bbedb22d6939001ad3.md @@ -0,0 +1,309 @@ +--- +id: 64e4e7bbedb22d6939001ad3 +title: Step 7 +challengeType: 0 +dashedName: step-7 +--- + +# --description-- + +Add a `click` event listener to the `closeTaskFormBtn` variable, then use the `showModal()` method on your `confirmCloseDialog` variable. This will display a modal with the `Discard` and `Cancel` buttons. + +`showModal()` is a method associated with the HTML `dialog` element. It is used to display a modal dialog box on a web page. + +# --hints-- + +You should call the `addEventListener()` method on your `closeTaskFormBtn` variable. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `showModal()` method on `confirmCloseDialog`. + +```js +assert.match(code, /closeTaskFormBtn\.addEventListener\(["'`]click["'`],\s*\(.*\)\s*=>\s*{?\s*confirmCloseDialog\.showModal\(\);?\s*}?\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md new file mode 100644 index 00000000000..311984043a0 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eaaa9070a66aecbfe603.md @@ -0,0 +1,316 @@ +--- +id: 64e4eaaa9070a66aecbfe603 +title: Step 8 +challengeType: 0 +dashedName: step-8 +--- + +# --description-- + +If the user clicks the `Cancel` button, you want to cancel the process (close the modal showing the two buttons) so the user can continue editing. + +Add a `click` event listener to the `cancelBtn` element, then use the `close()` method on the `confirmCloseDialog` variable. + +`close()` is a method of the window object you can use to close the current window, or a modal you create with the `dialog` element. + +# --hints-- + +You should call the `addEventListener()` method on your `cancelBtn` variable. + +```js +assert.match(code, /cancelBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1/) +``` + +Your event listener should use arrow syntax to set the `close()` method on `confirmCloseDialog`. Don't use curly braces. + +```js +assert.match(code, /cancelBtn\.addEventListener\(('|"|`)click\1\,\s*\(\)\s*\s*=>\s*confirmCloseDialog\.close\(\)\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- + +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md new file mode 100644 index 00000000000..66a40c9771b --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ebc7eabc5a6babd479cd.md @@ -0,0 +1,327 @@ +--- +id: 64e4ebc7eabc5a6babd479cd +title: Step 9 +challengeType: 0 +dashedName: step-9 +--- + +# --description-- + +If the user clicks the `Discard` button, you want to close the modal showing the `Cancel` and `Discard` buttons, then hide the form modal. + +Add a click event listener to `discardBtn`, then use the `close()` method on the `confirmCloseDialog` variable. Also, use `classList` to toggle the class `hidden` on `taskForm` so the form modal will close too. + +# --hints-- + +You should call the `addEventListener()` method on your `discardBtn` variable. + +```js +assert.match(code, /discardBtn\.addEventListener\(/) +``` + +Your event listener should listen for a `click` event. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{/) +``` + +Your event listener should use the `close()` method on `confirmCloseDialog`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?/) +``` + +Your event listener should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(code, /discardBtn\.addEventListener\(('|"|`)click\1,\s*\(\)\s*=>\s*\{\s*confirmCloseDialog\.close\(\);?\s*taskForm\.classList\.toggle\(\1hidden\1\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md new file mode 100644 index 00000000000..f4002dc0937 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4ecd7735a566c9266a338.md @@ -0,0 +1,326 @@ +--- +id: 64e4ecd7735a566c9266a338 +title: Step 10 +challengeType: 0 +dashedName: step-10 +--- + +# --description-- + +Now that you've worked on opening and closing the modal, it's time to get the values from the input fields, save them into the `taskData` array, and display them on the page. + +To start, add a `submit` event listener to your `taskForm` element and pass in `e` as the parameter of your arrow function. Inside the curly braces, use the `preventDefault()` method to stop the browser from refreshing the page after submitting the form. + +# --hints-- + +You should call the `addEventListener()` method on your `taskForm` variable. + +```js +assert.match(code, /taskForm\.addEventListener\(/) +``` + +Your event listener should listen for a `submit` event. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1/) +``` + +You should use arrow syntax to set your event listener to an empty pair of curly braces with `e` as the parameter. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{/) +``` + +You should use the `e.preventDefault()` method to stop the browser from reloading the page. + +```js +assert.match(code, /taskForm\.addEventListener\(('|"|`)submit\1,\s*\(e\)\s*=>\s*\{\s*e\.preventDefault\(\);?\s*\}\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md new file mode 100644 index 00000000000..148e52f3e93 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4eec13546c06d61a63d59.md @@ -0,0 +1,337 @@ +--- +id: 64e4eec13546c06d61a63d59 +title: Step 11 +challengeType: 0 +dashedName: step-11 +--- + +# --description-- + +You need to determine whether the task being added already exists or not. If it doesn't exist, you will add it, and if it already exists, you will set it up for editing. You can use the `findIndex()` method to accomplish this. + +`findIndex` is an array method that lets find the index of the first element in an array that satisfies a given testing function. + +Here's an example: + +```js +const numbers = [3, 1, 5, 6, 10, 9, 8]; +const firstEvenNumIndex = numbers.findIndex((num) => num % 2 === 0); + +console.log(firstEvenNumIndex); // Output: 3 – because the first even number (6) is at index 3 +``` + +Declare a `dataArrIndex` variable using `const` and set it to the result of the `findIndex()` method applied to the `taskData` array. Utilize arrow syntax to provide a callback function with `item` as the parameter, and within the callback, check if the `id` property of `item` is equal to the `id` property of `currentTask`. + +If the task exists, this returns the index, and if it doesn't exist, it returns `-1`. + +# --hints-- + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === currentTask.id`. + +```js +assert.match(code, /const dataArrIndex\s*=\s*taskData\.findIndex\(\(?item\)?\s*=>\s*item.id\s*===\s*currentTask.id\s*\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md new file mode 100644 index 00000000000..10826de6f17 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64e4f01d6c72086e016a8626.md @@ -0,0 +1,340 @@ +--- +id: 64e4f01d6c72086e016a8626 +title: Step 12 +challengeType: 0 +dashedName: step-12 +--- + +# --description-- + +Next, retrieve the values from the input fields and store them in a `taskObj` object. Each task should also have a unique `id`. + +Create a `taskObj` object with an `id` property as the first property. For the value of the `id` property, retrieve the value of the `titleInput` field, convert it to lowercase, and then use the `split()` and `join()` methods to hyphenate it. + +Make sure all of those are in template literals because you need the `id` property value as a string. + +# --hints-- + +You should use `const` to create a `taskObj` object. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*/) +``` + +Your `taskObj` object should have a key of `id`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id/) +``` + +You should use template strings to get `titleInput.value` as the value of your `id` key and convert it to lowercase. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)/) +``` + +You should use `.split(' ')` on `titleInput.value.toLowerCase()`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)/) +``` + +You should use `.join('-')` on `titleInput.value.toLowerCase().split(' ')`. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md new file mode 100644 index 00000000000..764de1b0918 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec89ee549ecf802de2b3e2.md @@ -0,0 +1,327 @@ +--- +id: 64ec89ee549ecf802de2b3e2 +title: Step 14 +challengeType: 0 +dashedName: step-14 +--- + +# --description-- + +Retrieve the values from the `titleInput`, `dateInput`, and `descriptionInput` fields, and then save them in the properties `title`, `date`, and `description` of the `taskObj` object. + +# --hints-- + +You should get the value of `titleInput` and save it in a `title` key. + +```js +assert.match(code, /title:\s*titleInput\.value,/) +``` + +You should get the value of `dateInput` and save it in a `date` key. + +```js +assert.match(code, /date:\s*dateInput\.value,/) +``` + +You should get the value of `descriptionInput` and save it in a `description` key. + +```js +assert.match(code, /description:\s*descriptionInput\.value,?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + const taskObj = { + id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`, + --fcc-editable-region-- + + --fcc-editable-region-- + }; +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md new file mode 100644 index 00000000000..3d009188d81 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec8f717b261e824d82d6a5.md @@ -0,0 +1,332 @@ +--- +id: 64ec8f717b261e824d82d6a5 +title: Step 15 +challengeType: 0 +dashedName: step-15 +--- + +# --description-- + +Now that you have obtained the values from the input fields and generated an `id`, you want to add them to your `taskData` array to keep track of each task. However, you should only do this if the task is new. If the task already exists, you will set it up for editing. This is why you have the `dataArrIndex` variable, which provides the index of each task. + +Create an `if` statement with the condition `dataArrIndex === -1`. Within the `if` statement, use the `unshift()` method to add the `taskObj` object to the beginning of the `taskData` array. + +`unshift()` is an array method that is used to add one or more elements to the beginning of an array. + +**N.B:** Try adding a task and log `taskData` to the console to see what it looks like. + +# --hints-- + +You should create an `if` statement with the condition `dataArrIndex === -1`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{/) +``` + +Your `if` statement should have `taskData.unshift(taskObj)` in it's body. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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, + }; + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md new file mode 100644 index 00000000000..f1202bb93fe --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9145e424d8835a4e0f28.md @@ -0,0 +1,331 @@ +--- +id: 64ec9145e424d8835a4e0f28 +title: Step 16 +challengeType: 0 +dashedName: step-16 +--- + +# --description-- + +Now that you have saved the task in the `taskData` array, you should display the task on the page by looping through it. + +Use `forEach()` on `taskData`, then destructure `id`, `title`, `date`, `description` as the parameters. Don't return anything yet. + +# --hints-- + +You should use `forEach()` on `taskData`. + +```js +assert.match(code, /taskData\.forEach\(\s*/) +``` + +You should destructure `id`, `title`, `date`, `description` as the parameters of the callback of your `forEach()`. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md new file mode 100644 index 00000000000..06bdd7c6ae5 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9282cd547785258cecf2.md @@ -0,0 +1,336 @@ +--- +id: 64ec9282cd547785258cecf2 +title: Step 17 +challengeType: 0 +dashedName: step-17 +--- + +# --description-- + +Using arrow syntax, create an implicit return and use addition assignment to set the `innerHTML` of `tasksContainer` to empty backticks. + +# --hints-- + +You should not alter the existing `taskData.forEach()` and the values you destructured. + +```js +assert.match(code, /taskData\.forEach\(\s*\(\s*\{\s*(?:id\s*,\s*title\s*,\s*date\s*,\s*description)|(?:title\s*,\s*id\s*,\s*date\s*,\s*description)|(?:date\s*,\s*id\s*,\s*title\s*,\s*description)|(?:id\s*,\s*date\s*,\s*title\s*,\s*description)|(?:title\s*,\s*date\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*id\s*,\s*description)|(?:date\s*,\s*title\s*,\s*description\s*,\s*id)|(?:title\s*,\s*date\s*,\s*description\s*,\s*id)|(?:description\s*,\s*date\s*,\s*title\s*,\s*id)|(?:date\s*,\s*description\s*,\s*title\s*,\s*id)|(?:title\s*,\s*description\s*,\s*date\s*,\s*id)|(?:description\s*,\s*title\s*,\s*date\s*,\s*id)|(?:description\s*,\s*id\s*,\s*date\s*,\s*title)|(?:id\s*,\s*description\s*,\s*date\s*,\s*title)|(?:date\s*,\s*description\s*,\s*id\s*,\s*title)|(?:description\s*,\s*date\s*,\s*id\s*,\s*title)|(?:id\s*,\s*date\s*,\s*description\s*,\s*title)|(?:date\s*,\s*id\s*,\s*description\s*,\s*title)|(?:title\s*,\s*id\s*,\s*description\s*,\s*date)|(?:id\s*,\s*title\s*,\s*description\s*,\s*date)|(?:description\s*,\s*title\s*,\s*id\s*,\s*date)|(?:title\s*,\s*description\s*,\s*id\s*,\s*date)|(?:id\s*,\s*description\s*,\s*title\s*,\s*date)|(?:description\s*,\s*id\s*,\s*title\s*,\s*date)\s*\}/) +``` + +You should use arrow syntax and attach `innerHTML` to `tasksContainer`. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\(\s*tasksContainer\.innerHTML\s*\))?/) +``` + +You should use addition assignment to set the `innerHTML` of `tasksContainer` to an empty pair of backticks. + +```js +assert.match(code, /taskData\.forEach\(\(\{.*\}\)\s*=>\s*(\s*\(?tasksContainer\.innerHTML\s*\+=\s*`\s*`\)?)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + --fcc-editable-region-- + taskData.forEach(({id, title, date, description})); + + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md new file mode 100644 index 00000000000..b5115f99a51 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9343769e8f85c1e17e05.md @@ -0,0 +1,333 @@ +--- +id: 64ec9343769e8f85c1e17e05 +title: Step 18 +challengeType: 0 +dashedName: step-18 +--- + +# --description-- + +Create a `div` element with the class of `task`. Utilize template strings to set the `id` attribute of the `div` to the `id` you destructured from the task data. + +# --hints-- + +You should create a `div` element with the class `task`. + +```js +assert.match(code, /\s*<\/div>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` + --fcc-editable-region-- + + --fcc-editable-region-- + `) + ); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md new file mode 100644 index 00000000000..81dd3530420 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec94f0de20c086e09b0fc3.md @@ -0,0 +1,348 @@ +--- +id: 64ec94f0de20c086e09b0fc3 +title: Step 19 +challengeType: 0 +dashedName: step-19 +--- + +# --description-- + +Create a `p` element and use template strings to set its content to the `title` you destructured. Right before the content of the `p` element, create a `strong` element with the text `Title:`. + +# --hints-- + +You should create a `p` element. + +```js +assert.match(code, /

/) +``` + +You should interpolate `${title}` as the text of your `p` element. + +```js +assert.match(code, /

.*\$\{title\}<\/p>/) +``` + +You should create a `strong` element after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)/) +``` + +Your `strong` element should have the text `Title:`. + +```js +assert.match(code, /(?<=

)Title:\s*<\/strong>\s*/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md new file mode 100644 index 00000000000..4820d100d4d --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec959a76336c8767f5cd4d.md @@ -0,0 +1,336 @@ +--- +id: 64ec959a76336c8767f5cd4d +title: Step 20 +challengeType: 0 +dashedName: step-20 +--- + +# --description-- + +Similarly to the previous step, create another `p` element, and interpolate the `date` you destructured as the text content. Inside this paragraph, create a `strong` element with the text `Date:`. + +# --hints-- + +You should create a `p` element and interpolate `${date}` as the text. + +```js +assert.match(code, /

.*\$\{date\}<\/p>/) +``` + +You should create a `strong` element with the text `Date:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Date:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md new file mode 100644 index 00000000000..4525b0e154c --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec96761156a187ed32b274.md @@ -0,0 +1,340 @@ +--- +id: 64ec96761156a187ed32b274 +title: Step 22 +challengeType: 0 +dashedName: step-22 +--- + +# --description-- + +To allow for task management, you need to include both a delete and an edit button for each task. + +Create two `button` elements with the `type` attribute set to `button` and the `class` attribute set to `btn`. Set the text of the first button to `Edit` and the text of the second button to `Delete`. + +# --hints-- + +You should create a `button` element of type `button`, a class `btn` and `Edit` as the text, in that order. + +```js +assert.match(code, /Edit<\/button/) +``` + +You should create a `button` element of type `button` a class `btn` and `Delete` as the text, in that order. + +```js +assert.match(code, /Delete<\/button/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md new file mode 100644 index 00000000000..0081fa88e1a --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9b10356c2d8aa05d9ce1.md @@ -0,0 +1,336 @@ +--- +id: 64ec9b10356c2d8aa05d9ce1 +title: Step 23 +challengeType: 0 +dashedName: step-23 +--- + +# --description-- + +After adding the task to the page, you should close the form modal to view the task. To do this, utilize `classList` to toggle the `hidden` class on the `taskForm` element. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +const splitter = code.split("taskData.forEach") +assert.match(splitter[1], /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md new file mode 100644 index 00000000000..6ae1e4dff48 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ec9c55fdeef78bacd2fc3b.md @@ -0,0 +1,347 @@ +--- +id: 64ec9c55fdeef78bacd2fc3b +title: Step 24 +challengeType: 0 +dashedName: step-24 +--- + +# --description-- + +If you attempt to add another task now, you'll notice that the input fields retain the values you entered for the previous task. To resolve this, you need to clear the input fields after adding a task. + +Instead of clearing the input fields one by one, it's a good practice to create a function that handles clearing those fields. You can then call this function whenever you need to clear the input fields again. + +Use arrow syntax to create a `reset` function and set it to a pair of curly braces. + +# --hints-- + +You should use `const` and arrow syntax to create a `reset` function. + +```js +assert.match(code, /const\s+reset\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +Your `reset` function should be empty. + +```js +assert.match(reset.toString(), /\(\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md new file mode 100644 index 00000000000..e12fb12ab81 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac365aeb8ad70b69b366f.md @@ -0,0 +1,366 @@ +--- +id: 64fac365aeb8ad70b69b366f +title: Step 25 +challengeType: 0 +dashedName: step-25 +--- + +# --description-- + +Inside the `reset` function, set each value of `titleInput`, `dateInput`, `descriptionInput` to an empty string. + +Also, use `classList` to toggle the class `hidden` on the `taskForm` and set `currentTask` to an empty object. That's because at this point, `currentTask` will be filled with the task the user might have added. + +# --hints-- + +You should set `titleInput.value` to an empty string. + +```js +assert.match(reset.toString(), /titleInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `dateInput.value` to an empty string. + +```js +assert.match(reset.toString(), /dateInput\.value\s*=\s*('|")\1;?/) +``` + +You should set `descriptionInput.value` to an empty string. + +```js +assert.match(reset.toString(), /descriptionInput\.value\s*=\s*('|")\1;?/) +``` + +You should use `classList` to toggle the class `hidden` on `taskForm` + +```js +assert.match(reset.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\)/) +``` + +You should set `currentTask` to an empty object. + +```js +assert.match(reset.toString(), /currentTask\s*=\s*\{\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- +const reset = () => { + +} +--fcc-editable-region-- + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + taskForm.classList.toggle("hidden"); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md new file mode 100644 index 00000000000..5f281d0de16 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac4d1773e7a719b1254de.md @@ -0,0 +1,351 @@ +--- +id: 64fac4d1773e7a719b1254de +title: Step 26 +challengeType: 0 +dashedName: step-26 +--- + +# --description-- + +Remove the existing code toggling the class of `hidden` on `taskForm` and call the `reset` function instead. This would clear the input fields and also hide the form modal for the user to see the added task. + +# --hints-- + +Yous should remove the code toggling the `hidden` class on `taskForm`. + +```js +const splitter = code.split('') +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /reset\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + +--fcc-editable-region-- + taskForm.classList.toggle("hidden"); +--fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md new file mode 100644 index 00000000000..c1a7b0f8965 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fac6a497811572b338e5e5.md @@ -0,0 +1,351 @@ +--- +id: 64fac6a497811572b338e5e5 +title: Step 27 +challengeType: 0 +dashedName: step-27 +--- + +# --description-- + +Also, remove the existing code toggling the class `hidden` on `taskForm` inside the `discardBtn` event listener and call the `reset` function instead. That's because when you click the `Discard` button, everything in the input fields should go away. + +# --hints-- + +Yous should remove the code toggling the class `hidden` on `taskForm`. + +```js +const splitter = code.split("confirmCloseDialog.close();") +assert.notMatch(splitter[1], /taskForm\.classList\.toggle\(("|')hidden\1\);?/) +``` + +You should call the `reset` function. + +```js +assert.match(code, /confirmCloseDialog\.close\(\);?\s*reset\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +--fcc-editable-region-- +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); +--fcc-editable-region-- + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md new file mode 100644 index 00000000000..84005e1b01a --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faca774fd9fd74bc084cc9.md @@ -0,0 +1,346 @@ +--- +id: 64faca774fd9fd74bc084cc9 +title: Step 28 +challengeType: 0 +dashedName: step-28 +--- + +# --description-- + +You should display the `Cancel` and `Discard` buttons to the user only if there is some text present in the input fields. + +To begin, within the `closeTaskFormBtn` event listener, create a `formInputsContainValues` variable to check if there is a value in the `titleInput` field **or** the `dateInput` field **or** the `descriptionInput` field. + +# --hints-- + +You should use `const` to create a variable `formInputsContainValues` with the value `titleInput.value || dateInput.value || descriptionInput.value;` + +```js +assert.match(code, /const\s*formInputsContainValues\s*=\s*titleInput\.value\s*\|\|\s*dateInput\.value\s*\|\|\s*descriptionInput\.value;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +--fcc-editable-region-- +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); + +}); +--fcc-editable-region-- + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md new file mode 100644 index 00000000000..07482464ebf --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64facf6180824876f70a2e86.md @@ -0,0 +1,362 @@ +--- +id: 64facf6180824876f70a2e86 +title: Step 29 +challengeType: 0 +dashedName: step-29 +--- + +# --description-- + +Create an `if` statement to check if `formInputsContainValues` is true. If `formInputsContainValues` is true, indicating that there are changes, use the `showModal()` method on `confirmCloseDialog`. Otherwise, if there are no changes, call the `reset()` function to clear the input fields and hide the form modal. + +# --hints-- + +You should create an `if` statement with the condition `formInputsContainValues`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{/) +``` + +The `if` block of your `if` statement should contain `confirmCloseDialog.showModal();`. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?/) +``` + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{/) +``` + +You should call the `reset()` function in the `else` block of your `if` statement. + +```js +assert.match(code, /if\s*\(formInputsContainValues\)\s*\{\s*confirmCloseDialog\.showModal\(\);?\s*\}\s*else\s*\{\s*reset\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 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; +--fcc-editable-region-- + confirmCloseDialog.showModal(); +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md new file mode 100644 index 00000000000..95b71fba69f --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad07f43a101779cb8692a.md @@ -0,0 +1,372 @@ +--- +id: 64fad07f43a101779cb8692a +title: Step 30 +challengeType: 0 +dashedName: step-30 +--- + +# --description-- + +You can enhance code readability and maintainability by refactoring the `submit` event listener into two separate functions. The first function can be used to add the input values to `taskData`, while the second function can be responsible for adding the tasks to the DOM. + +Use arrow syntax to create an `addOrUpdateTask` function. Then move the `dataArrIndex` variable, the `taskObj` object, and the `if` statement into the `addOrUpdateTask` function. + +# --hints-- + +You should use `const` and arrow syntax to create an `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*/) +``` + +You should move the `dataArrIndex` variable into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?/) +``` + +You should move the `taskObj` object into the `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?/) +``` + +You should move the `if` statement with the condition `dataArrIndex === 1` into your `addOrUpdateTask` function. + +```js +assert.match(code, /const\s+addOrUpdateTask\s*=\s*\(\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s===\s*currentTask\.id\);?\s*const\s*taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,\s*title:\s*titleInput\.value,\s*date:\s*dateInput\.value,\s*description:\s*descriptionInput\.value,\s*\};?\s*if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + 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); + } + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md new file mode 100644 index 00000000000..8192699bb61 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fad9cd2eeb1e7ca2ca8c8b.md @@ -0,0 +1,358 @@ +--- +id: 64fad9cd2eeb1e7ca2ca8c8b +title: Step 31 +challengeType: 0 +dashedName: step-31 +--- + +# --description-- + +Use arrow syntax to create an `updateTaskContainer` function. Then move the `taskData.forEach()` and its content into it. + +# --hints-- + +You should use `const` and arrow syntax to create a `updateTaskContainer` function. + +```js +assert.match(code, /const\s*updateTaskContainer\s*=\s*\(\)\s*=>\s*\{/) +``` + +You should move `taskData.forEach()` and its content into the `updateTaskContainer()` function. + +```js +assert.match(code, /const\s+updateTaskContainer\s+=\s*\(\)\s*=>\s*\{\s*taskData\.forEach\(\s*\(\{\s*id,\s*title,\s*date,\s*description\s*\}\)\s*=>\s*\(tasksContainer\.innerHTML\s*\+=\s*`\s*\s*

Title:<\/strong>\s*\$\{title\}<\/p>\s*

Date:<\/strong>\s*\$\{date\}<\/p>\s*

Description:<\/strong>\s*\$\{description\}<\/p>\s*Edit<\/button>\s*Delete<\/button>\s*<\/div>\s*`\)\s*\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); + + reset() +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md new file mode 100644 index 00000000000..18b2a462de4 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadae4f2d51b7d5d8b98d8.md @@ -0,0 +1,360 @@ +--- +id: 64fadae4f2d51b7d5d8b98d8 +title: Step 32 +challengeType: 0 +dashedName: step-32 +--- + +# --description-- + +Inside the `addOrUpdateTask` function, call the `updateTaskContainer` and `reset` functions. + +# --hints-- + +You should call the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\)\s*/) +``` + +You should call the `reset` function after calling the `updateTaskContainer` function. + +```js +assert.match(code, /updateTaskContainer\(\);?\s*reset\(\);?\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + --fcc-editable-region-- + + --fcc-editable-region-- +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + reset() +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md new file mode 100644 index 00000000000..657bb4aa05b --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fadff23375f27ff06c6d40.md @@ -0,0 +1,355 @@ +--- +id: 64fadff23375f27ff06c6d40 +title: Step 33 +challengeType: 0 +dashedName: step-33 +--- + +# --description-- + +Now remove the `reset()` call inside the `taskForm` submit event listener and call the `addOrUpdateTask` function instead. + +# --hints-- + +You should remove `reset()` and call `addOrUpdateTask()`. + +```js +assert.match(code, /addOrUpdateTask\(\)/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); + + --fcc-editable-region-- + reset() + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md new file mode 100644 index 00000000000..bd759fb7e5f --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fae068bcdc9c805bd8399e.md @@ -0,0 +1,364 @@ +--- +id: 64fae068bcdc9c805bd8399e +title: Step 35 +challengeType: 0 +dashedName: step-35 +--- + +# --description-- + +To enable editing and deleting for each task, add an `onclick` attribute to both buttons. Set the value of the `onclick` attribute to `editTask(this)` for the `Edit` button and `deleteTask(this)` for the `Delete` button. The `editTask(this)` function will handle editing, while the `deleteTask(this)` function will handle deletion. + +`this` is a keyword that refers to the current context. In this case, `this` points to the element that triggers the event – the buttons. + +# --hints-- + +You should add `onclick="editTask(this)"` as the first attribute of the edit button. + +```js +assert.match(code, /Edit<\/button>/) +``` + +You should add `onclick="deleteTask(this)"` as the first attribute of the delete button. + +```js +assert.match(code, /Delete<\/button>/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ --fcc-editable-region-- + + + --fcc-editable-region-- +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md new file mode 100644 index 00000000000..c6fac350818 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faedcd16a1e985c4c2dc94.md @@ -0,0 +1,371 @@ +--- +id: 64faedcd16a1e985c4c2dc94 +title: Step 36 +challengeType: 0 +dashedName: step-36 +--- + +# --description-- + +Create a `deleteTask` function using arrow syntax. Pass `buttonEl` as the parameter and define an empty set of curly braces for the function body. + +# --hints-- + +You should use `const` and arrow syntax to create a `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `deleteTask` function should take a `buttonEl` parameter. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `deleteTask` function should be empty. + +```js +assert.match(deleteTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md new file mode 100644 index 00000000000..27631c77f2f --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf0418e828c0114a558a7.md @@ -0,0 +1,359 @@ +--- +id: 64faf0418e828c0114a558a7 +title: Step 34 +challengeType: 0 +dashedName: step-34 +--- + +# --description-- + +There's a problem. If you add a task, and then add another, the previous task gets duplicated. This means you need to clear out the existing contents of `tasksContainer` before adding a new task. + +Set the `innerHTML` of `taskContainer` back to an empty string. + + +# --hints-- + +You should set the `innerHTML` of `tasksContainer` to an empty string. + +```js +assert.match(code, /tasksContainer\.innerHTML\s*=\s*("|')\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + --fcc-editable-region-- + + --fcc-editable-region-- + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md new file mode 100644 index 00000000000..9cdbd7860ca --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf65b22ad8d07df9be14d.md @@ -0,0 +1,382 @@ +--- +id: 64faf65b22ad8d07df9be14d +title: Step 37 +challengeType: 0 +dashedName: step-37 +--- + +# --description-- + +You need to find the index of the task you want to delete first. + +Create a `dataArrIndex` variable and set its value using the `findIndex()` method on the `taskData` array. Pass `item` as the parameter for the arrow callback function, and within the callback, check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `deleteTask` function. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?item\)?/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. Don't use curly braces. + +```js +assert.match(code, /const\s*deleteTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(\s*\(?\s*item\s*\)?\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\s*\);?\s*\};?/) +``` + + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md new file mode 100644 index 00000000000..0e54adadf06 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64faf874364ec308f875f636.md @@ -0,0 +1,398 @@ +--- +id: 64faf874364ec308f875f636 +title: Step 38 +challengeType: 0 +dashedName: step-38 +--- + +# --description-- + +You need to remove the task from the DOM using `remove()` and from the `taskData` array using `splice()`. + +`splice()` is an array method that modifies arrays by removing, replacing, or adding elements at a specified index, while also returning the removed elements. It can take up to three parameters: the first one is the mandatory index at which to start, the second is the number of items to remove, and the third is an optional replacement element. Here's an example: + +```js +const fruits = ["mango", "date", "cherry", "banana", "apple"]; + +// Remove date and cherry from the array starting at index 1 +const removedFruits = fruits.splice(1, 2); + +console.log(fruits); // [ 'mango', 'banana', 'apple' ] +console.log(removedFruits); // [ 'date', 'cherry' ] +``` + +Use the `remove()` method to remove the `parentElement` of the `buttonEl` from the DOM. Then use `splice()` to remove the task from the `taskData` array. Pass in `dataArrIndex` and `1` as the parameters of your `splice()`. + +`dataArrIndex` is the index to start and `1` is the number of items to remove. + +# --hints-- + +You should use the `remove()` method to remove the parent element of `buttonEl`. + +```js +assert.match(deleteTask.toString(), /buttonEl\.parentElement\.remove\(\);?/) +``` + +You should use `splice()` on the `taskData` array. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(/) +``` + +The first parameter of your `splice()` method should be `dataArrIndex`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex/) +``` + +The second parameter of your `splice()` method should be `1`. + +```js +assert.match(deleteTask.toString(), /taskData\.splice\(dataArrIndex,\s*1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + +--fcc-editable-region-- +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md new file mode 100644 index 00000000000..3f379d51764 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fafac95328110a69bcb75f.md @@ -0,0 +1,381 @@ +--- +id: 64fafac95328110a69bcb75f +title: Step 39 +challengeType: 0 +dashedName: step-39 +--- + +# --description-- + +Use arrow syntax to create an `editTask` function. Pass in `buttonEl` as the parameter and add empty curly braces for the body. + +# --hints-- + +You should use `const` and arrow syntax to create an `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(.*\)\s*=>\s*\{\s*/) +``` + +Your `editTask` function should take a `buttonEl` parameter. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)/); +``` + +Your `editTask` function should be empty. + +```js +assert.match(editTask.toString(), /\(\s*buttonEl\s*\)\s*\{\s*\}/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md new file mode 100644 index 00000000000..bc0e2a84503 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb0fa0968f2b113b2d90e9.md @@ -0,0 +1,391 @@ +--- +id: 64fb0fa0968f2b113b2d90e9 +title: Step 40 +challengeType: 0 +dashedName: step-40 +--- + +# --description-- + +As you did in the `deleteTask` function, you need to find the index of the task to be edited. + +Create a `dataArrIndex` variable. For its value, utilize the `findIndex()` method on `taskData`. Pass `item` as the parameter to its callback function and check if the `id` of `item` is equal to the `id` of the `parentElement` of `buttonEl`. + +# --hints-- + +You should not alter the `editTask` function. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*/) +``` + +You should use `const` to declare a `dataArrIndex` variable and set it to `taskData.findIndex()`. + +```js +assert.match(code, /const\s*editTask\s*=\s*\(buttonEl\)\s*=>\s*\{\s*const dataArrIndex\s*=\s*taskData\.findIndex\(/) +``` + +You should pass in `item` as the parameter of the `findIndex()` arrow function callback. Don't use curly braces. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)/) +``` + +Your arrow function callback should check if `item.id === buttonEl.parentElement.id`. + +```js +assert.match(code, /const\s*editTask\s+=\s*\(buttonEl\)\s*=>\s*\{\s*const\s+dataArrIndex\s*=\s*taskData\.findIndex\(\(item\)\s*=>\s*item\.id\s*===\s*buttonEl\.parentElement\.id\);?\s*\};?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md new file mode 100644 index 00000000000..14d527ecf40 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1061ca838611ed6a7d6b.md @@ -0,0 +1,373 @@ +--- +id: 64fb1061ca838611ed6a7d6b +title: Step 41 +challengeType: 0 +dashedName: step-41 +--- + +# --description-- + +Use square bracket notation to retrieve the task to be edited from the `taskData` array using the `dataArrIndex`. Then, assign it to the `currentTask` object to keep track of it. + +# --hints-- + +You should assign `taskData[dataArrIndex]` to `currentTask`. + +```js +assert.match(code, /currentTask\s*=\s*taskData\[dataArrIndex\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex((item) => item.id === buttonEl.parentElement.id); + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md new file mode 100644 index 00000000000..73ef19befcc --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1321e189a6136d200f77.md @@ -0,0 +1,389 @@ +--- +id: 64fb1321e189a6136d200f77 +title: Step 42 +challengeType: 0 +dashedName: step-42 +--- + +# --description-- + +The task to be edited is now in the `currentTask` object. Stage it for editing inside the input fields by setting the value of `titleInput` to `currentTask.title`, `dateInput` to `currentTask.date`, and `descriptionInput` to `currentTask.description`. + +# --hints-- + +You should set `titleInput.value` to `currentTask.title`. + +```js +assert.match(editTask.toString(), /titleInput\.value\s*=\s*currentTask\.title;?/) +``` + +You should set `dateInput.value` to `currentTask.date`. + +```js +assert.match(editTask.toString(), /dateInput\.value\s*=\s*currentTask\.date;?/) +``` + +You should set `descriptionInput.value` to `currentTask.description`. + +```js +assert.match(editTask.toString(), /descriptionInput\.value\s*=\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +const editTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + currentTask = taskData[dataArrIndex]; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md new file mode 100644 index 00000000000..c6312bf6bd0 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1436adef3e145b4c3501.md @@ -0,0 +1,386 @@ +--- +id: 64fb1436adef3e145b4c3501 +title: Step 43 +challengeType: 0 +dashedName: step-43 +--- + +# --description-- + +Set the `innerText` of the `addOrUpdateTaskBtn` button to `Update Task` and its `aria-label` to `Update Task` as well. + +# --hints-- + +You should set the inner text of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.innerText\s*=\s*("|')Update Task\1;?/) +``` + +You should set the `ariaLabel` of the `addOrUpdateTaskBtn` button to `Update Task` + +```js +assert.match(editTask.toString(), /addOrUpdateTaskBtn\.ariaLabel\s*=\s*("|')Update Task\1;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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; + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md new file mode 100644 index 00000000000..101870d098a --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb14d890415c14f93069ce.md @@ -0,0 +1,384 @@ +--- +id: 64fb14d890415c14f93069ce +title: Step 44 +challengeType: 0 +dashedName: step-44 +--- + +# --description-- + +Finally, display the `form` modal with the values of the input fields by using `classList` to toggle the `hidden` class on `taskForm`. + +# --hints-- + +You should use `classList` to toggle the class `hidden` on `taskForm`. + +```js +assert.match(editTask.toString(), /taskForm\.classList\.toggle\(('|")hidden\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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); + } + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); +} + +--fcc-editable-region-- +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + +} +--fcc-editable-region-- + +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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md new file mode 100644 index 00000000000..7ad62060776 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb154a7c48cd159924bb18.md @@ -0,0 +1,391 @@ +--- +id: 64fb154a7c48cd159924bb18 +title: Step 45 +challengeType: 0 +dashedName: step-45 +--- + +# --description-- + +At this point, editing a task won't reflect when you submit the task. To make the editing functional, go back to the `if` statement inside the `addOrUpdateTask` function. Create an `else` block and set `taskData[dataArrIndex]` to `taskObj`. + +# --hints-- + +Your `if` statement should have an `else` block. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*/) +``` + +Your `else` block should have the code `taskData[dataArrIndex] = taskObj`. + +```js +assert.match(code, /if\s*\(dataArrIndex\s*===\s*-1\)\s*\{\s*taskData\.unshift\(taskObj\);?\s*\}\s*else\s*\{\s*taskData\[dataArrIndex\]\s*=\s*taskObj;?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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, + }; + + --fcc-editable-region-- + if (dataArrIndex === -1) { + taskData.unshift(taskObj); + } + + --fcc-editable-region-- + + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md new file mode 100644 index 00000000000..4ec33172878 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb1c4dc0feb219149a7c7d.md @@ -0,0 +1,401 @@ +--- +id: 64fb1c4dc0feb219149a7c7d +title: Step 46 +challengeType: 0 +dashedName: step-46 +--- + +# --description-- + +If the user attempts to edit a task but decides not to make any changes before closing the form, there is no need to display the modal with the `Cancel` and `Discard` buttons. + +Inside the `closeTaskFormBtn` event listener, use `const` to create another variable named `formInputValuesUpdated`. Check if the user made changes while trying to edit a task by verifying that the `titleInput` value **is not equal to** `currentTask.title`, the `dateInput` value **is not equal to** `currentTask.date`, and the `descriptionInput` value **is not equal to** `currentTask.description`. + +# --hints-- + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*/) +``` + +Your `formInputValuesUpdated` variable should check if `titleInput.value` is not equal to `currentTask.title` or `dateInput.value` is not equal to `currentTask.date`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date/) +``` + +Your `formInputValuesUpdated` variable should have the value `titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description`. + +```js +assert.match(code, /const\s*formInputValuesUpdated\s*=\s*titleInput\.value\s*!==\s*currentTask\.title\s*\|\|\s*dateInput\.value\s*!==\s*currentTask\.date\s*\|\|\s*descriptionInput\.value\s*!==\s*currentTask\.description;?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + --fcc-editable-region-- + + --fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md new file mode 100644 index 00000000000..bb8a87acf60 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb285637fa1e1c222033e3.md @@ -0,0 +1,390 @@ +--- +id: 64fb285637fa1e1c222033e3 +title: Step 47 +challengeType: 0 +dashedName: step-47 +--- + +# --description-- + +Now add `formInputValuesUpdated` as the second mandatory condition in the `if` statement using the `AND` operator. + +This way, the `Cancel` and `Discard` buttons in the modal won't be displayed to the user if they haven't made any changes to the input fields while attempting to edit a task. + +# --hints-- + +Your `if` should have the condition `formInputsContainValues && formInputValuesUpdated`. + +```js +assert.match(code.split("if (formInputsContainValues"), /\s*&&\s*formInputValuesUpdated/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description; + +--fcc-editable-region-- + if (formInputsContainValues) { + confirmCloseDialog.showModal(); + } else { + reset(); + } +--fcc-editable-region-- +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + reset() +}); + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + addOrUpdateTask(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md new file mode 100644 index 00000000000..94fdc40185a --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fb29348a60361ccd45c1e2.md @@ -0,0 +1,414 @@ +--- +id: 64fb29348a60361ccd45c1e2 +title: Step 48 +challengeType: 0 +dashedName: step-48 +--- + +# --description-- + +`localStorage` offers methods for saving, retrieving, and deleting items. The items you save can be of any JavaScript data type. + +For instance, the `setItem()` method is used to save an item, and the `getItem()` method retrieves the item. To delete a specific item, you can utilize the `removeItem()` method, or if you want to delete all items in the storage, you can use `clear()`. + +Here's how you can save an item: + +```js +localStorage.setItem("key", value); // value could be string, number, or any other data type +``` + +A `myTaskArr` array has been provided for you. Use the `setItem()` method to save it with a key of `data`. + +After that, open your browser console and go to the `Applications` tab, select `Local Storage`, and the freeCodeCamp domain you see. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +Your `localStorage.setItem()` should have a key of `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*myTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md new file mode 100644 index 00000000000..2ce0b111937 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64fefebad99209211ec30537.md @@ -0,0 +1,404 @@ +--- +id: 64fefebad99209211ec30537 +title: Step 49 +challengeType: 0 +dashedName: step-49 +--- + +# --description-- + +If you check the `Application` tab of your browser console, you'll notice a series of `[object Object]`. This is because everything you save in `localStorage` needs to be in string format. + +To resolve the issue, wrap the data you're saving in the `JSON.stringify()` method. Then, check local storage again to observe the results. + +# --hints-- + +Your `localStorage.setItem()` should have a key of `"data"`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1/) +``` + +You should wrap `JSON.stringify()` around `myTaskArr`. + +```js +assert.match(code, /localStorage\.setItem\(("|')data\1,\s*JSON\.stringify\(myTaskArr\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +--fcc-editable-region-- +localStorage.setItem("data", myTaskArr); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md new file mode 100644 index 00000000000..1cb0c0c5930 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff0313700dad264d19dfe4.md @@ -0,0 +1,406 @@ +--- +id: 64ff0313700dad264d19dfe4 +title: Step 50 +challengeType: 0 +dashedName: step-50 +--- + +# --description-- + +Now that you have the `myTaskArr` array saved in `localStorage` correctly, you can retrieve it with `getItem()` by specifying the key you used to save the item. + +Use the `getItem()` method to retrieve the `myTaskArr` array and assign it to the variable `getTaskArr`. Then, log the `getTaskArr` variable to the console to see the result. + +# --hints-- + +You should use `const` to create a `getTaskArr` variable and assign `localStorage.getItem("data")` to it. + +```js +assert.match(code, /const\s*getTaskArr\s*=\s*localStorage\.getItem\(('|")data\1\);?/) +``` + +You should log the `getTaskArr` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArr\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md new file mode 100644 index 00000000000..608713897b5 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff04cc33779427a6412449.md @@ -0,0 +1,411 @@ +--- +id: 64ff04cc33779427a6412449 +title: Step 51 +challengeType: 0 +dashedName: step-51 +--- + +# --description-- + +The item you retrieve is a string, as you saved it with `JSON.stringify()`. To view it in its original form before saving, you need to use `JSON.parse()`. + +Use `getItem()` to retrieve the `myTaskArr` array again. This time, wrap it inside `JSON.parse()`, assign it to the variable `getTaskArrObj` and log the `getTaskArrObj` to the console. + +Check the console to see the difference between `getTaskArr` and `getTaskObj`. + +# --hints-- + +You should use `const` to create a `getTaskArrObj` variable and assign it to `JSON.parse(localStorage.getItem('data'));`. + +```js +assert.match(code, /const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\(('|")data\1\)\);?/) +``` + +You should log the `getTaskArrObj` variable to the console. + +```js +assert.match(code, /console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md new file mode 100644 index 00000000000..09bad9433fa --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff068e0426eb288874ed79.md @@ -0,0 +1,406 @@ +--- +id: 64ff068e0426eb288874ed79 +title: Step 52 +challengeType: 0 +dashedName: step-52 +--- + +# --description-- + +You can use `localStorage.removeItem()` to remove a specific item and `localStorage.clear()` to clear all items in the local storage. + +Remove the `data` item from local storage and open the console to observe the result. You should see `null`. + +# --hints-- + +You should use `localStorage.removeItem()` to remove the `data` item from the browser's local storage. + +```js +assert.match(code, /localStorage\.removeItem\(('|")data\1\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md new file mode 100644 index 00000000000..5dc3bd6e1d1 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff23daf176a92de95f24dc.md @@ -0,0 +1,413 @@ +--- +id: 64ff23daf176a92de95f24dc +title: Step 53 +challengeType: 0 +dashedName: step-53 +--- + +# --description-- + +Using `localStorage.clear()` won't just delete a single item from local storage but will remove all items. + +Remove `localStorage.removeItem()` and use `localStorage.clear()` instead. You don't need to pass in anything. You should also see `null` in the console. + +# --hints-- + +You should remove `localStorage.removeItem("data")`. + +```js +assert.notMatch(code, /localStorage\.removeItem\(('|")data\1\);/) +``` + +You should remove everything from the browser `local storage` with `localStorage.clear()`. + +```js +assert.match(code, /localStorage\.clear\(\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +--fcc-editable-region-- +localStorage.removeItem("data"); + +--fcc-editable-region-- + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md new file mode 100644 index 00000000000..0d6c42c989e --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/64ff24b80431f62ec6b93f65.md @@ -0,0 +1,404 @@ +--- +id: 64ff24b80431f62ec6b93f65 +title: Step 54 +challengeType: 0 +dashedName: step-54 +--- + +# --description-- + +Remove the `myTaskArr` array and all of the code for `localStorage` because you don't need them anymore. + +# --hints-- + +You should remove `myTaskArr` and all the code related to `localStorage` that you've just learned. + +```js +assert.notMatch(code, /const\s*myTaskArr\s*=\s*\[\s*\{\s*task:\s"Walk\s*the\s*Dog",\s*date:\s*"22-04-2022"\s*\},\s*\{\s*task:\s"Read\s*some\s*books",\s*date:\s*"02-11-2023"\s*\},\s*\{\s*task:\s"Watch\s*football",\s*date:\s*"10-08-2021"\s*\},\s*\];?\s*localStorage\.setItem\("data", JSON\.stringify\(myTaskArr\)\);\s*localStorage\.clear\(\);?\s*const\s*getTaskArr\s*=\s*localStorage\.getItem\("data"\)\s*console\.log\(getTaskArr\)\s*const\s*getTaskArrObj\s*=\s*JSON\.parse\(localStorage\.getItem\("data"\)\);?\s*console\.log\(getTaskArrObj\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); + +--fcc-editable-region-- +const myTaskArr = [ + { task: "Walk the Dog", date: "22-04-2022" }, + { task: "Read some books", date: "02-11-2023" }, + { task: "Watch football", date: "10-08-2021" }, +]; + +localStorage.setItem("data", JSON.stringify(myTaskArr)); + +localStorage.clear(); + +const getTaskArr = localStorage.getItem("data") +console.log(getTaskArr) + +const getTaskArrObj = JSON.parse(localStorage.getItem("data")); +console.log(getTaskArrObj); +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md new file mode 100644 index 00000000000..cbbcd4df6e1 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65003986d17d1e1865b269c0.md @@ -0,0 +1,404 @@ +--- +id: 65003986d17d1e1865b269c0 +title: Step 55 +challengeType: 0 +dashedName: step-55 +--- + +# --description-- + +Now you should save the task items to local storage when the user adds, updates, or removes a task. + +Inside the `addOrUpdateTask` function, use `setItem()` to save the tasks with a key of `data`, then pass the `taskData` array as its parameter. Ensure that you stringify the `taskData`. + +This would persist data once the user adds or updates tasks. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +assert.match(code, /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +assert.match(code, /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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; + } + --fcc-editable-region-- + + --fcc-editable-region-- + updateTaskContainer() + reset() +}; + +const updateTaskContainer = () => { + tasksContainer.innerHTML = ""; + + taskData.forEach( + ({ id, title, date, description }) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md new file mode 100644 index 00000000000..ec3a700096a --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650046832f92c01a35834bca.md @@ -0,0 +1,407 @@ +--- +id: 650046832f92c01a35834bca +title: Step 56 +challengeType: 0 +dashedName: step-56 +--- + +# --description-- + +You also want a deleted task to be removed from local storage. For this, you don't need the `removeItem()` or `clear()` methods. Since you already use `splice()` to remove the deleted task from `taskData`, all you need to do now is save `taskData` to local storage again. + +Use `setItem()` to save the `taskData` array again. Pass in `data` as the key and ensure that `taskData` is stringified before saving. + +# --hints-- + +You should use `localStorage.setItem()` to save the tasks to the browser `local storage`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(/) +``` + +You should pass in `"data"` as the first parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1/) +``` + +You should pass in `JSON.stringify(taskData)` as the second parameter of your `localStorage.setItem()`. + +```js +const splitter = code.split("taskData.splice(dataArrIndex, 1);") +assert.match(splitter[1], /localStorage\.setItem\(('|")data\1,\s*JSON\.stringify\(taskData\)\);?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = () => { + 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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +const deleteTask = (buttonEl) => { + const dataArrIndex = taskData.findIndex( + (item) => item.id === buttonEl.parentElement.id + ); + + buttonEl.parentElement.remove(); + taskData.splice(dataArrIndex, 1); + --fcc-editable-region-- + + --fcc-editable-region-- +} + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md new file mode 100644 index 00000000000..40cf6384c4e --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650048b0764f9c1b798200e2.md @@ -0,0 +1,392 @@ +--- +id: 650048b0764f9c1b798200e2 +title: Step 57 +challengeType: 0 +dashedName: step-57 +--- + +# --description-- + +If you add, update, or remove a task, it should reflect in the UI. However, that's not happening now because you have yet to retrieve the tasks. To do this, you need to modify your initial `taskData` to be an empty array. + +Set `taskData` to the retrieval of `data` from local storage **or** an empty array. Make sure you parse the data coming with `JSON.parse()` because you saved it as a string. + +# --hints-- + +You should set the `taskData` variable to `JSON.parse(localStorage.getItem("data")) || [];`. + +```js +assert.match(code, /const\s*taskData\s*=\s*JSON.parse\(localStorage\.getItem\(('|")data\1\)\)\s*\|\|\s*\[\];?/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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"); + +--fcc-editable-region-- +const taskData = []; +--fcc-editable-region-- +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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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; + 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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md new file mode 100644 index 00000000000..08ced41b63f --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65004ba581d03d1d5628b41c.md @@ -0,0 +1,776 @@ +--- +id: 65004ba581d03d1d5628b41c +title: Step 58 +challengeType: 0 +dashedName: step-58 +--- + +# --description-- + +You've retrieved the task item(s) now, but they still don't reflect in the UI when the page loads. However, they appear when you add a new task. + +To fix this issue, you can check if there's a task inside `taskData`, and then call the `updateTaskContainer()`. + +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 the condition `taskData.length`. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*/) +``` + +You should call the `updateTaskContainer` function in your `if` statement. + +```js +assert.match(code, /if\s*\(taskData\.length\)\s*\{\s*updateTaskContainer\(\);?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "Update Task"; + + taskForm.classList.toggle("hidden"); +} + +const reset = () => { + titleInput.value = ""; + dateInput.value = ""; + descriptionInput.value = ""; + taskForm.classList.toggle("hidden"); + currentTask = {}; +} + +--fcc-editable-region-- + +--fcc-editable-region-- + +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 + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 += ` +
+

Title: ${title}

+

Date: ${date}

+

Description: ${description}

+ + +
+ `) + ); +}; + + +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"; + addOrUpdateTaskBtn.ariaLabel = "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(); +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md new file mode 100644 index 00000000000..6b21e6888ce --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/650300a25b6f72964ab8aca6.md @@ -0,0 +1,314 @@ +--- +id: 650300a25b6f72964ab8aca6 +title: Step 13 +challengeType: 0 +dashedName: step-13 +--- + +# --description-- + +To make the `id` more unique, add another hyphen and use `Date.now()`. + +# --hints-- + +You should attach `-${Date.now()}` to the existing value of the `id` key. Don't forget it needs to be inside the template string. + +```js +assert.match(code, /const\s+taskObj\s*=\s*\{\s*id:\s*`\$\{titleInput\.value\.toLowerCase\(\)\.split\(('|")\s{1}\1\)\.join\(\1-\1\)\}-\$\{Date\.now\(\)\}`,?\s*\}/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +
+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id); + --fcc-editable-region-- + const taskObj = { + id: `${titleInput.value.toLowerCase().split(' ').join('-')}`, + }; + --fcc-editable-region-- +}); +``` diff --git a/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md new file mode 100644 index 00000000000..0c9fbc152b7 --- /dev/null +++ b/curriculum/challenges/ukrainian/15-javascript-algorithms-and-data-structures-22/learn-localstorage-by-building-a-todo-app/65099dbd8f137d58e5c0ff16.md @@ -0,0 +1,337 @@ +--- +id: 65099dbd8f137d58e5c0ff16 +title: Step 21 +challengeType: 0 +dashedName: step-21 +--- + +# --description-- + +Create one more `p` element and interpolate the `description` you destructured as the text. Also, create a `strong` element inside the paragraph with the text `Description:`. + +# --hints-- + +You should create a `p` element with `${description}` as the text. + +```js +assert.match(code, /

.*\$\{description\}<\/p>/) +``` + +You should create a `strong` element with the text `Description:` after the opening tag of your `p` element. + +```js +assert.match(code, /(?<=

)Description:\s*<\/strong>\s*/) +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + + + + Learn localStorage By Building a Todo App + + + + +

+

Todo App

+
+ + + +
+

Discard unsaved changes?

+
+ + +
+
+
+
+
+
+ + + + +``` + +```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 = {}; + +openTaskFormBtn.addEventListener("click", () => + taskForm.classList.toggle("hidden") +); + +closeTaskFormBtn.addEventListener("click", () => { + confirmCloseDialog.showModal(); +}); + +cancelBtn.addEventListener("click", () => confirmCloseDialog.close()); + +discardBtn.addEventListener("click", () => { + confirmCloseDialog.close(); + taskForm.classList.toggle("hidden"); +}); + + +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + + 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); + } + + taskData.forEach(({id, title, date, description}) => + (tasksContainer.innerHTML += ` +
+

Title: ${title}

+

Date: ${date}

+--fcc-editable-region-- + +--fcc-editable-region-- +
+ `) + ); +}); +``` diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md index 15b44212387..a9bdeaf7818 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-10-summation-of-primes.md @@ -71,8 +71,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md index 738ca0d9eb9..8679e168031 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-43-sub-string-divisibility.md @@ -73,7 +73,7 @@ substringDivisibility(5); ```js function substringDivisibility(n) { - function isSubDivisable(digits) { + function isSubDivisible(digits) { const factors = [2, 3, 5, 7, 11, 13, 17]; for (let i = 1; i < digits.length - 2; i++) { @@ -108,17 +108,17 @@ function substringDivisibility(n) { } const allowedDigits = [...new Array(n + 1).keys()]; - const divisablePandigitals = []; + const divisiblePandigitals = []; heapsPermutations( allowedDigits.length, allowedDigits, - isSubDivisable, - divisablePandigitals + isSubDivisible, + divisiblePandigitals ); let sum = 0; - for (let i = 0; i < divisablePandigitals.length; i++) { - sum += divisablePandigitals[i]; + for (let i = 0; i < divisiblePandigitals.length; i++) { + sum += divisiblePandigitals[i]; } return sum; diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md index 4a2b0139011..3eda97c4b0a 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-50-consecutive-prime-sum.md @@ -67,8 +67,8 @@ class PrimeSeive { const prime = 2 * i + 3; primes.push(prime); // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } @@ -101,10 +101,10 @@ class PrimeSeive { }; function consecutivePrimeSum(limit) { - // Initalize seive + // Initialize seive const primeSeive = new PrimeSeive(limit); - // Initalize for longest sum < 100 + // Initialize for longest sum < 100 let bestPrime = 41; let bestI = 0; let bestJ = 5; diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md index bfdcef71f2b..c249204d6f8 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-1-to-100/problem-51-prime-digit-replacements.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md index a165013c7c8..a7a02c2cc35 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-101-to-200/problem-122-efficient-exponentiation.md @@ -31,10 +31,10 @@ $$\begin{align} & n × n = n^2\\\\ # --hints-- -`efficientExponentation()` має повернути `1582`. +`efficientExponentiation()` має повернути `1582`. ```js -assert.strictEqual(efficientExponentation(), 1582); +assert.strictEqual(efficientExponentiation(), 1582); ``` # --seed-- @@ -42,12 +42,12 @@ assert.strictEqual(efficientExponentation(), 1582); ## --seed-contents-- ```js -function efficientExponentation() { +function efficientExponentiation() { return true; } -efficientExponentation(); +efficientExponentiation(); ``` # --solutions-- diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md index 1e51877b454..f56512c97ed 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-101-to-200/problem-128-hexagonal-tile-differences.md @@ -67,8 +67,8 @@ class PrimeSeive { // Mark value in seive array const prime = 2 * i + 3; // Mark all multiples of this number as false (not prime) - const primeSqaredIndex = 2 * i ** 2 + 6 * i + 3; - for (let j = primeSqaredIndex; j < upper; j += prime) { + const primeSquaredIndex = 2 * i ** 2 + 6 * i + 3; + for (let j = primeSquaredIndex; j < upper; j += prime) { seive[j] = false; } } diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md index fef8122064b..97e9afd30f6 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-201-to-300/problem-206-concealed-square.md @@ -35,7 +35,7 @@ concealedSquare(); ```js // Check if n**2 matches the pattern -function squareMatchs(n) { +function squareMatches(n) { // Need BigInt due to size of values let nSquared = (BigInt(n) * BigInt(n)).toString(); @@ -55,8 +55,8 @@ function concealedSquare() { for (let x = maxSquareRoot; x >= minSquareRoot; x -= 10) { // Note: 3*3 = 9 and 7*7 = 49 are only trailing digits // that can produce 9 as trailing digit in square - if (squareMatchs(x + 3)) return (x + 3)*10; - if (squareMatchs(x + 7)) return (x + 7)*10; + if (squareMatches(x + 3)) return (x + 3)*10; + if (squareMatches(x + 7)) return (x + 7)*10; } return -1; } diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-401-sum-of-squares-of-divisors.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-401-sum-of-squares-of-divisors.md index 3767064301c..d5c752eb244 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-401-sum-of-squares-of-divisors.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-401-sum-of-squares-of-divisors.md @@ -14,9 +14,9 @@ dashedName: problem-401-sum-of-squares-of-divisors Нехай $\sigma_2(n)$ позначає суму квадратів дільників $n$. Отже, $\sigma_2(6) = 50$. -Нехай $\Sigma_2$ позначає суматорну функцію $\sigma_2$, тобто $\Sigma_2(n) = \sum \sigma_2(i)$ для $i$ від $1 до n$. Першими шістьма значеннями $\Sigma_2$ є 1, 6, 16, 37, 63 та 113. +Нехай $\Sigma_2$ позначає суматорну функцію $\sigma_2$, тобто $\Sigma_2(n) = \sum \sigma_2(i)$ для $i$ від $1 до n$. Першими шістьма значеннями $\Sigma_2$ будуть 1, 6, 16, 37, 63 та 113. -Знайдіть $\Sigma_2({10}^{15})$ modulo ${10}^9$. +Знайдіть $\Sigma_2({10}^{15})$ mod ${10}^9$. # --hints-- diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-402-integer-valued-polynomials.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-402-integer-valued-polynomials.md index 3eee869e006..0159ccfeed1 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-402-integer-valued-polynomials.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-402-integer-valued-polynomials.md @@ -8,11 +8,11 @@ dashedName: problem-402-integer-valued-polynomials # --description-- -Можна довести, що многочлен $n^4 + 4n^3 + 2n^2 + 5n$ кратний 6 для кожного цілого $n$. Також можна довести, що 6 є найбільшим цілим числом, яке задовільняє цю властивість. +Можна довести, що многочлен $n^4 + 4n^3 + 2n^2 + 5n$ кратний 6 за кожного цілого $n$. Також можна довести, що 6 є найбільшим цілим числом, яке задовільняє цю властивість. -Визначте $M(a, b, c)$ як максимальне значення $m$, за якого $n^4 + an^3 + bn^2 + cn$ є кратним $m$ для всіх цілих $n$. Наприклад, $M(4, 2, 5) = 6$. +Визначимо $M(a, b, c)$ як максимальне значення $m$, за якого $n^4 + an^3 + bn^2 + cn$ є кратним $m$ для всіх цілих $n$. Наприклад, $M(4, 2, 5) = 6$. -Також визначте $S(N)$ як суму $M(a, b, c)$ для всіх $0 < a, b, c ≤ N$. +Також визначимо $S(N)$ як суму $M(a, b, c)$ для всіх $0 < a, b, c ≤ N$. Можна довести, що $S(10) = 1\\,972$ та $S(10\\,000) = 2\\,024\\,258\\,331\\,114$. diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-404-crisscross-ellipses.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-404-crisscross-ellipses.md index 3021d38176c..be5d555febe 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-404-crisscross-ellipses.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-404-crisscross-ellipses.md @@ -10,7 +10,7 @@ dashedName: problem-404-crisscross-ellipses $E_a$ є еліпсом, заданим рівнянням $x^2 + 4y^2 = 4a^2$. -$E_a'$ є зображенням еліпса $E_a$, перевернутим на $θ$ градусів проти годинникової стрілки навколо початку координат $O(0, 0)$ для $0° < θ < 90°$. +$E_a'$ є зображенням еліпса $E_a$, перевернутим на $θ$ градусів проти годинникової стрілки навколо початку координат $O(0, 0)$ за умови $0° < θ < 90°$. еліпс E_a та еліпс E_a' повернутий на θ градусів diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-413-one-child-numbers.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-413-one-child-numbers.md index a777a0395cc..9f38f8ae7ba 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-413-one-child-numbers.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-413-one-child-numbers.md @@ -10,7 +10,7 @@ dashedName: problem-413-one-child-numbers Додатне $d$-значне число (без початкових нулів) називають однодітним, якщо лише одне витягнуте число ділиться на $d$. -Наприклад, 5671 є чотиризначним однодітним числом. Серед усіх його витягнутих чисел 5, 6, 7, 1, 56, 67, 71, 567, 671 та 5671, лише 56 ділиться на 4. +Наприклад, 5671 є чотиризначним однодітним числом. Серед усіх його витягнутих чисел (5, 6, 7, 1, 56, 67, 71, 567, 671 та 5671), лише 56 ділиться на 4. Схожим чином, 104 є тризначним однодітним числом, оскільки лише 0 ділиться на 3. 1132451 є семизначним однодітним числом, оскільки лише 245 ділиться на 7. diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-414-kaprekar-constant.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-414-kaprekar-constant.md index 2a0552b53f1..f9830256f6a 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-414-kaprekar-constant.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-414-kaprekar-constant.md @@ -25,7 +25,7 @@ $$\begin{align} & 8730 - 0378 = 8352 \\\\ Наприклад, основа 15: ${(10, 4, 14, 9, 5)}\_{15}$ основа 21: $(14, 6, 20, 13, 7)\_{21}$ -Визначимо $C_b$ як сталу Капрекара з основою $b$ для 5 цифр. Визначте функцію $sb(i)$ рівною: +Визначимо $C_b$ як сталу Капрекара з основою $b$ для 5 цифр. Визначимо функцію $sb(i)$ рівною: - 0, якщо $i = C_b$ або $i$ записано в основі $b$ з 5 однакових цифр - кількості повторень, необхідних для перетворення Капрекара в основі $b$, щоб отримати $C_b$ в інших випадках diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-427-n-sequences.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-427-n-sequences.md index c511de908f0..d5727820ef4 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-427-n-sequences.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-427-n-sequences.md @@ -12,7 +12,7 @@ dashedName: problem-427-n-sequences Наприклад, послідовність $S = \\{1, 5, 5, 10, 7, 7, 7, 2, 3, 7\\}$ є 10-послідовністю. -Нехай $L(S)$ буде довжиною найдовшої сусідньої вкладеної послідовності $S$ з однакових значень для послідовності $S$. Наприклад, для послідовності $S$ вище $L(S) = 3$, оскільки вона містить три сусідні 7. +Нехай $L(S)$ буде довжиною найдовшої сусідньої вкладеної послідовності $S$ з однакових значень для послідовності $S$. Наприклад, для послідовності $S$, поданої вище, $L(S) = 3$, оскільки вона містить три сусідні 7. Нехай $f(n) = \sum L(S)$ для всіх $n$-послідовностей $S$. diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-429-sum-of-squares-of-unitary-divisors.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-429-sum-of-squares-of-unitary-divisors.md index a3623a7d4a8..4892110b941 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-429-sum-of-squares-of-unitary-divisors.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-429-sum-of-squares-of-unitary-divisors.md @@ -16,7 +16,7 @@ dashedName: problem-429-sum-of-squares-of-unitary-divisors Нехай $S(n)$ представляє суму квадратів унітарних дільників $n$. Таким чином, $S(4!) = 650$. -Знайдіть $S(100\\,000\\,000!)$ за модулем $1\\,000\\,000\\,009$. +Знайдіть $S(100\\,000\\,000!)$ mod $1\\,000\\,000\\,009$. # --hints-- diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-437-fibonacci-primitive-roots.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-437-fibonacci-primitive-roots.md index eb8683f3860..994fcb9c21f 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-437-fibonacci-primitive-roots.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-437-fibonacci-primitive-roots.md @@ -1,6 +1,6 @@ --- id: 5900f5241000cf542c510036 -title: 'Problem 437: Fibonacci primitive roots' +title: 'Завдання 437: примітивні корені Фібоначчі' challengeType: 1 forumTopicId: 302108 dashedName: problem-437-fibonacci-primitive-roots @@ -8,13 +8,13 @@ dashedName: problem-437-fibonacci-primitive-roots # --description-- -When we calculate $8^n$ modulo 11 for $n = 0$ to 9 we get: 1, 8, 9, 6, 4, 10, 3, 2, 5, 7. +Якщо вичислити $8^n$ mod 11 для n від 0 до 9, то отримаємо 1, 8, 9, 6, 4, 10, 3, 2, 5, 7. -As we see all possible values from 1 to 10 occur. So 8 is a primitive root of 11. +Як бачимо, ми отримали всі можливі значення від 1 до 10. Отже, 8 є примітивним коренем 11. -But there is more: +Проте це не все: -If we take a closer look we see: +Якщо розглянути детальніше, то: $$\begin{align} & 1 + 8 = 9 \\\\ & 8 + 9 = 17 ≡ 6\bmod 11 \\\\ & 9 + 6 = 15 ≡ 4\bmod 11 \\\\ @@ -22,11 +22,11 @@ $$\begin{align} & 1 + 8 = 9 \\\\ & 10 + 3 = 13 ≡ 2\bmod 11 \\\\ & 3 + 2 = 5 \\\\ & 2 + 5 = 7 \\\\ & 5 + 7 = 12 ≡ 1\bmod 11. \end{align}$$ -So the powers of 8 mod 11 are cyclic with period 10, and $8^n + 8^{n + 1} ≡ 8^{n + 2} (\text{mod } 11)$. 8 is called a Fibonacci primitive root of 11. +Отже, степені 8 mod 11 циклічні з періодом 10, а $8^n + 8^{n + 1} ≡ 8^{n + 2} (\text{mod } 11)$. Число 8 називають примітивним коренем Фібоначчі по модулю 11. -Not every prime has a Fibonacci primitive root. There are 323 primes less than 10000 with one or more Fibonacci primitive roots and the sum of these primes is 1480491. +Не кожне просте число має примітивний корінь Фібоначчі. Існує 323 простих чисел, менших за 10000, з одним чи більше примітивними коренями Фібоначчі, а сума цих простих чисел дорівнює 1480491. -Find the sum of the primes less than $100\\,000\\,000$ with at least one Fibonacci primitive root. +Знайдіть суму простих чисел, менших за $100\\,000\\,000$ з принаймні одним примітивним коренем Фібоначчі. # --hints-- diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-438-integer-part-of-polynomial-equations-solutions.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-438-integer-part-of-polynomial-equations-solutions.md index 4dca9216e0d..0a8b5e33268 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-438-integer-part-of-polynomial-equations-solutions.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-438-integer-part-of-polynomial-equations-solutions.md @@ -1,6 +1,6 @@ --- id: 5900f5231000cf542c510034 -title: 'Problem 438: Integer part of polynomial equation''s solutions' +title: 'Завдання 438: ціла частина розв’язків поліноміального рівняння' challengeType: 1 forumTopicId: 302109 dashedName: problem-438-integer-part-of-polynomial-equations-solutions @@ -8,18 +8,18 @@ dashedName: problem-438-integer-part-of-polynomial-equations-solutions # --description-- -For an $n$-tuple of integers $t = (a_1, \ldots, a_n)$, let $(x_1, \ldots, x_n)$ be the solutions of the polynomial equation $x^n + a_1x^{n - 1} + a_2x^{n - 2} + \ldots + a_{n - 1}x + a_n = 0$. +Нехай $(x_1, \ldots, x_n)$ будуть розв’язками для поліноміального рівняння $x^n + a_1x^{n - 1} + a_2x^{n - 2} + \ldots + a_{n - 1}x + a_n = 0$ для кортежу з $n$ цілих чисел $t = (a_1, \ldots, a_n)$. -Consider the following two conditions: +Розглянемо дві умови: -- $x_1, \ldots, x_n$ are all real. -- If $x_1, ..., x_n$ are sorted, $⌊x_i⌋ = i$ for $1 ≤ i ≤ n$. ($⌊·⌋:$ floor function.) +- $x_1, \ldots, x_n$ є дійсними числами. +- Якщо $x_1, ..., x_n$ відсортовані, то $⌊x_i⌋ = i$ за умови $1 ≤ i ≤ n$. ($⌊·⌋:$ функція підлоги.) -In the case of $n = 4$, there are 12 $n$-tuples of integers which satisfy both conditions. +Якщо $n = 4$, то існує 12 кортежів з $n$ цілих чисел, що задовільняють обидві умови. -We define $S(t)$ as the sum of the absolute values of the integers in $t$. +Визначимо $S(t)$ як суму абсолютних значень цілих чисел в $t$. -For $n = 4$ we can verify that $\sum S(t) = 2087$ for all $n$-tuples $t$ which satisfy both conditions. +Якщо $n = 4$, то можна довести, що $\sum S(t) = 2087$ для всіх кортежів $t$ з $n$ цілих чисел, що задовільняють обидві умови. Знайдіть $\sum S(t)$ за умови $n = 7$. diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-440-gcd-and-tiling.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-440-gcd-and-tiling.md index 566c43cc070..c40b615bedd 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-440-gcd-and-tiling.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-440-gcd-and-tiling.md @@ -1,6 +1,6 @@ --- id: 5900f5241000cf542c510037 -title: 'Problem 440: GCD and Tiling' +title: 'Завдання 440: НСД і покриття плиткою' challengeType: 1 forumTopicId: 302112 dashedName: problem-440-gcd-and-tiling @@ -8,19 +8,19 @@ dashedName: problem-440-gcd-and-tiling # --description-- -We want to tile a board of length $n$ and height 1 completely, with either 1 × 2 blocks or 1 × 1 blocks with a single decimal digit on top: +Нам потрібно обкласти панель довжиною $n$ і висотою 1 за допомогою блоків 1 × 2 або 1 × 1, позначених цифрою: -ten blocks 1x1 with single decimal digit on top, and 1x2 block +десять блоків 1x1, позначених цифрою зверху, і блок 1х2 -For example, here are some of the ways to tile a board of length $n = 8$: +Наприклад, ось декілька способів обкласти панель довжиною $n = 8$: -examples of ways to tile a board of length n = 8 +декілька способів обкласти панель довжиною n = 8 -Let $T(n)$ be the number of ways to tile a board of length $n$ as described above. +Нехай $T(n)$ буде кількістю способів обкласти панелі довжиною $n$, як описано вище. Наприклад, $T(1) = 10$ та $T(2) = 101$. -Let $S(L)$ be the triple sum $\sum_{a, b, c} gcd(T(c^a), T(c^b))$ for $1 ≤ a, b, c ≤ L$. +Нехай $S(L)$ буде потрійною сумою $\sum_{a, b, c} нсд(T(c^a), T(c^b))$ за умови $1 ≤ a, b, c ≤ L$. Наприклад: diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-441-the-inverse-summation-of-coprime-couples.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-441-the-inverse-summation-of-coprime-couples.md index 78032a30591..74f6394571b 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-441-the-inverse-summation-of-coprime-couples.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-441-the-inverse-summation-of-coprime-couples.md @@ -1,6 +1,6 @@ --- id: 5900f5261000cf542c510038 -title: 'Problem 441: The inverse summation of coprime couples' +title: 'Завдання 441: обернена сума пар взаємно простих чисел' challengeType: 1 forumTopicId: 302113 dashedName: problem-441-the-inverse-summation-of-coprime-couples @@ -8,15 +8,15 @@ dashedName: problem-441-the-inverse-summation-of-coprime-couples # --description-- -For an integer $M$, we define $R(M)$ as the sum of $\frac{1}{p·q}$ for all the integer pairs $p$ and $q$ which satisfy all of these conditions: +Для цілого числа $M$ визначимо $R(M)$ як суму $\frac{1}{p·q}$ для всіх пар цілих чисел $p$ та $q$, які задовільняють такі умови: - $1 ≤ p < q ≤ M$ - $p + q ≥ M$ -- $p$ and $q$ are coprime. +- $p$ та $q$ є взаємно простими числами. -We also define $S(N)$ as the sum of $R(i)$ for $2 ≤ i ≤ N$. +Також визначимо $S(N)$ як суму $R(i)$ за умови $2 ≤ i ≤ N$. -We can verify that $S(2) = R(2) = \frac{1}{2}$, $S(10) ≈ 6.9147$ and $S(100) ≈ 58.2962$. +Можна довести, що $S(2) = R(2) = \frac{1}{2}$, $S(10) ≈ 6.9147$ та $S(100) ≈ 58.2962$. Знайдіть $S({10}^7)$. Дайте відповідь, заокруглену до чотирьох знаків після коми. diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-442-eleven-free-integers.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-442-eleven-free-integers.md index d75ec5b6d6f..0ef6d7199b2 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-442-eleven-free-integers.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-442-eleven-free-integers.md @@ -1,6 +1,6 @@ --- id: 5900f5271000cf542c510039 -title: 'Problem 442: Eleven-free integers' +title: 'Завдання 442: цілі числа «без-11»' challengeType: 1 forumTopicId: 302114 dashedName: problem-442-eleven-free-integers @@ -8,11 +8,11 @@ dashedName: problem-442-eleven-free-integers # --description-- -An integer is called eleven-free if its decimal expansion does not contain any substring representing a power of 11 except 1. +Ціле число називають «без-11», якщо його десяткове представлення не містить жодного рядка, яке є степенем числа 11, окрім 1. -For example, 2404 and 13431 are eleven-free, while 911 and 4121331 are not. +Наприклад, 2404 та 13431 є числами «без-11», а 911 та 4121331 — ні. -Let $E(n)$ be the $n$th positive eleven-free integer. Наприклад, $E(3) = 3$, $E(200) = 213$ та $E(500\\,000) = 531\\,563$. +Нехай $E(n)$ буде $n$-ним натуральним числом «без-11». Наприклад, $E(3) = 3$, $E(200) = 213$ та $E(500\\,000) = 531\\,563$. Знайдіть $E({10}^{18})$. diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-444-the-roundtable-lottery.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-444-the-roundtable-lottery.md index 2efe6d3fd9c..fb121fcb512 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-444-the-roundtable-lottery.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-444-the-roundtable-lottery.md @@ -1,6 +1,6 @@ --- id: 5900f52a1000cf542c51003b -title: 'Problem 444: The Roundtable Lottery' +title: 'Завдання 444: лотерея за круглим столом' challengeType: 1 forumTopicId: 302116 dashedName: problem-444-the-roundtable-lottery @@ -8,24 +8,24 @@ dashedName: problem-444-the-roundtable-lottery # --description-- -A group of $p$ people decide to sit down at a round table and play a lottery-ticket trading game. Each person starts off with a randomly-assigned, unscratched lottery ticket. Each ticket, when scratched, reveals a whole-pound prize ranging anywhere from £1 to £$p$, with no two tickets alike. The goal of the game is for each person to maximize his ticket winnings upon leaving the game. +Група з $p$ людей вирішила сісти за круглий стіл та зіграти гру в обмін лотерейними білетами. Кожен гравець починає гру з випадковим неподряпаним білетом. Під захисним покриттям на кожному білеті написано виграш від £1 до £$p$, причому всі білети різні. Мета гри для кожного з учасників полягає в тому, щоб максимально збільшити свій виграш до кінця гри. -An arbitrary person is chosen to be the first player. Going around the table, each player has only one of two options: +Першим гравцем обирають довільну людину. Наступні гравці ходять по колу. Кожен гравець має два варіанти дій: -1. The player can scratch his ticket and reveal its worth to everyone at the table. -2. The player can trade his unscratched ticket for a previous player's scratched ticket, and then leave the game with that ticket. The previous player then scratches his newly-acquired ticket and reveals its worth to everyone at the table. +1. Гравець може стерти захисне покриття білету та показати його вартість всім за столом. +2. Гравець може обміняти свій білет з нестертим покриттям на білет попереднього гравця зі стертим покриттям та завершити гру з цим білетом. Потім попередній гравець стирає покриття нового білету та оголошує його вартість всім за столом. -The game ends once all tickets have been scratched. All players still remaining at the table must leave with their currently-held tickets. +Гра закінчується, коли зі всіх білетів стерто покриття. Всі гравці, які залишилися за столом, мають закінчити гру зі своїми поточними білетами. -Assume that each player uses the optimal strategy for maximizing the expected value of his ticket winnings. +Припустимо, що кожен гравець використовує оптимальну стратегію, щоб збільшити очікуваний виграш свого білету. -Let $E(p)$ represent the expected number of players left at the table when the game ends in a game consisting of $p$ players (e.g. $E(111) = 5.2912$ when rounded to 5 significant digits). +Нехай $E(p)$ буде очікуваною кількістю гравців, які залишились за столом під кінець гри, в яку грали $p$ гравців (тобто $E(111) = 5.2912$, якщо заокруглити до 5 знаків). -Let $S_1(N) = \displaystyle\sum_{p = 1}^N E(p)$. +Нехай $S_1(N) = \displaystyle\sum_{p = 1}^N E(p)$. -Let $S_k(N) = \displaystyle\sum_{p = 1}^N S_{k - 1}(p)$ for $k > 1$. +Нехай $S_k(N) = \displaystyle\sum_{p = 1}^N S_{k - 1}(p)$ за умови $k > 1$. -Find $S_{20}({10}^{14})$ and write the answer as a string in scientific notation rounded to 10 significant digits. Use a lowercase `e` to separate mantissa and exponent. For example, the answer for $S_3(100)$ would be `5.983679014e5`. +Знайдіть $S_{20}({10}^{14})$ та надайте відповідь у вигляді рядка в експоненційному записі, заокругливши до десяти знаків після коми. Використайте `e` в нижньому регістрі, щоб розділити мантису та показник степеня. Наприклад, відповіддю для $S_3(100)$ буде `5.983679014e5`. # --hints-- diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-445-retractions-a.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-445-retractions-a.md index 392f961a424..671fa3d28b6 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-445-retractions-a.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-445-retractions-a.md @@ -1,6 +1,6 @@ --- id: 5900f52a1000cf542c51003c -title: 'Problem 445: Retractions A' +title: 'Завдання 445: ретракції А' challengeType: 1 forumTopicId: 302117 dashedName: problem-445-retractions-a @@ -8,13 +8,13 @@ dashedName: problem-445-retractions-a # --description-- -For every integer $n > 1$, the family of functions $f_{n, a, b}$ is defined by: +Сімейство функцій $f_{n, a, b}$ для кожного цілого числа $n > 1$ задано як: -$f_{n, a, b}(x) ≡ ax + b\bmod n$ for $a, b, x$ integer and $0 \lt a \lt n$, $0 \le b \lt n$, $0 \le x \lt n$. +$f_{n, a, b}(x) ≡ ax + b\bmod n$ для цілих $a, b, x$ та $0 \lt a \lt n$, $0 \le b \lt n$, $0 \le x \lt n$. -We will call $f_{n, a, b}$ a retraction if $f_{n, a, b}(f_{n, a, b}(x)) \equiv f_{n, a, b}(x)\bmod n$ for every $0 \le x \lt n$. +Назвемо $f_{n, a, b}$ ретракцією, якщо $f_{n, a, b}(f_{n, a, b}(x)) \equiv f_{n, a, b}(x)\bmod n$ для кожного $0 \le x \lt n$. -Let $R(n)$ be the number of retractions for $n$. +Нехай $R(n)$ буде кількістю ретракцій для $n$. Дано diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-446-retractions-b.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-446-retractions-b.md index 72d10821a92..e45c197304d 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-446-retractions-b.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-446-retractions-b.md @@ -1,6 +1,6 @@ --- id: 5900f52c1000cf542c51003d -title: 'Problem 446: Retractions B' +title: 'Завдання 446: ретракції В' challengeType: 1 forumTopicId: 302118 dashedName: problem-446-retractions-b @@ -8,19 +8,19 @@ dashedName: problem-446-retractions-b # --description-- -For every integer $n > 1$, the family of functions $f_{n, a, b}$ is defined by: +Сімейство функцій $f_{n, a, b}$ для кожного цілого числа $n > 1$ задано як: -$f_{n, a, b}(x) ≡ ax + b\bmod n$ for $a, b, x$ integer and $0 \lt a \lt n$, $0 \le b \lt n$, $0 \le x \lt n$. +$f_{n, a, b}(x) ≡ ax + b\bmod n$ для цілих $a, b, x$ та $0 \lt a \lt n$, $0 \le b \lt n$, $0 \le x \lt n$. -We will call $f_{n, a, b}$ a retraction if $f_{n, a, b}(f_{n, a, b}(x)) \equiv f_{n, a, b}(x)\bmod n$ for every $0 \le x \lt n$. +Назвемо $f_{n, a, b}$ ретракцією, якщо $f_{n, a, b}(f_{n, a, b}(x)) \equiv f_{n, a, b}(x)\bmod n$ для кожного $0 \le x \lt n$. -Let $R(n)$ be the number of retractions for $n$. +Нехай $R(n)$ буде кількістю ретракцій для $n$. $F(N) = \displaystyle\sum_{n = 1}^N R(n^4 + 4)$. $F(1024) = 77\\,532\\,377\\,300\\,600$. -Знайдіть $F({10}^7)$. Give your answer modulo $1\\,000\\,000\\,007$. +Знайдіть $F({10}^7)$. Дайте відповідь за модулем $1\\,000\\,000\\,007$. # --hints-- diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-447-retractions-c.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-447-retractions-c.md index f5e85ea2acf..b5cd8e5ad4c 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-447-retractions-c.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-447-retractions-c.md @@ -1,6 +1,6 @@ --- id: 5900f52c1000cf542c51003e -title: 'Problem 447: Retractions C' +title: 'Завдання 447: ретракції С' challengeType: 1 forumTopicId: 302119 dashedName: problem-447-retractions-c @@ -8,19 +8,19 @@ dashedName: problem-447-retractions-c # --description-- -For every integer $n > 1$, the family of functions $f_{n, a, b}$ is defined by: +Сімейство функцій $f_{n, a, b}$ для кожного цілого числа $n > 1$ задано як: -$f_{n, a, b}(x) ≡ ax + b\bmod n$ for $a, b, x$ integer and $0 \lt a \lt n$, $0 \le b \lt n$, $0 \le x \lt n$. +$f_{n, a, b}(x) ≡ ax + b\bmod n$ для цілих $a, b, x$ та $0 \lt a \lt n$, $0 \le b \lt n$, $0 \le x \lt n$. -We will call $f_{n, a, b}$ a retraction if $f_{n, a, b}(f_{n, a, b}(x)) \equiv f_{n, a, b}(x)\bmod n$ for every $0 \le x \lt n$. +Назвемо $f_{n, a, b}$ ретракцією, якщо $f_{n, a, b}(f_{n, a, b}(x)) \equiv f_{n, a, b}(x)\bmod n$ для кожного $0 \le x \lt n$. -Let $R(n)$ be the number of retractions for $n$. +Нехай $R(n)$ буде кількістю ретракцій для $n$. $F(N) = \displaystyle\sum_{n = 2}^N R(n)$. $F({10}^7) ≡ 638\\,042\\,271\bmod 1\\,000\\,000\\,007$. -Знайдіть $F({10}^{14})$. Give your answer modulo $1\\,000\\,000\\,007$. +Знайдіть $F({10}^{14})$. Дайте відповідь за модулем $1\\,000\\,000\\,007$. # --hints-- diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-450-hypocycloid-and-lattice-points.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-450-hypocycloid-and-lattice-points.md index b952f00d2b2..8635d0f95e5 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-450-hypocycloid-and-lattice-points.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-450-hypocycloid-and-lattice-points.md @@ -8,26 +8,26 @@ dashedName: problem-450-hypocycloid-and-lattice-points # --description-- -A hypocycloid is the curve drawn by a point on a small circle rolling inside a larger circle. The parametric equations of a hypocycloid centered at the origin, and starting at the right most point is given by: +Гіпоциклоїда — це крива, яка окреслюється точкою малого кола, що котиться всередині більшого кола. Параметричні рівняння гіпоциклоїди з центром на початку координат, починаючи з дальньої правої точки, задано як: $$x(t) = (R - r) \cos(t) + r \cos(\frac{R - r}{r}t)$$ $$y(t) = (R - r) \sin(t) - r \sin(\frac{R - r}{r} t)$$ -Where $R$ is the radius of the large circle and $r$ the radius of the small circle. +Де $R$ є радіусом великого кола, а $r$ є радіусом малого кола. -Let $C(R, r)$ be the set of distinct points with integer coordinates on the hypocycloid with radius $R$ and $r$ and for which there is a corresponding value of $t$ such that $\sin(t)$ and $\cos(t)$ are rational numbers. +Нехай $C(R, r)$ буде множиною окремих точок з цілочисловими координатами на гіпоциклоїді з радіусами $R$ та $r$, для яких існує відповідне значення $t$, за якого $\sin(t)$ та $\cos(t)$ є раціональними числами. -Let $S(R, r) = \sum\_{(x,y) \in C(R, r)} |x| + |y|$ be the sum of the absolute values of the $x$ and $y$ coordinates of the points in $C(R, r)$. +Нехай $S(R, r) = \sum\_{(x,y) \in C(R, r)} |x| + |y|$ буде сумою абсолютних значень координат $x$ та $y$ точок в $C(R, r)$. -Let $T(N) = \sum_{R = 3}^N \sum_{r=1}^{\left\lfloor \frac{R - 1}{2} \right\rfloor} S(R, r)$ be the sum of $S(R, r)$ for $R$ and $r$ positive integers, $R\leq N$ and $2r < R$. +Нехай $T(N) = \sum_{R = 3}^N \sum_{r=1}^{\left\lfloor \frac{R - 1}{2} \right\rfloor} S(R, r)$ буде сумою $S(R, r)$ для натуральних $R$ та $r$, $R\leq N$ та $2r < R$. Дано: $$\begin{align} C(3, 1) = & \\{(3, 0), (-1, 2), (-1,0), (-1,-2)\\} \\\\ C(2500, 1000) = & \\{(2500, 0), (772, 2376), (772, -2376), (516, 1792), (516, -1792), (500, 0), (68, 504), \\\\ &(68, -504),(-1356, 1088), (-1356, -1088), (-1500, 1000), (-1500, -1000)\\} \end{align}$$ -**Note:** (-625, 0) is not an element of $C(2500, 1000)$ because $\sin(t)$ is not a rational number for the corresponding values of $t$. +**Примітка:** (-625, 0) не є елементом $C(2500, 1000)$, оскільки $\sin(t)$ не є раціональним числом для відповідних значень $t$. $S(3, 1) = (|3| + |0|) + (|-1| + |2|) + (|-1| + |0|) + (|-1| + |-2|) = 10$ diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-451-modular-inverses.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-451-modular-inverses.md index 931ce309825..6d5275b3f86 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-451-modular-inverses.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-451-modular-inverses.md @@ -1,6 +1,6 @@ --- id: 5900f5311000cf542c510042 -title: 'Problem 451: Modular inverses' +title: 'Завдання 451: обернені за модулем числа' challengeType: 1 forumTopicId: 302124 dashedName: problem-451-modular-inverses @@ -10,20 +10,20 @@ dashedName: problem-451-modular-inverses Розглянемо число 15. -There are eight positive numbers less than 15 which are coprime to 15: 1, 2, 4, 7, 8, 11, 13, 14. +Існує вісім додатних чисел, менших за 15, які є взаємно простими з 15: 1, 2, 4, 7, 8, 11, 13, 14. -The modular inverses of these numbers modulo 15 are: 1, 8, 4, 13, 2, 11, 7, 14 because +Оберненими за модулем 15 цих чисел є 1, 8, 4, 13, 2, 11, 7, 14, оскільки $$\begin{align} & 1 \times 1\bmod 15 = 1 \\\\ & 2 \times 8 = 16\bmod 15 = 1 \\\\ & 4 \times 4 = 16\bmod 15 = 1 \\\\ & 7 \times 13 = 91\bmod 15 = 1 \\\\ & 11 \times 11 = 121\bmod 15 = 1 \\\\ & 14 \times 14 = 196\bmod 15 = 1 \end{align}$$ -Let $I(n)$ be the largest positive number $m$ smaller than $n - 1$ such that the modular inverse of $m$ modulo $n$ equals $m$ itself. +Нехай $I(n)$ буде найбільшим додатним числом $m$, меншим за $n - 1$, за якого обернене за модулем $m$ число $n$ дорівнюватиме $m$. -So $I(15) = 11$. +Отже, $I(15) = 11$. -Also $I(100) = 51$ and $I(7) = 1$. +А також $I(100) = 51$ та $I(7) = 1$. Знайдіть $\sum I(n)$ за умови $3 ≤ n ≤ 2 \times {10}^7$ diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-469-empty-chairs.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-469-empty-chairs.md index c0cf7d21f5a..1f3aed7686e 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-469-empty-chairs.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-469-empty-chairs.md @@ -14,7 +14,7 @@ dashedName: problem-469-empty-chairs Щоб мати достатньо місця для ліктів, лицарі залишають принаймні один порожній стілець між собою. -Коли немає місця, щоб сісти, визначається частина порожніх стільців $C$. Також визначимо $E(N)$ як очікуване значення $C$. +Коли немає місця, щоб сісти, визначимо частину порожніх стільців $C$. Також визначимо $E(N)$ як очікуване значення $C$. Можна довести, що $E(4) = \frac{1}{2}$ та $E(6) = \frac{5}{9}$. diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-470-super-ramvok.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-470-super-ramvok.md index 1d6c9eaaf8d..1f0b06bcab1 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-470-super-ramvok.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-470-super-ramvok.md @@ -1,6 +1,6 @@ --- id: 5900f5431000cf542c510055 -title: 'Problem 470: Super Ramvok' +title: 'Завдання 470: супер рамвок' challengeType: 1 forumTopicId: 302146 dashedName: problem-470-super-ramvok @@ -8,19 +8,19 @@ dashedName: problem-470-super-ramvok # --description-- -Consider a single game of Ramvok: +Розглянемо звичайну гру в рамвок: -Let $t$ represent the maximum number of turns the game lasts. If $t = 0$, then the game ends immediately. Otherwise, on each turn $i$, the player rolls a die. After rolling, if $i < t$ the player can either stop the game and receive a prize equal to the value of the current roll, or discard the roll and try again next turn. If $i = t$, then the roll cannot be discarded and the prize must be accepted. Before the game begins, $t$ is chosen by the player, who must then pay an up-front cost $ct$ for some constant $c$. For $c = 0$, $t$ can be chosen to be infinite (with an up-front cost of 0). Let $R(d, c)$ be the expected profit (i.e. net gain) that the player receives from a single game of optimally-played Ramvok, given a fair $d$-sided die and cost constant $c$. Наприклад, $R(4, 0.2) = 2.65$. Assume that the player has sufficient funds for paying any/all up-front costs. +Нехай $t$ означає максимальну кількість ходів за цілу гру. Якщо $t = 0$, гра одразу закінчується. В іншому випадку, гравець викидає кубик за кожен хід $i$. Після цього, якщо $i < t$, то гравець може або зупинити гру та отримати виграш, рівний отриманому числу, або скасувати свій хід та спробувати ще раз. Якщо $i = t$, то хід не можна скасувати і потрібно прийняти свій виграш. Гравець обирає $t$ перед початком гри та має оплатити аванс в розмірі $ct$ для сталого значення $c$. Якщо $c = 0$, то $t$ може бути нескінченним (аванс дорівнює 0). Нехай $R(d, c)$ буде очікуваним прибутком (тобто чистим виграшем), який гравець отримає за одну оптимально зіграну гру «Рамвок», використовуючи чесний $d$-бічний кубик та сталу ціну $c$. Наприклад, $R(4, 0.2) = 2.65$. Припустимо, що гравець має достатньо коштів для оплати будь-якого авансу. -Now consider a game of Super Ramvok: +Тепер розглянемо гру в супер рамвок: -In Super Ramvok, the game of Ramvok is played repeatedly, but with a slight modification. After each game, the die is altered. The alteration process is as follows: The die is rolled once, and if the resulting face has its pips visible, then that face is altered to be blank instead. If the face is already blank, then it is changed back to its original value. After the alteration is made, another game of Ramvok can begin (and during such a game, at each turn, the die is rolled until a face with a value on it appears). The player knows which faces are blank and which are not at all times. The game of Super Ramvok ends once all faces of the die are blank. +В грі «Супер рамвок», рамвок грається декілька разів, але з невеликим доповненням. Після кожної гри гральний кубик змінюється. Зміна здійснюється таким чином: гральний кубик викидається один раз, і якщо на отриманій грані можна побачити значення, це значення потрібно стерти. Якщо грань вже порожня, то потрібно повернути початкове значення. Після зміни починається інша гра в рамвок, під час якої гральний кубик викидається, поки не з’явиться грань зі значенням. Гравець завжди знає, які грані порожні та повні. Гра закінчується, коли всі грані кубика будуть порожніми. -Let $S(d, c)$ be the expected profit that the player receives from an optimally-played game of Super Ramvok, given a fair $d$-sided die to start (with all sides visible), and cost constant $c$. Наприклад, $S(6, 1) = 208.3$. +Нехай $S(d, c)$ буде очікуваним прибутком, який гравець отримає за одну оптимально зіграну гру «Рамвок», використовуючи чесний $d$-бічний кубик (з видимими значеннями) та сталою ціною $c$. Наприклад, $S(6, 1) = 208.3$. -Let $F(n) = \sum_{4 ≤ d ≤ n} \sum_{0 ≤ c ≤ n} S(d, c)$. +Нехай $F(n) = \sum_{4 ≤ d ≤ n} \sum_{0 ≤ c ≤ n} S(d, c)$. -Calculate $F(20)$, rounded to the nearest integer. +Знайдіть $F(20)$, заокруглене до найближчого цілого числа. # --hints-- diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-471-triangle-inscribed-in-ellipse.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-471-triangle-inscribed-in-ellipse.md index a437ecf05a2..60e0c8b886a 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-471-triangle-inscribed-in-ellipse.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-471-triangle-inscribed-in-ellipse.md @@ -1,6 +1,6 @@ --- id: 5900f5431000cf542c510056 -title: 'Problem 471: Triangle inscribed in ellipse' +title: 'Завдання 471: трикутник, вписаний в еліпс' challengeType: 1 forumTopicId: 302148 dashedName: problem-471-triangle-inscribed-in-ellipse @@ -8,23 +8,23 @@ dashedName: problem-471-triangle-inscribed-in-ellipse # --description-- -The triangle $ΔABC$ is inscribed in an ellipse with equation $\frac{x^2}{a^2} + \frac{y^2}{b^2} = 1$, $0 < 2b < a$, $a$ and $b$ integers. +Трикутник $ΔABC$ вписаний в еліпс, заданий рівнянням $\frac{x^2}{a^2} + \frac{y^2}{b^2} = 1$, де $0 < 2b < a$, а $a$ та $b$ є цілими числами. -Let $r(a, b)$ be the radius of the incircle of $ΔABC$ when the incircle has center $(2b, 0)$ and $A$ has coordinates $\left(\frac{a}{2}, \frac{\sqrt{3}}{2}b\right)$. +Нехай $r(a, b)$ буде радіусом вписаного в $ΔABC$ кола, а його центр лежить в центрі $(2b, 0)$ та $A$ має координати $\left(\frac{a}{2}, \frac{\sqrt{3}}{2}b\right)$. Наприклад, $r(3, 1) = \frac{1}{2}, r(6, 2) = 1, r(12, 3) = 2$. -triangle ΔABC inscribed in an ellipse, radius of the incircle of ΔABC r(6, 2) = 1 +трикутник ΔABC вписаний в еліпс, радіус кола, вписаного в ΔABC, r(6, 2) = 1 -triangle ΔABC inscribed in an ellipse, radius of the incircle of ΔABC r(12, 3) = 2 +трикутник ΔABC вписаний в еліпс, радіус кола, вписаного в ΔABC, r(12, 3) = 2 Нехай $G(n) = \sum_{a = 3}^n \sum_{b = 1}^{\left\lfloor\frac{a - 1}{2} \right\rfloor} r(a, b)$ -You are given $G(10) = 20.59722222$, $G(100) = 19223.60980$ (rounded to 10 significant digits). +Дано, що $G(10) = 20.59722222$, $G(100) = 19223.60980$ (заокруглено до 10 цифр). -Знайдіть $G({10}^{11})$. Give your answer as a string in scientific notation rounded to 10 significant digits. Use a lowercase `e` to separate mantissa and exponent. +Знайдіть $G({10}^{11})$. Надайте відповідь у вигляді рядка в експоненційному записі, заокругливши до десяти знаків після коми. Використайте `e` в нижньому регістрі, щоб розділити мантису та показник степеня. -For $G(10)$ the answer would have been `2.059722222e1` +За умови $G(10)$ відповіддю буде `2.059722222e1` # --hints-- diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-472-comfortable-distance-ii.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-472-comfortable-distance-ii.md index a119591752d..b5bb95f5080 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-472-comfortable-distance-ii.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-472-comfortable-distance-ii.md @@ -1,6 +1,6 @@ --- id: 5900f5451000cf542c510057 -title: 'Problem 472: Comfortable Distance II' +title: 'Завдання 472: комфортна відстань II' challengeType: 1 forumTopicId: 302149 dashedName: problem-472-comfortable-distance-ii @@ -8,23 +8,23 @@ dashedName: problem-472-comfortable-distance-ii # --description-- -There are $N$ seats in a row. $N$ people come one after another to fill the seats according to the following rules: +Ряд складається з $N$ місць. Приходить $N$ людей та вони починають займати місця згідно з такими правилами: -1. No person sits beside another. -1. The first person chooses any seat. -1. Each subsequent person chooses the seat furthest from anyone else already seated, as long as it does not violate rule 1. If there is more than one choice satisfying this condition, then the person chooses the leftmost choice. +1. Жодна людина не сидить поруч з іншою. +1. Перша людина обирає будь-яке місце. +1. Кожна наступна людина вибирає місце, найбільш віддалене від того, хто вже сидить, до тих пір, поки не порушує правило №1. Якщо існує більше одного варіанту, що задовільняє цю умову, то людина обирає місце зліва. -Note that due to rule 1, some seats will surely be left unoccupied, and the maximum number of people that can be seated is less than $N$ (for $N > 1$). +Зверніть увагу, що через правило №1, деякі місця, безумовно, будуть вільними, і максимальна кількість осіб, які можуть бути розміщені, менша за $N$ (за умови $N > 1$). -Here are the possible seating arrangements for $N = 15$: +Ось можливі розсадження за умови $N = 15$: -seating arrangements for N = 15 +розсадження за умови N = 15 -We see that if the first person chooses correctly, the 15 seats can seat up to 7 people. We can also see that the first person has 9 choices to maximize the number of people that may be seated. +Як бачимо, якщо перша людина обирає правильне місце, то на 15 місцях можуть сісти не більше 7 людей. Ми також можемо бачити, що перша людина має 9 варіантів, щоб максимізувати кількість осіб, яких можна розмістити. -Let $f(N)$ be the number of choices the first person has to maximize the number of occupants for $N$ seats in a row. Thus, $f(1) = 1$, $f(15) = 9$, $f(20) = 6$, and $f(500) = 16$. +Нехай $f(N)$ буде кількістю можливих місць першої людини, щоб максимізувати кількість сидячих людей в ряді з $N$ місць. Таким чином, $f(1) = 1$, $f(15) = 9$, $f(20) = 6$ та $f(500) = 16$. -Also, $\sum f(N) = 83$ for $1 ≤ N ≤ 20$ and $\sum f(N) = 13\\,343$ for $1 ≤ N ≤ 500$. +Також $\sum f(N) = 83$ за умови $1 ≤ N ≤ 20$ та $\sum f(N) = 13\\,343$ за умови $1 ≤ N ≤ 500$. Знайдіть $\sum f(N)$ за умови $1 ≤ N ≤ {10}^{12}$. У відповіді запишіть 8 останніх цифр. diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-473-phigital-number-base.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-473-phigital-number-base.md index ca525322258..771c61c260b 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-473-phigital-number-base.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-473-phigital-number-base.md @@ -1,6 +1,6 @@ --- id: 5900f5461000cf542c510058 -title: 'Problem 473: Phigital number base' +title: 'Завдання 473: φ-цифрова система' challengeType: 1 forumTopicId: 302150 dashedName: problem-473-phigital-number-base @@ -8,25 +8,25 @@ dashedName: problem-473-phigital-number-base # --description-- -Let $\varphi$ be the golden ratio: $\varphi = \frac{1+\sqrt{5}}{2}.$ +Нехай $\varphi$ буде золотим січенням: $\varphi = \frac{1+\sqrt{5}}{2}.$ -Remarkably it is possible to write every positive integer as a sum of powers of $\varphi$ even if we require that every power of $\varphi$ is used at most once in this sum. +Цікаво, що будь-яке натуральне число можна записати як суму степенів $\varphi$, навіть якщо потрібно, щоб кожен степінь $\varphi$ використовувався не більше одного разу. -Even then this representation is not unique. +Навіть у цьому випадку таке представлення не буде унікальним. -We can make it unique by requiring that no powers with consecutive exponents are used and that the representation is finite. +Ми можемо зробити його унікальним, вимагаючи, щоб не використовувалися степені з послідовними показниками, і щоб представлення було скінченним. Наприклад: $2 = \varphi + \varphi^{-2}$ та $3 = \varphi^{2} + \varphi^{-2}$ -To represent this sum of powers of $\varphi$ we use a string of 0's and 1's with a point to indicate where the negative exponents start. We call this the representation in the phigital numberbase. +Щоб представити цю суму степенів $\varphi$, будемо використовувати рядок з 0 та 1 із крапкою, що покаже початок від’ємних показників. Називатимемо це представлення φ-цифровою системою. -So $1 = 1_{\varphi}$, $2 = 10.01_{\varphi}$, $3 = 100.01_{\varphi}$ and $14 = 100100.001001_{\varphi}$. The strings representing 1, 2 and 14 in the phigital number base are palindromic, while the string representing 3 is not (the phigital point is not the middle character). +Таким чином, $1 = 1_{\varphi}$, $2 = 10.01_{\varphi}$, $3 = 100.01_{\varphi}$ та $14 = 100100.001001_{\varphi}$. Рядки, які представляють 1, 2 та 14 у φ-цифровій системі є паліндромними, а рядок, який представляє 3 — ні (φ-цифрова крапка не знаходиться посередині). -The sum of the positive integers not exceeding 1000 whose phigital representation is palindromic is 4345. +Сума натуральних чисел, менших за 1000 та чий φ-цифровий запис паліндромний, дорівнює 4345. -Find the sum of the positive integers not exceeding $10^{10}$ whose phigital representation is palindromic. +Знайдіть суму натуральних чисел, менших за $10^{10}$, чий φ-цифровий запис є паліндромним. # --hints-- diff --git a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-478-mixtures.md b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-478-mixtures.md index 15e123b6a54..1d3a7e9e3d9 100644 --- a/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-478-mixtures.md +++ b/curriculum/challenges/ukrainian/18-project-euler/project-euler-problems-401-to-480/problem-478-mixtures.md @@ -16,7 +16,7 @@ dashedName: problem-478-mixtures Однак з трьома однаковими сумішами неможливо утворити співвідношення (3 : 2 : 1), оскільки кількість $B$ завжди менша за кількість $C$. -Нехай $n$ буде натуральним числом. Припустимо, що для кожної трійки цілих чисел $(a, b, c)$ за умови $0 ≤ a, b, c ≤ n$ та $gcd(a, b, c) = 1$, ми маємо суміш зі співвідношенням $(a : b : c)$. Нехай $M(n)$ буде множиною таких сумішей. +Нехай $n$ буде натуральним числом. Припустимо, що для кожної трійки цілих чисел $(a, b, c)$ за умови $0 ≤ a, b, c ≤ n$ та $нсд(a, b, c) = 1$, ми маємо суміш зі співвідношенням $(a : b : c)$. Нехай $M(n)$ буде множиною таких сумішей. Наприклад, $M(2)$ містить 19 сумішей з такими співвідношеннями: