From c5d3600c2381bee6f30d211095d836e3de04fc3d Mon Sep 17 00:00:00 2001 From: Ivan Date: Sun, 12 Apr 2026 16:03:16 +0300 Subject: [PATCH 1/2] add task solution --- README.md | 14 ++-- src/scripts/main.js | 156 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 162 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index cca9c9f4a..21d674b49 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ 1. Replace `` with your GitHub username in the link - - [DEMO LINK](https://.github.io/js_employees_table_DOM/) + - [DEMO LINK](https://JordanKoby.github.io/js_employees_table_DOM/) 2. Follow [this instructions](https://mate-academy.github.io/layout_task-guideline/) - Run `npm run test` command to test your code; - Run `npm run test:only -- -n` to run fast test ignoring linter; @@ -44,11 +44,11 @@ Start table: ``` - Add qa attributes for each input field: ``` - data-qa="name" - data-qa="position" - data-qa="office" - data-qa="age" - data-qa="salary" + data-qa="name" + data-qa="position" + data-qa="office" + data-qa="age" + data-qa="salary" ``` - Select should have 6 options: `Tokyo`, `Singapore`, `London`, `New York`, `Edinburgh`, `San Francisco`. - Use texts for labels and buttons from the screenshot below. @@ -64,7 +64,7 @@ Start table: - Notification titles and descriptions are up to you. - Add qa attribute for notification: `data-qa="notification"` and class `error`/`success` depending on the result. -##### Implement editing of table cells by double-clicking on them (optional). +##### Implement editing of table cells by double-clicking on them (optional). - Double click on the cell of the table, which should remove the text, and append input with `cell-input` class. - The input value should be replaced by the input text. - Only one cell can be edited at a time. diff --git a/src/scripts/main.js b/src/scripts/main.js index a765fdb1d..73ec4be1a 100644 --- a/src/scripts/main.js +++ b/src/scripts/main.js @@ -1,3 +1,157 @@ 'use strict'; -// write code here +document.addEventListener('DOMContentLoaded', () => { + const table = document.querySelector('table'); + const tbody = table.querySelector('tbody'); + const headers = table.querySelectorAll('thead th'); + const notification = document.createElement('div'); + + notification.setAttribute('data-qa', 'notification'); + document.body.appendChild(notification); + + let sortState = {}; + + headers.forEach((th, index) => { + th.addEventListener('click', () => { + const rows = Array.from(tbody.querySelectorAll('tr')); + const asc = sortState[index] !== 'asc'; + + rows.sort((a, b) => { + let valA = a.children[index].innerText.replace(/[$,]/g, ''); + let valB = b.children[index].innerText.replace(/[$,]/g, ''); + + if (!isNaN(valA) && !isNaN(valB)) { + valA = Number(valA); + valB = Number(valB); + } + + return asc ? (valA > valB ? 1 : -1) : valA < valB ? 1 : -1; + }); + tbody.innerHTML = ''; + rows.forEach((r) => tbody.appendChild(r)); + sortState = { [index]: asc ? 'asc' : 'desc' }; + }); + }); + + tbody.addEventListener('click', (e) => { + const row = e.target.closest('tr'); + + if (row) { + tbody.querySelectorAll('tr').forEach((r) => r.classList.remove('active')); + row.classList.add('active'); + } + }); + + const form = document.createElement('form'); + + form.className = 'new-employee-form'; + + form.innerHTML = ` + + + + + + + + `; + document.body.appendChild(form); + + const showNotification = (msg, type) => { + notification.textContent = msg; + notification.className = type; + }; + + form.addEventListener('submit', (e) => { + e.preventDefault(); + + const data = Object.fromEntries(new FormData(form)); + const employeeName = data.name.trim(); + const position = data.position.trim(); + const office = data.office; + const age = Number(data.age); + const salary = Number(data.salary); + + if (employeeName.length < 4) { + showNotification('Name must be at least 4 characters long', 'error'); + + return; + } + + if (!position.trim()) { + showNotification('Position is required', 'error'); + + return; + } + + if (age < 18 || age > 90) { + showNotification('Age must be between 18 and 90', 'error'); + + return; + } + + if (!office.trim()) { + showNotification('Office is required', 'error'); + + return; + } + + if (!salary) { + showNotification('Salary is required', 'error'); + + return; + } + + const tr = document.createElement('tr'); + + tr.innerHTML = ` + ${employeeName} + ${position} + ${office} + ${age} + $${salary.toLocaleString('en-US')} + `; + tbody.appendChild(tr); + + showNotification('Employee added successfully', 'success'); + form.reset(); + }); + + tbody.addEventListener('dblclick', (e) => { + const cell = e.target; + + if (cell.tagName === 'TD' && !cell.querySelector('input')) { + const oldValue = cell.innerText; + const input = document.createElement('input'); + + input.value = oldValue; + input.className = 'cell-input'; + cell.innerHTML = ''; + cell.appendChild(input); + input.focus(); + + const save = () => { + cell.innerText = input.value.trim() || oldValue; + }; + + input.addEventListener('blur', save); + + input.addEventListener('keydown', (ev) => { + if (ev.key === 'Enter') { + save(); + } + }); + } + }); +}); From b32f9f9da3004705cd629928bfb05f326f6c52bb Mon Sep 17 00:00:00 2001 From: Ivan Date: Sun, 12 Apr 2026 16:55:38 +0300 Subject: [PATCH 2/2] amendments have been made --- src/scripts/main.js | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/scripts/main.js b/src/scripts/main.js index 73ec4be1a..a22a6da8c 100644 --- a/src/scripts/main.js +++ b/src/scripts/main.js @@ -20,12 +20,17 @@ document.addEventListener('DOMContentLoaded', () => { let valA = a.children[index].innerText.replace(/[$,]/g, ''); let valB = b.children[index].innerText.replace(/[$,]/g, ''); - if (!isNaN(valA) && !isNaN(valB)) { + const isNumA = valA.trim() !== '' && !Number.isNaN(Number(valA)); + const isNumB = valB.trim() !== '' && !Number.isNaN(Number(valB)); + + if (isNumA && isNumB) { valA = Number(valA); valB = Number(valB); + + return asc ? valA - valB : valB - valA; } - return asc ? (valA > valB ? 1 : -1) : valA < valB ? 1 : -1; + return asc ? valA.localeCompare(valB) : valB.localeCompare(valA); }); tbody.innerHTML = ''; rows.forEach((r) => tbody.appendChild(r)); @@ -107,7 +112,7 @@ document.addEventListener('DOMContentLoaded', () => { return; } - if (!salary) { + if (data.salary.trim() === '' || !Number.isFinite(salary)) { showNotification('Salary is required', 'error'); return; @@ -132,17 +137,27 @@ document.addEventListener('DOMContentLoaded', () => { const cell = e.target; if (cell.tagName === 'TD' && !cell.querySelector('input')) { - const oldValue = cell.innerText; + const existingInput = document.querySelector('.cell-input'); + + if (existingInput) { + const parentCell = existingInput.parentElement; + const oldValue = existingInput.value || parentCell.dataset.oldValue; + + parentCell.innerText = existingInput.value.trim() || oldValue; + } + + const currentValue = cell.innerText; const input = document.createElement('input'); - input.value = oldValue; + input.value = currentValue; input.className = 'cell-input'; + cell.dataset.oldValue = currentValue; cell.innerHTML = ''; cell.appendChild(input); input.focus(); const save = () => { - cell.innerText = input.value.trim() || oldValue; + cell.innerText = input.value.trim() || currentValue; }; input.addEventListener('blur', save);