From 801f67e0c3a3ee561e6f2e86483fe84b712d0a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20B=C4=85bo=C5=84?= Date: Mon, 9 Oct 2023 00:28:01 +0200 Subject: [PATCH 1/2] solution --- README.md | 2 +- src/App.tsx | 16 +- src/components/Navbar.tsx | 22 +- src/components/PeopleFilters.tsx | 124 ++--- src/components/PeoplePage.tsx | 100 +++- src/components/PeopleTable.tsx | 767 +++++-------------------------- src/components/PersonLink.tsx | 27 ++ src/types/Params.ts | 4 + src/types/SortCategories.ts | 6 + src/types/index.ts | 2 + 10 files changed, 351 insertions(+), 719 deletions(-) create mode 100644 src/components/PersonLink.tsx create mode 100644 src/types/Params.ts create mode 100644 src/types/SortCategories.ts diff --git a/README.md b/README.md index 24519caab..7d32c566e 100644 --- a/README.md +++ b/README.md @@ -28,4 +28,4 @@ implement the ability to filter and sort people in the table. - Implement a solution following the [React task guideline](https://github.com/mate-academy/react_task-guideline#react-tasks-guideline). - Use the [React TypeScript cheat sheet](https://mate-academy.github.io/fe-program/js/extra/react-typescript). - Open one more terminal and run tests with `npm test` to ensure your solution is correct. -- Replace `` with your Github username in the [DEMO LINK](https://.github.io/react_people-table-advanced/) and add it to the PR description. +- Replace `` with your Github username in the [DEMO LINK](https://patryk-babon.github.io/react_people-table-advanced/) and add it to the PR description. diff --git a/src/App.tsx b/src/App.tsx index adcb8594e..7bd28756a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,3 +1,4 @@ +import { Navigate, Route, Routes } from 'react-router-dom'; import { PeoplePage } from './components/PeoplePage'; import { Navbar } from './components/Navbar'; @@ -10,9 +11,18 @@ export const App = () => {
-

Home Page

-

Page not found

- + + Home Page} /> + } /> + + } /> + } /> + + Page not found} + /> +
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index c2aa20f1c..0380c899c 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,4 +1,13 @@ +import classNames from 'classnames'; +import { NavLink } from 'react-router-dom'; + export const Navbar = () => { + const getLinkClass = ({ isActive }:{ isActive: boolean }) => classNames( + 'navbar-item', { + 'has-background-grey-lighter': isActive, + }, + ); + return ( diff --git a/src/components/PeopleFilters.tsx b/src/components/PeopleFilters.tsx index 2f1608bef..5b2e0bbec 100644 --- a/src/components/PeopleFilters.tsx +++ b/src/components/PeopleFilters.tsx @@ -1,12 +1,49 @@ +import classNames from 'classnames'; +import { useSearchParams } from 'react-router-dom'; +import { SearchLink } from './SearchLink'; + export const PeopleFilters = () => { + const [searchParams, setSearchParams] = useSearchParams(); + const query = searchParams.get('query') || ''; + const centuries = searchParams.getAll('centuries') || ''; + const sex = searchParams.get('sex') || ''; + const arrayCentury = ['16', '17', '18', '19', '20']; + + function handleQueryChange(event: React.ChangeEvent) { + const params = new URLSearchParams(searchParams); + + params.set('query', event.target.value); + setSearchParams(params); + } + return ( ); diff --git a/src/components/PeoplePage.tsx b/src/components/PeoplePage.tsx index 4c8713b16..0e92258fc 100644 --- a/src/components/PeoplePage.tsx +++ b/src/components/PeoplePage.tsx @@ -1,12 +1,82 @@ +import { useSearchParams } from 'react-router-dom'; +import { useEffect, useMemo, useState } from 'react'; import { PeopleFilters } from './PeopleFilters'; import { Loader } from './Loader'; import { PeopleTable } from './PeopleTable'; +import { Person, SortCategories } from '../types'; +import { getPeople } from '../api'; export const PeoplePage = () => { + const [people, setPeople] = useState ([]); + const [isLoading, setIsLoading] = useState(true); + const [isError, setIsError] = useState(false); + const [searchParams] = useSearchParams(); + const query = searchParams.get('query') || ''; + const centuries = searchParams.getAll('centuries') || ''; + const sex = searchParams.get('sex') || ''; + const order = searchParams.get('order') || null; + const sort = searchParams.get('sort') || null; + + useEffect(() => { + getPeople() + .then(setPeople) + .catch(() => setIsError(true)) + .finally(() => setIsLoading(false)); + }, []); + + const visiblePeople = useMemo(() => { + let preparedPeople = people; + + if (query) { + preparedPeople = preparedPeople.filter( + person => person.name.toLowerCase().includes(query.toLowerCase().trim()) + || person.motherName?.toLowerCase().includes(query.toLowerCase().trim()) + || person.fatherName?.toLowerCase() + .includes(query.toLowerCase().trim()), + ); + } + + if (centuries.length > 0) { + preparedPeople = preparedPeople.filter(person => ( + centuries.includes(Math.ceil(person.born / 100).toString()) + )); + } + + if (sex) { + preparedPeople = preparedPeople.filter(person => ( + person.sex === sex + )); + } + + if (sort) { + preparedPeople = [...preparedPeople].sort((person1, person2) => { + switch (sort) { + case SortCategories.Name: + case SortCategories.Sex: + return person1[sort].localeCompare(person2[sort]); + case SortCategories.Born: + case SortCategories.Died: + return person2[sort] - person1[sort]; + default: + return 0; + } + }); + } + + if (order) { + preparedPeople.reverse(); + } + + return preparedPeople; + }, [people, query, centuries, sex, sort, order]); + + if (isLoading) { + return ; + } + return ( <>

People Page

-
@@ -15,18 +85,24 @@ export const PeoplePage = () => {
- - -

Something went wrong

- -

- There are no people on the server -

- -

There are no people matching the current search criteria

- - + {!people.length ? ( +

+ There are no people on the server +

+ ) + : }
+ {!visiblePeople.length && ( +

+ There are no people matching the current search + criteria +

+ )} + {!isLoading && isError && ( +

+ Something went wrong +

+ )}
diff --git a/src/components/PeopleTable.tsx b/src/components/PeopleTable.tsx index 8ab5b90f7..4a83fef53 100644 --- a/src/components/PeopleTable.tsx +++ b/src/components/PeopleTable.tsx @@ -1,4 +1,53 @@ -export const PeopleTable = () => { +import { useParams, useSearchParams } from 'react-router-dom'; +import classNames from 'classnames'; +import { Person, Params, SortCategories } from '../types'; +import { PersonLink } from './PersonLink'; +import { getSearchWith } from '../utils/searchHelper'; + +type Props = { + people: Person[]; +}; + +export const PeopleTable: React.FC = ({ people }) => { + const { slugPerson } = useParams(); + const [searchParams, setSearchParams] = useSearchParams(); + const order = searchParams.get('order') || null; + const sort = searchParams.get('sort') || null; + + function iconSort(field: SortCategories) { + if (sort === field && order !== 'desc') { + return (); + } + + if (sort === field && order === 'desc') { + return (); + } + + return ; + } + + function getPersonName(name: string) { + return people.find(pers => pers.name === name); + } + + function setSearch(params: Params) { + const reselt = getSearchWith(searchParams, params); + + setSearchParams(reselt); + } + + function handleClick(field: SortCategories) { + if (sort !== field) { + setSearch({ sort: field, order: null }); + } else if (sort === field && order) { + setSearch({ sort: null, order: null }); + } else if (sort === field) { + setSearch({ + order: 'desc', + }); + } + } + return ( { @@ -54,629 +114,50 @@ export const PeopleTable = () => { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + {people.map(({ + sex, born, died, motherName, fatherName, slug, name, + }) => { + const mother = motherName + ? getPersonName(motherName) : undefined; + const father = fatherName + ? getPersonName(fatherName) : undefined; + + return ( + + + + + + + + + + ); + })}
Name - - - - - + Sex - - - - - + Born - - - - - + Died - - - - - + Father
- Pieter Haverbeke - m16021642- - - Lieven van Haverbeke - -
- - Anna van Hecke - - f16071670Martijntken BeelaertPaschasius van Hecke
- Lieven Haverbeke - m16311676 - - Anna van Hecke - - - Pieter Haverbeke -
- - Elisabeth Hercke - - f16321674Margriet de BrabanderWillem Hercke
- Daniel Haverbeke - m16521723 - - Elisabeth Hercke - - - Lieven Haverbeke -
- - Joanna de Pape - - f16541723Petronella WautersVincent de Pape
- - Martina de Pape - - f16661727Petronella WautersVincent de Pape
- Willem Haverbeke - m16681731 - - Elisabeth Hercke - - - Lieven Haverbeke -
- Jan Haverbeke - m16711731 - - Elisabeth Hercke - - - Lieven Haverbeke -
- - Maria de Rycke - - f16831724Laurentia van VlaenderenFrederik de Rycke
- - Livina Haverbeke - - f16921743 - - Joanna de Pape - - - Daniel Haverbeke -
- - Pieter Bernard Haverbeke - - m16951762Petronella Wauters - Willem Haverbeke -
- - Lieven de Causmaecker - - m16961724Joanna ClaesCarel de Causmaecker
- - Jacoba Lammens - - f16991740Livina de VriezeLieven Lammens
- Pieter de Decker - m17051780Petronella van de SteeneJoos de Decker
- - Laurentia Haverbeke - - f17101786 - - Maria de Rycke - - - Jan Haverbeke -
- - Elisabeth Haverbeke - - f17111754 - - Maria de Rycke - - - Jan Haverbeke -
- Jan van Brussel - m17141748Joanna van RootenJacobus van Brussel
- - Bernardus de Causmaecker - - m17211789 - - Livina Haverbeke - - - - Lieven de Causmaecker - -
- - Jan Francies Haverbeke - - m17251779Livina de Vrieze - - Pieter Bernard Haverbeke - -
- - Angela Haverbeke - - f17281734Livina de Vrieze - - Pieter Bernard Haverbeke - -
- - Petronella de Decker - - f17311781 - - Livina Haverbeke - - - Pieter de Decker -
- - Jacobus Bernardus van Brussel - - m17361809 - - Elisabeth Haverbeke - - - Jan van Brussel -
- - Pieter Antone Haverbeke - - m17531798 - - Petronella de Decker - - - - Jan Francies Haverbeke - -
- - Jan Frans van Brussel - - m17611833- - - Jacobus Bernardus van Brussel - -
- - Livina Sierens - - f17611826Maria van WaesJan Sierens
- - Joanna de Causmaecker - - f17621807- - - Bernardus de Causmaecker - -
- Carel Haverbeke - m17961837 - - Livina Sierens - - - - Pieter Antone Haverbeke - -
- - Maria van Brussel - - f18011834 - - Joanna de Causmaecker - - - - Jan Frans van Brussel - -
- Carolus Haverbeke - m18321905 - - Maria van Brussel - - - Carel Haverbeke -
- - Maria Sturm - - f18351917Seraphina SpelierCharles Sturm
- - Emma de Milliano - - f18761956Sophia van DammePetrus de Milliano
- Emile Haverbeke - m18771968 - - Maria Sturm - - - Carolus Haverbeke -
+ + {sex}{born}{died} + {mother + ? ( + + ) + : motherName || (-)} + + {father + ? ( + + ) + : fatherName || (-)} +
); diff --git a/src/components/PersonLink.tsx b/src/components/PersonLink.tsx new file mode 100644 index 000000000..61168feae --- /dev/null +++ b/src/components/PersonLink.tsx @@ -0,0 +1,27 @@ +import classNames from 'classnames'; + +import { Link, useSearchParams } from 'react-router-dom'; +import { Person } from '../types'; + +type PersonLinkProps = { + person: Person; +}; + +export const PersonLink: React.FC = ({ person }) => { + const { name, slug, sex } = person; + const [searchParams] = useSearchParams(); + + return ( + + {name} + + ); +}; diff --git a/src/types/Params.ts b/src/types/Params.ts new file mode 100644 index 000000000..d9620a22d --- /dev/null +++ b/src/types/Params.ts @@ -0,0 +1,4 @@ +export type Params = { + sort?: string | null, + order: 'desc' | null, +}; diff --git a/src/types/SortCategories.ts b/src/types/SortCategories.ts new file mode 100644 index 000000000..046d9f241 --- /dev/null +++ b/src/types/SortCategories.ts @@ -0,0 +1,6 @@ +export enum SortCategories { + Name = 'name', + Sex = 'sex', + Born = 'born', + Died = 'died', +} diff --git a/src/types/index.ts b/src/types/index.ts index 1cbfa3601..cba046f78 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1 +1,3 @@ +export * from './Params'; export * from './Person'; +export * from './SortCategories'; From 0ec5fdb2b4820f79e54dfba66585a8505d9f67e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20B=C4=85bo=C5=84?= Date: Tue, 10 Oct 2023 10:41:58 +0200 Subject: [PATCH 2/2] suggested changes --- src/components/PeopleFilters.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/PeopleFilters.tsx b/src/components/PeopleFilters.tsx index 5b2e0bbec..f4068bab1 100644 --- a/src/components/PeopleFilters.tsx +++ b/src/components/PeopleFilters.tsx @@ -2,12 +2,13 @@ import classNames from 'classnames'; import { useSearchParams } from 'react-router-dom'; import { SearchLink } from './SearchLink'; +const FILTER_CENTURIES = ['16', '17', '18', '19', '20']; + export const PeopleFilters = () => { const [searchParams, setSearchParams] = useSearchParams(); const query = searchParams.get('query') || ''; const centuries = searchParams.getAll('centuries') || ''; const sex = searchParams.get('sex') || ''; - const arrayCentury = ['16', '17', '18', '19', '20']; function handleQueryChange(event: React.ChangeEvent) { const params = new URLSearchParams(searchParams); @@ -66,7 +67,7 @@ export const PeopleFilters = () => {
- {arrayCentury.map((century) => ( + {FILTER_CENTURIES.map((century) => (