diff --git a/package.json b/package.json
index 2b097dc1..1cc7b2bd 100755
--- a/package.json
+++ b/package.json
@@ -19,7 +19,7 @@
"@cypress/webpack-dev-server": "^1.8.4",
"@mate-academy/cypress-tools": "^1.0.4",
"@mate-academy/eslint-config-react-typescript": "^1.0.11",
- "@mate-academy/scripts": "^1.2.3",
+ "@mate-academy/scripts": "^1.2.8",
"@mate-academy/students-ts-config": "*",
"@mate-academy/stylelint-config": "*",
"@types/node": "^17.0.45",
diff --git a/src/App.tsx b/src/App.tsx
index f6361547..33474132 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,39 +1,28 @@
-import React from 'react';
+import { FC, useEffect, useState } from 'react';
import '@fortawesome/fontawesome-free/css/all.css';
import 'bulma/css/bulma.css';
import './App.scss';
-import peopleFromServer from './people.json';
+import { PeopleTable } from './components/PeopleTable/PeopleTable';
+import { Loader } from './components/Loader';
-export class App extends React.Component {
- state = {};
+export const App: FC = () => {
+ const [isLoaded, setIsLoaded] = useState(false);
- render() {
- return (
-
-
People table
+ useEffect(() => {
+ setTimeout(() => {
+ setIsLoaded(true);
+ }, 1000);
+ }, []);
-
-
-
- | name |
- sex |
- born |
-
-
+ return (
+
+
People table
-
- {peopleFromServer.map(person => (
-
- | {person.name} |
- {person.sex} |
- {person.born} |
-
- ))}
-
-
-
- );
- }
-}
+ {!isLoaded && }
+
+ {isLoaded && }
+
+ );
+};
diff --git a/src/components/PeopleTable/PeopleTable.scss b/src/components/PeopleTable/PeopleTable.scss
new file mode 100644
index 00000000..6cb93e78
--- /dev/null
+++ b/src/components/PeopleTable/PeopleTable.scss
@@ -0,0 +1,3 @@
+.isSelected {
+ background-color: yellowgreen;
+}
diff --git a/src/components/PeopleTable/PeopleTable.tsx b/src/components/PeopleTable/PeopleTable.tsx
new file mode 100644
index 00000000..ba0a9e49
--- /dev/null
+++ b/src/components/PeopleTable/PeopleTable.tsx
@@ -0,0 +1,231 @@
+import { ChangeEvent, useState } from 'react';
+import classNames from 'classnames';
+
+import peopleFromServer from '../../people.json';
+import { Person } from '../../types/Person';
+import './PeopleTable.scss';
+
+export const PeopleTable = () => {
+ const [people, setPeople] = useState(peopleFromServer);
+ const [selectedPeople, setSelectedPeople] = useState([]);
+ const [query, setQuery] = useState('');
+ const [sex, setSex] = useState('');
+
+ // region Methods
+ const handlePersonSelection = (personToAdd: Person) => {
+ setSelectedPeople((prevSelectedPeople) => ([
+ ...prevSelectedPeople,
+ personToAdd,
+ ]));
+ };
+
+ const handlePersonUnselection = (personToRemove: Person) => {
+ setSelectedPeople((prevSelectedPeople) => prevSelectedPeople
+ .filter(
+ person => person.slug !== personToRemove.slug,
+ ));
+ };
+
+ const handleMoveDown = (personToMoveDown: Person) => {
+ const peopleCopy = [...people];
+
+ const personIndex = peopleCopy.findIndex(
+ person => person.slug === personToMoveDown.slug,
+ );
+
+ if (personIndex === peopleCopy.length - 1) {
+ return;
+ }
+
+ setPeople((prevPeople) => ([
+ ...prevPeople.slice(0, personIndex),
+ prevPeople[personIndex + 1],
+ prevPeople[personIndex],
+ ...prevPeople.slice(personIndex + 2),
+ ]));
+ };
+
+ const handleMoveUp = (personToMoveDown: Person) => {
+ const peopleCopy = [...people];
+
+ const personIndex = peopleCopy.findIndex(
+ person => person.slug === personToMoveDown.slug,
+ );
+
+ if (personIndex === 0) {
+ return;
+ }
+
+ const swap = peopleCopy[personIndex];
+
+ peopleCopy[personIndex] = peopleCopy[personIndex - 1];
+ peopleCopy[personIndex - 1] = swap;
+
+ setPeople(peopleCopy);
+ };
+
+ const isSelected = (personToCheck: Person) => {
+ return selectedPeople.some(
+ person => person.slug === personToCheck.slug,
+ );
+ };
+
+ const clearSelectedPeople = () => {
+ setSelectedPeople([]);
+ };
+ // endregion
+
+ const handleInputChange = (event: ChangeEvent) => {
+ setQuery(event.target.value);
+ };
+
+ let visiblePeople = [...people];
+
+ if (sex !== '') {
+ visiblePeople = visiblePeople.filter(person => person.sex === sex);
+ }
+
+ if (query !== '') {
+ const normalizedQuery = query.toLocaleLowerCase();
+
+ visiblePeople = visiblePeople.filter(person => {
+ person.motherName?.toLocaleLowerCase(); // === null
+
+ if (person.motherName) {
+ person.motherName.toLocaleLowerCase();
+ } else {
+ return person.motherName;
+ }
+
+
+ const stringToCheck = `
+ ${person.name}
+ ${person.motherName || ''}
+ ${person?.fatherName || ''}
+ `;
+
+ return stringToCheck.toLocaleLowerCase().includes(normalizedQuery);
+ });
+ }
+
+ return (
+
+
+
+
+ {selectedPeople.map(person => person.name).join(', ')}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | - |
+ name |
+ Mother name |
+ Father name |
+ sex |
+ born |
+ reorder |
+
+
+
+
+ {visiblePeople.map(person => (
+
+ |
+ {isSelected(person)
+ ? (
+
+ )
+ : (
+
+ )}
+ |
+
+ {person.name}
+ |
+
+ {person.motherName || '-'} |
+ {person.fatherName || '-'} |
+ {person.sex} |
+ {person.born} |
+
+
+
+
+ |
+
+ ))}
+
+
+ );
+};