From 3c9a6ec7fde5106a6a0b3811a25a9bc446df9ab6 Mon Sep 17 00:00:00 2001 From: AndrzejPec Date: Fri, 6 Oct 2023 21:25:20 +0200 Subject: [PATCH 01/10] 1nitial commet --- README.md | 2 +- src/App.tsx | 21 ++++++++++++++------- src/components/HomePage.tsx | 5 +++++ src/components/Navbar.tsx | 24 ++++++++++++++++++------ src/components/PageNotFound.tsx | 5 +++++ src/components/PersonLink.tsx | 17 +++++++++++++++++ src/types/Person.ts | 10 ++++++++++ 7 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 src/components/HomePage.tsx create mode 100644 src/components/PageNotFound.tsx create mode 100644 src/components/PersonLink.tsx diff --git a/README.md b/README.md index 24519caab..19d012e7d 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://andrzejpec.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..3ef8a5ac6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,20 +1,27 @@ +import './App.scss'; +import { Routes, Route, Navigate } from 'react-router-dom'; +import { HomePage } from './components/HomePage'; import { PeoplePage } from './components/PeoplePage'; +import { PageNotFound } from './components/PageNotFound'; import { Navbar } from './components/Navbar'; -import './App.scss'; - export const App = () => { return (
+ -
+
-

Home Page

-

Page not found

- + + } /> + } /> + } /> + } /> + } /> +
-
+
); }; diff --git a/src/components/HomePage.tsx b/src/components/HomePage.tsx new file mode 100644 index 000000000..0b41a51df --- /dev/null +++ b/src/components/HomePage.tsx @@ -0,0 +1,5 @@ +export const HomePage = () => { + return ( +

Home Page

+ ); +}; diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index c2aa20f1c..cfffb70c8 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,3 +1,6 @@ +import { NavLink } from 'react-router-dom'; +import classNames from 'classnames'; + export const Navbar = () => { return ( diff --git a/src/components/PageNotFound.tsx b/src/components/PageNotFound.tsx new file mode 100644 index 000000000..0eaf93c08 --- /dev/null +++ b/src/components/PageNotFound.tsx @@ -0,0 +1,5 @@ +export const PageNotFound = () => { + return ( +

Page not found

+ ); +}; diff --git a/src/components/PersonLink.tsx b/src/components/PersonLink.tsx new file mode 100644 index 000000000..3f781be51 --- /dev/null +++ b/src/components/PersonLink.tsx @@ -0,0 +1,17 @@ +import { Link } from 'react-router-dom'; +import { PersonLinkProps } from '../types'; + +export const PersonLink: React.FC = ({ person, people }) => { + const existingPerson = people.find(p => p.name === person); + + return existingPerson ? ( + + {person} + + ) : ( + {person || '-'} + ); +}; diff --git a/src/types/Person.ts b/src/types/Person.ts index 952873d1d..0a09135fe 100644 --- a/src/types/Person.ts +++ b/src/types/Person.ts @@ -9,3 +9,13 @@ export interface Person { mother?: Person, father?: Person, } + +export interface PeoplePageProps { + people: Person[]; + isLoading: boolean; +} + +export interface PersonLinkProps { + person: string; + people: Person[]; +} From 066f1903f9a6d151ca837f037ab0490f63326ed0 Mon Sep 17 00:00:00 2001 From: AndrzejPec Date: Fri, 6 Oct 2023 21:59:14 +0200 Subject: [PATCH 02/10] table rendering fine --- src/components/PeoplePage.tsx | 52 ++- src/components/PeopleTable.tsx | 642 +-------------------------------- 2 files changed, 59 insertions(+), 635 deletions(-) diff --git a/src/components/PeoplePage.tsx b/src/components/PeoplePage.tsx index 4c8713b16..9f13257fc 100644 --- a/src/components/PeoplePage.tsx +++ b/src/components/PeoplePage.tsx @@ -1,31 +1,57 @@ +import { useEffect, useState } from 'react'; +import { getPeople } from '../api'; +import { Person } from '../types'; import { PeopleFilters } from './PeopleFilters'; import { Loader } from './Loader'; import { PeopleTable } from './PeopleTable'; export const PeoplePage = () => { + const [people, setPeople] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [hasError, setHasError] = useState(false); + + useEffect(() => { + getPeople() + .then((data) => { + setPeople(data); + setIsLoading(false); + }) + .catch(() => { + setIsLoading(false); + setHasError(true); + }); + }, []); + + const renderContent = () => { + switch (true) { + + case hasError: + return

Something went wrong

; + + case isLoading: + return ; + + case people.length === 0: + return ( +

There are no people on the server

+ ); + + default: + return ; + } + }; + return ( <>

People Page

-
-
- - -

Something went wrong

- -

- There are no people on the server -

- -

There are no people matching the current search criteria

- - + {renderContent()}
diff --git a/src/components/PeopleTable.tsx b/src/components/PeopleTable.tsx index 8ab5b90f7..8afc75657 100644 --- a/src/components/PeopleTable.tsx +++ b/src/components/PeopleTable.tsx @@ -1,4 +1,7 @@ -export const PeopleTable = () => { +import { Person } from '../types'; +import { PersonLink } from './PersonLink'; + +export const PeopleTable = ({ people }: { people: Person[] }) => { return ( { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + {people.map((person: Person) => ( + + + + + + + + + ))}
- 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 -
+ + {person.sex}{person.born}{person.died} + + + +
); From 90a8fb4a5818c78f441c1d14fb1df3ece6d05fdb Mon Sep 17 00:00:00 2001 From: AndrzejPec Date: Fri, 6 Oct 2023 22:11:50 +0200 Subject: [PATCH 03/10] all tests passing --- src/components/PeoplePage.tsx | 1 - src/components/PeopleTable.tsx | 11 ++++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/PeoplePage.tsx b/src/components/PeoplePage.tsx index 9f13257fc..4a8c00a0e 100644 --- a/src/components/PeoplePage.tsx +++ b/src/components/PeoplePage.tsx @@ -24,7 +24,6 @@ export const PeoplePage = () => { const renderContent = () => { switch (true) { - case hasError: return

Something went wrong

; diff --git a/src/components/PeopleTable.tsx b/src/components/PeopleTable.tsx index 8afc75657..fa37b5595 100644 --- a/src/components/PeopleTable.tsx +++ b/src/components/PeopleTable.tsx @@ -1,7 +1,12 @@ +// import { useState } from 'react'; +import { useParams } from 'react-router-dom'; import { Person } from '../types'; import { PersonLink } from './PersonLink'; export const PeopleTable = ({ people }: { people: Person[] }) => { + // const [selectedPerson, setSelectedPerson] = useState(null); + const { slug } = useParams(); + return ( { {people.map((person: Person) => ( - + From 147157c88c4ad9b0e0bea6d32bb6e7d29824518f Mon Sep 17 00:00:00 2001 From: AndrzejPec Date: Fri, 6 Oct 2023 22:47:00 +0200 Subject: [PATCH 04/10] added context --- src/SearchParamsContext.tsx | 37 ++++++++++++++++++++++++++++++++ src/components/PeopleFilters.tsx | 4 ++++ src/components/PeoplePage.tsx | 5 +++++ src/components/PeopleTable.tsx | 2 -- src/index.tsx | 6 +++++- 5 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 src/SearchParamsContext.tsx diff --git a/src/SearchParamsContext.tsx b/src/SearchParamsContext.tsx new file mode 100644 index 000000000..169f2ae83 --- /dev/null +++ b/src/SearchParamsContext.tsx @@ -0,0 +1,37 @@ +import { createContext, useContext, ReactNode } from 'react'; +import { useSearchParams } from 'react-router-dom'; + +interface SearchParamsContextProps { + searchParams: URLSearchParams; + setSearchParams: (params: URLSearchParams) => void; +} + +const SearchParamsContext = createContext(undefined); + +export const useSearchParamsContext = () => { + const context = useContext(SearchParamsContext); + + if (!context) { + throw new + Error('useSearchParamsContext must be used within a SearchParamsProvider'); + } + + return context; +}; + +interface SearchParamsProviderProps { + children: ReactNode; +} + +export const SearchParamsProvider: React.FC = ({ + children +}) => { + const [searchParams, setSearchParams] = useSearchParams(); + + return ( + + {children} + + ); +}; diff --git a/src/components/PeopleFilters.tsx b/src/components/PeopleFilters.tsx index 2f1608bef..bab38c382 100644 --- a/src/components/PeopleFilters.tsx +++ b/src/components/PeopleFilters.tsx @@ -1,4 +1,8 @@ +// import { useSearchParamsContext } from '../SearchParamsContext'; + export const PeopleFilters = () => { + // const { searchParams, setSearchParams } = useSearchParamsContext(); + return ( - - - - From 4e272d0ac2b9da071bc35d57c0a4891688fd900f Mon Sep 17 00:00:00 2001 From: AndrzejPec Date: Sun, 8 Oct 2023 09:10:34 +0200 Subject: [PATCH 07/10] got rid of nested ternaries --- src/components/PeopleFilters.tsx | 36 +++++++++++++++++++++++--- src/components/PeoplePage.tsx | 44 +++++++++++++++++++++++--------- src/components/PeopleTable.tsx | 37 +++++++++++++++++++++------ 3 files changed, 94 insertions(+), 23 deletions(-) diff --git a/src/components/PeopleFilters.tsx b/src/components/PeopleFilters.tsx index 246f4dc75..c8094f635 100644 --- a/src/components/PeopleFilters.tsx +++ b/src/components/PeopleFilters.tsx @@ -8,6 +8,18 @@ export const PeopleFilters = () => { const selectedCenturies = searchParams.getAll('centuries'); const inputValue = searchParams.get('query') || ''; + const selectedSex = searchParams.get('sex') || 'all'; + + const handleSexFilterChange = (sex: string) => { + if (sex === 'all') { + searchParams.delete('sex'); + } else { + searchParams.set('sex', sex); + } + + setSearchParams(new URLSearchParams(searchParams.toString())); + }; + const handleNameFilterChange = (event: React.ChangeEvent< HTMLInputElement >) => { @@ -60,9 +72,27 @@ export const PeopleFilters = () => {

Filters

- All - Male - Female + handleSexFilterChange('all')} + > + All + + handleSexFilterChange('m')} + > + Male + + handleSexFilterChange('f')} + > + Female +

diff --git a/src/components/PeoplePage.tsx b/src/components/PeoplePage.tsx index f6fe5c5db..ba0c86979 100644 --- a/src/components/PeoplePage.tsx +++ b/src/components/PeoplePage.tsx @@ -20,8 +20,9 @@ export const PeoplePage = () => { const query = searchParams.get('query') || ''; const selectedCenturies = searchParams.getAll('centuries'); + const selectedSex = searchParams.get('sex'); - const filteredPeople = people.filter(person => { + const filteredByQuery = people.filter(person => { if (query) { return person.name.toLowerCase().includes(query.toLowerCase()) || person.motherName?.toLowerCase().includes(query.toLowerCase()) @@ -31,7 +32,7 @@ export const PeoplePage = () => { return true; }); - const filteredByCentury = filteredPeople.filter(person => { + const filteredByCentury = filteredByQuery.filter(person => { if (selectedCenturies.length > 0) { const century = Math.ceil(person.died / 100); @@ -41,6 +42,16 @@ export const PeoplePage = () => { return true; }); + const filteredBySex = filteredByCentury.filter(person => { + if (selectedSex) { + return person.sex === selectedSex; + } + + return true; + }); + + const filteredPeople = filteredBySex; + const handleSort = (field: string) => { let newOrder = 'asc'; const currentField = searchParams.get('sort'); @@ -64,22 +75,24 @@ export const PeoplePage = () => { }; useEffect(() => { - const newPeople = [...filteredByCentury]; + const newPeople = [...filteredPeople]; const sortField = searchParams.get('sort'); const sortOrder = searchParams.get('order'); if (sortField && sortOrder) { + const firstPerson = newPeople[0]; + const sortFieldType = typeof firstPerson?.[sortField as keyof Person]; + newPeople.sort((a, b) => { - if (typeof a[sortField as keyof Person] === 'string' - && typeof b[sortField as keyof Person] === 'string') { - return (a[sortField as keyof Person] as string) - .localeCompare(b[sortField as keyof Person] as string); + const aValue = a[sortField as keyof Person]; + const bValue = b[sortField as keyof Person]; + + if (sortFieldType === 'string') { + return (aValue as string).localeCompare(bValue as string); } - if (typeof a[sortField as keyof Person] === 'number' - && typeof b[sortField as keyof Person] === 'number') { - return (a[sortField as keyof Person] as number) - - (b[sortField as keyof Person] as number); + if (sortFieldType === 'number') { + return (aValue as number) - (bValue as number); } return 0; @@ -91,7 +104,7 @@ export const PeoplePage = () => { } setSortedPeople(newPeople); - }, [filteredByCentury, searchParams]); + }, [filteredPeople, searchParams]); useEffect(() => { getPeople() @@ -118,6 +131,13 @@ export const PeoplePage = () => {

There are no people on the server

); + case filteredByQuery.length === 0: + return ( +

+ There are no people matching the current search criteria +

+ ); + default: return ( { + if (sortState.sortField === field) { + return sortState.sortOrder === 'asc' + ? 'fas fa-sort-up' + : 'fas fa-sort-down'; + } + + return 'fas fa-sort'; +}; + export const PeopleTable = ({ people, handleSort, @@ -24,8 +37,10 @@ export const PeopleTable = ({ Name - {/* eslint-disable-next-line no-nested-ternary */} - + @@ -34,8 +49,10 @@ export const PeopleTable = ({ Sex - {/* eslint-disable-next-line no-nested-ternary */} - + @@ -44,8 +61,10 @@ export const PeopleTable = ({ Born - {/* eslint-disable-next-line no-nested-ternary */} - + @@ -54,8 +73,10 @@ export const PeopleTable = ({ Died - {/* eslint-disable-next-line no-nested-ternary */} - + From 652e7c7d307e249b98489535a04ecffb1b472485 Mon Sep 17 00:00:00 2001 From: AndrzejPec Date: Sun, 8 Oct 2023 11:21:58 +0200 Subject: [PATCH 08/10] fixes in filtering consistency --- src/App.tsx | 2 ++ src/components/PeopleFilters.tsx | 44 ++++++++------------------------ src/components/PeoplePage.tsx | 6 ++--- src/components/PersonLink.tsx | 15 ++++++++++- src/components/SexFilter.tsx | 42 ++++++++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 38 deletions(-) create mode 100644 src/components/SexFilter.tsx diff --git a/src/App.tsx b/src/App.tsx index 3ef8a5ac6..9239ccc91 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -13,6 +13,7 @@ export const App = () => {
+ } /> } /> @@ -20,6 +21,7 @@ export const App = () => { } /> } /> +
diff --git a/src/components/PeopleFilters.tsx b/src/components/PeopleFilters.tsx index c8094f635..42862aa04 100644 --- a/src/components/PeopleFilters.tsx +++ b/src/components/PeopleFilters.tsx @@ -1,4 +1,5 @@ import { useSearchParamsContext } from '../SearchParamsContext'; +import { SexFilter } from './SexFilter'; const CENTURIES = ['16', '17', '18', '19', '20']; @@ -8,17 +9,17 @@ export const PeopleFilters = () => { const selectedCenturies = searchParams.getAll('centuries'); const inputValue = searchParams.get('query') || ''; - const selectedSex = searchParams.get('sex') || 'all'; + // const selectedSex = searchParams.get('sex') || 'all'; - const handleSexFilterChange = (sex: string) => { - if (sex === 'all') { - searchParams.delete('sex'); - } else { - searchParams.set('sex', sex); - } + // const handleSexFilterChange = (sex: string) => { + // if (sex === 'all') { + // searchParams.delete('sex'); + // } else { + // searchParams.set('sex', sex); + // } - setSearchParams(new URLSearchParams(searchParams.toString())); - }; + // setSearchParams(new URLSearchParams(searchParams.toString())); + // }; const handleNameFilterChange = (event: React.ChangeEvent< HTMLInputElement @@ -69,31 +70,8 @@ export const PeopleFilters = () => { return (
+ handleSort('name')}> Name - - - - - + + {/* eslint-disable-next-line no-nested-ternary */} + + + handleSort('sex')}> Sex - - - - - + + {/* eslint-disable-next-line no-nested-ternary */} + + + handleSort('born')}> Born - - - - - + + {/* eslint-disable-next-line no-nested-ternary */} + + + handleSort('died')}> Died - - - - - + + {/* eslint-disable-next-line no-nested-ternary */} + +