Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
1. Replace `<your_account>` with your GitHub username in the link
- [DEMO LINK](https://<your_account>.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;
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down
171 changes: 170 additions & 1 deletion src/scripts/main.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,172 @@
'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, '');

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);
Comment on lines +20 to +28
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Using !isNaN(valA) on the raw string after removing $/, can treat an empty string ('') as numeric (Number('') === 0). That causes empty cells to be treated as 0 during numeric sort. Consider using Number.isNaN(Number(valA)) or checking valA.trim() === '' before converting to Number.


return asc ? valA - valB : valB - valA;
}

return asc ? valA.localeCompare(valB) : valB.localeCompare(valA);
});
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 = `
<label>Name: <input name='name'
type='text' data-qa='name'></label>
<label>Position: <input name='position' type='text' data-qa='position'></label>
<label>Office:
<select name='office' data-qa='office'>
<option>Tokyo</option>
<option>Singapore</option>
<option>London</option>
<option>New York</option>
<option>Edinburgh</option>
<option>San Francisco</option>

</select>
</label>
<label>Age: <input name='age' type='number' data-qa='age'></label>

<label>Salary: <input name='salary' type='number' data-qa='salary'></label>
<button type='submit'>Save to table</button>
`;
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 (data.salary.trim() === '' || !Number.isFinite(salary)) {
showNotification('Salary is required', 'error');

return;
}

const tr = document.createElement('tr');

tr.innerHTML = `
<td>${employeeName}</td>
<td>${position}</td>
<td>${office}</td>
<td>${age}</td>
<td>$${salary.toLocaleString('en-US')}</td>
`;
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 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 = currentValue;
input.className = 'cell-input';
cell.dataset.oldValue = currentValue;
cell.innerHTML = '';
cell.appendChild(input);
Comment on lines +139 to +156
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Optional requirement: only one cell should be editable at a time. Current code only checks the target cell for an existing input but doesn't close any other open .cell-input. Consider checking document.querySelector('.cell-input') and saving/closing it before inserting a new input.

input.focus();

const save = () => {
cell.innerText = input.value.trim() || currentValue;
};

input.addEventListener('blur', save);

input.addEventListener('keydown', (ev) => {
if (ev.key === 'Enter') {
save();
}
});
}
});
});
Loading