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.scss b/src/App.scss
index 93f0af3a..4d26146b 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -2,3 +2,11 @@
iframe {
display: none;
}
+
+button {
+ cursor: pointer;
+}
+
+tr {
+ transition: 0.3s background-color;
+}
diff --git a/src/App.tsx b/src/App.tsx
index f6361547..e9cb3635 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,39 +1,28 @@
-import React from 'react';
+import React, { 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 { Loader } from './components/Loader';
+import { PeopleTable } from './components/PeopleTable';
-export class App extends React.Component {
- state = {};
+export const App: React.FC = () => {
+ const [isLoaded, setLoading] = useState(false);
- render() {
- return (
-
-
People table
+ useEffect(() => {
+ setTimeout(() => {
+ setLoading(true);
+ }, 1000);
+ }, []);
-
-
-
- | name |
- sex |
- born |
-
-
+ return (
+
+
People table
-
- {peopleFromServer.map(person => (
-
- | {person.name} |
- {person.sex} |
- {person.born} |
-
- ))}
-
-
-
- );
- }
-}
+ {isLoaded
+ ?
+ : }
+
+ );
+};
diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx
new file mode 100644
index 00000000..33fd3cf8
--- /dev/null
+++ b/src/components/Button/Button.tsx
@@ -0,0 +1,15 @@
+import React from 'react';
+
+type Props = React.ButtonHTMLAttributes & {
+ children: React.ReactNode;
+};
+
+export const Button: React.FC = ({ children, className, ...props }) => (
+
+);
diff --git a/src/components/Button/index.ts b/src/components/Button/index.ts
new file mode 100644
index 00000000..8b166a86
--- /dev/null
+++ b/src/components/Button/index.ts
@@ -0,0 +1 @@
+export * from './Button';
diff --git a/src/components/PeopleTable/PeopleTable.tsx b/src/components/PeopleTable/PeopleTable.tsx
new file mode 100644
index 00000000..c5dfbbb9
--- /dev/null
+++ b/src/components/PeopleTable/PeopleTable.tsx
@@ -0,0 +1,203 @@
+import React, { useState } from 'react';
+import classNames from 'classnames';
+import { Person } from '../../types/Person';
+import { Button } from '../Button';
+import peopleFromServer from '../../people.json';
+
+enum SortField {
+ Name = 'name',
+ Slug = 'slug',
+ Born = 'born',
+ Died = 'died',
+ Null = 'null',
+}
+
+function sortByField(items: Person[], field: SortField) {
+ return [...items].sort((personA, personB) => {
+ switch (field) {
+ case SortField.Born:
+ case SortField.Died:
+ return personA[field] - personB[field];
+
+ case SortField.Name:
+ case SortField.Slug:
+ return personA[field].localeCompare(personB[field]);
+
+ default:
+ return 0;
+ }
+ });
+}
+
+export const PeopleTable: React.FC<{}> = () => {
+ const [people, setPeople] = useState(peopleFromServer);
+ const [selectedPeople, setSelectedPeople] = useState([]);
+ const [sortField, setSortField] = useState(SortField.Null);
+
+ const moveDown = (person: Person) => {
+ setPeople(currentPeople => {
+ const peopleCopy = [...currentPeople];
+
+ const personIndex = peopleCopy.findIndex(
+ personA => personA.slug === person.slug,
+ );
+
+ if (personIndex === peopleCopy.length - 1) {
+ return currentPeople;
+ }
+
+ peopleCopy[personIndex + 1] = person;
+ peopleCopy[personIndex] = currentPeople[personIndex + 1];
+
+ return peopleCopy;
+ });
+ };
+
+ const moveUp = (person: Person) => {
+ setPeople(currentPeople => {
+ const peopleCopy = [...currentPeople];
+
+ const personIndex = peopleCopy.findIndex(
+ personA => personA.slug === person.slug,
+ );
+
+ if (personIndex === 0) {
+ return currentPeople;
+ }
+
+ peopleCopy[personIndex - 1] = person;
+ peopleCopy[personIndex] = currentPeople[personIndex - 1];
+
+ return peopleCopy;
+ });
+ };
+
+ const selectPerson = (person: Person) => {
+ setSelectedPeople(currentPeople => [...currentPeople, person]);
+ };
+
+ const unselectPerson = (person: Person) => {
+ setSelectedPeople((currentPeople) => (
+ currentPeople.filter(
+ selectedPerson => selectedPerson.slug !== person.slug,
+ )
+ ));
+ };
+
+ const clearSelectedPeople = () => {
+ setSelectedPeople([]);
+ };
+
+ const isPersonSelected = (person: Person) => {
+ return selectedPeople.some(
+ selectedPerson => selectedPerson.slug === person.slug,
+ );
+ };
+
+ const visiblePeople = sortByField(people, sortField);
+
+ if (people.length === 0) {
+ return (
+ No people data yet
+ );
+ }
+
+ return (
+
+
+ {selectedPeople.length > 0 && (
+
+ )}
+
+ {selectedPeople.map(person => person.name).join(', ') || '-'}
+
+
+
+
+ | select |
+
+ name
+
+ setSortField(SortField.Name)}
+ >
+
+
+
+
+ |
+ sex |
+ born |
+
+
+
+
+ {visiblePeople.map(person => (
+
+ |
+ {isPersonSelected(person)
+ ? (
+
+ )
+ : (
+
+ )}
+ |
+
+ {person.name}
+ |
+ {person.sex} |
+ {person.born} |
+
+
+ |
+
+
+
+ |
+
+ ))}
+
+
+ );
+};
diff --git a/src/components/PeopleTable/index.ts b/src/components/PeopleTable/index.ts
new file mode 100644
index 00000000..45984e7c
--- /dev/null
+++ b/src/components/PeopleTable/index.ts
@@ -0,0 +1 @@
+export * from './PeopleTable';