From 2332e2454bfc29c5922f17e484dfe38d5fe55f9b Mon Sep 17 00:00:00 2001
From: Denys Cheporniuk
Date: Sun, 5 Feb 2023 20:03:39 +0200
Subject: [PATCH 1/2] initial commit
---
package.json | 2 +-
src/App.tsx | 258 +++++++++++++++++++++++++++----
src/components/Button/Button.tsx | 15 ++
src/components/Button/index.ts | 1 +
4 files changed, 244 insertions(+), 32 deletions(-)
create mode 100644 src/components/Button/Button.tsx
create mode 100644 src/components/Button/index.ts
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..2f16615a 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,39 +1,235 @@
-import React from 'react';
+import { useState } from 'react';
+import classNames from 'classnames';
import '@fortawesome/fontawesome-free/css/all.css';
import 'bulma/css/bulma.css';
-import './App.scss';
import peopleFromServer from './people.json';
+import { Person } from './types/Person';
+import { Button } from './components/Button';
-export class App extends React.Component {
- state = {};
-
- render() {
- return (
-
-
People table
-
-
-
-
- | name |
- sex |
- born |
-
-
-
-
- {peopleFromServer.map(person => (
-
- | {person.name} |
- {person.sex} |
- {person.born} |
-
- ))}
-
-
-
+export const App = () => {
+ const [people, setPeople] = useState(peopleFromServer);
+ const [selectedPeople, setSelectedPeople] = useState([]);
+ const [sortField, setSortField] = useState('');
+ const [isReversed, setReversed] = useState(false);
+
+ if (people.length === 0) {
+ return No people yet
;
+ }
+
+ function isSelected({ slug }: Person) {
+ return selectedPeople.some(person => person.slug === slug);
+ }
+
+ const sortBy = (field: string) => {
+ const isFirstClick = sortField !== field;
+ const isSecondClick = sortField === field && !isReversed;
+
+ setSortField(isFirstClick || isSecondClick ? field : '');
+ setReversed(isSecondClick);
+ };
+
+ const selectPerson = (personToAdd: Person) => {
+ setSelectedPeople(current => [...current, personToAdd]);
+ };
+
+ const unselectPerson = (personToDelete: Person) => {
+ setSelectedPeople(current => current.filter(
+ person => person.slug !== personToDelete.slug,
+ ));
+ };
+
+ const clearSelection = () => {
+ setSelectedPeople([]);
+ };
+
+ const moveUp = (personToMove: Person) => {
+ setPeople(currentPeople => {
+ const position = currentPeople.findIndex(
+ person => person.slug === personToMove.slug,
+ );
+
+ if (position === 0) {
+ return currentPeople;
+ }
+
+ return [
+ ...currentPeople.slice(0, position - 1),
+ currentPeople[position],
+ currentPeople[position - 1],
+ ...currentPeople.slice(position + 1),
+ ];
+ });
+ };
+
+ const moveDown = (personToMove: Person) => {
+ setPeople(curentPeople => {
+ const position = curentPeople.findIndex(
+ person => person.slug === personToMove.slug,
+ );
+
+ if (position === curentPeople.length - 1) {
+ return curentPeople;
+ }
+
+ const updatedPeople = [...curentPeople];
+
+ updatedPeople[position] = curentPeople[position + 1];
+ updatedPeople[position + 1] = curentPeople[position];
+
+ return updatedPeople;
+ });
+ };
+
+ const visiblePeople = [...people];
+
+ if (sortField) {
+ visiblePeople.sort(
+ (a, b) => {
+ switch (sortField) {
+ case 'name':
+ case 'sex':
+ return a[sortField].localeCompare(b[sortField]);
+
+ case 'born':
+ return a.born - b.born;
+
+ default:
+ return 0;
+ }
+ },
);
}
-}
+
+ if (isReversed) {
+ visiblePeople.reverse();
+ }
+
+ return (
+
+
+
+ {selectedPeople.length === 0 ? '-' : (
+ <>
+ {/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
+
+ {selectedPeople.map(p => p.name).join(', ')}
+ >
+ )}
+
+
+
+
+
+ | |
+
+ name
+ sortBy('name')}>
+
+
+
+
+ |
+
+
+ sex
+ sortBy('sex')}>
+
+
+
+
+ |
+
+
+ born
+ sortBy('born')}>
+
+
+
+
+ |
+
+ |
+
+
+
+
+ {visiblePeople.map(person => (
+
+ |
+ {isSelected(person) ? (
+
+ ) : (
+
+ )}
+ |
+
+
+ {person.name}
+ |
+
+ {person.sex} |
+ {person.born} |
+
+
+
+
+
+ |
+
+ ))}
+
+
+ );
+};
diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx
new file mode 100644
index 00000000..655f09a1
--- /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';
From 0eed0fc5125c1dc028c8ec539c49e6527fd5bb16 Mon Sep 17 00:00:00 2001
From: Denys Cheporniuk
Date: Sun, 5 Feb 2023 20:27:49 +0200
Subject: [PATCH 2/2] add people filtering
---
src/App.tsx | 130 +++++++++++++++++++--------
src/components/SortLink/SortLink.tsx | 24 +++++
src/components/SortLink/index.ts | 1 +
3 files changed, 118 insertions(+), 37 deletions(-)
create mode 100644 src/components/SortLink/SortLink.tsx
create mode 100644 src/components/SortLink/index.ts
diff --git a/src/App.tsx b/src/App.tsx
index 2f16615a..eeac0549 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -7,12 +7,15 @@ import 'bulma/css/bulma.css';
import peopleFromServer from './people.json';
import { Person } from './types/Person';
import { Button } from './components/Button';
+import { SortLink } from './components/SortLink';
export const App = () => {
const [people, setPeople] = useState(peopleFromServer);
const [selectedPeople, setSelectedPeople] = useState([]);
- const [sortField, setSortField] = useState('');
+ const [sortField, setSortField] = useState('');
const [isReversed, setReversed] = useState(false);
+ const [query, setQuery] = useState('');
+ const [sex, setSex] = useState('');
if (people.length === 0) {
return No people yet
;
@@ -22,7 +25,7 @@ export const App = () => {
return selectedPeople.some(person => person.slug === slug);
}
- const sortBy = (field: string) => {
+ const sortBy = (field: keyof Person) => {
const isFirstClick = sortField !== field;
const isSecondClick = sortField === field && !isReversed;
@@ -82,7 +85,27 @@ export const App = () => {
});
};
- const visiblePeople = [...people];
+ let visiblePeople = [...people];
+
+ if (sex) {
+ visiblePeople = visiblePeople.filter(person => person.sex === sex);
+ }
+
+ if (query) {
+ const lowerQuery = query.toLocaleLowerCase();
+
+ visiblePeople = visiblePeople.filter(person => {
+ const stringToCheck = `
+ ${person.name}
+ ${person.motherName || ''}
+ ${person.fatherName || ''}
+ `;
+
+ return stringToCheck
+ .toLocaleLowerCase()
+ .includes(lowerQuery);
+ });
+ }
if (sortField) {
visiblePeople.sort(
@@ -90,7 +113,13 @@ export const App = () => {
switch (sortField) {
case 'name':
case 'sex':
- return a[sortField].localeCompare(b[sortField]);
+ case 'motherName':
+ case 'fatherName': {
+ const aValue = a[sortField] || '';
+ const bValue = a[sortField] || '';
+
+ return aValue.localeCompare(bValue);
+ }
case 'born':
return a.born - b.born;
@@ -122,6 +151,31 @@ export const App = () => {
>
)}
+
+
+
+ setQuery(e.target.value)}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
@@ -129,47 +183,47 @@ export const App = () => {
|
name
- sortBy('name')}>
-
-
-
-
+ sortBy('name')}
+ />
|
sex
- sortBy('sex')}>
-
-
-
-
+ sortBy('sex')}
+ />
|
born
- sortBy('born')}>
-
-
-
-
+ sortBy('born')}
+ />
+ |
+
+
+ Mother
+ sortBy('motherName')}
+ />
+ |
+
+
+ Father
+ sortBy('fatherName')}
+ />
|
|
@@ -217,6 +271,8 @@ export const App = () => {
{person.sex} |
{person.born} |
+ {person.motherName || ''} |
+ {person.fatherName || ''} |
|