diff --git a/src/App.tsx b/src/App.tsx index adcb8594e..2e62bcfc5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,18 +1,16 @@ -import { PeoplePage } from './components/PeoplePage'; -import { Navbar } from './components/Navbar'; +import { Outlet } from 'react-router-dom'; +import { NavBar } from './components/Navbar'; import './App.scss'; export const App = () => { return (
- +
-

Home Page

-

Page not found

- +
diff --git a/src/api.ts b/src/api.ts index c799527b8..7b65c9631 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,4 +1,4 @@ -import { Person } from './types/Person'; +import { PersonType } from './types/Person'; // eslint-disable-next-line max-len const API_URL = 'https://mate-academy.github.io/react_people-table/api/people.json'; @@ -7,9 +7,9 @@ function wait(delay: number) { return new Promise(resolve => setTimeout(resolve, delay)); } -export async function getPeople(): Promise { +export async function getPeople(): Promise { // keep this delay for testing purpose return wait(500) .then(() => fetch(API_URL)) - .then(response => response.json()); + .then((response) => response.json()); } 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..fa82d34e3 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,4 +1,11 @@ -export const Navbar = () => { +import classNames from 'classnames'; +import { NavLink } from 'react-router-dom'; + +export const NavBar = () => { + const handleNavActive = ({ isActive }: { isActive: boolean }) => classNames( + 'navbar-item', { 'has-background-grey-lighter': isActive }, + ); + return ( diff --git a/src/components/NotFoundPage.tsx b/src/components/NotFoundPage.tsx new file mode 100644 index 000000000..c38405914 --- /dev/null +++ b/src/components/NotFoundPage.tsx @@ -0,0 +1,5 @@ +export const NotFoundPage = () => { + return ( +

Page not found

+ ); +}; diff --git a/src/components/PeopleFilters.tsx b/src/components/PeopleFilters.tsx index 2f1608bef..6408db205 100644 --- a/src/components/PeopleFilters.tsx +++ b/src/components/PeopleFilters.tsx @@ -1,12 +1,51 @@ +import { useSearchParams } from 'react-router-dom'; +import { ChangeEvent } from 'react'; +import cn from 'classnames'; +import { SearchLink } from './SearchLink'; +import { getSearchWith } from '../utils/searchHelper'; +import { searchENUM } from '../types/serchENUM'; + export const PeopleFilters = () => { + const [searchParams, setSearchParams] = useSearchParams(); + const centuriesList = ['16', '17', '18', '19', '20']; + + const sex = searchParams.get(searchENUM.Sex); + const centuries = searchParams.getAll(searchENUM.Centuries); + const query = searchParams.get(searchENUM.Query) || ''; + + function handleQuery(event: ChangeEvent) { + const params = (event.target.value === '') + ? getSearchWith(searchParams, { query: null }) + : getSearchWith(searchParams, { query: event.target.value }); + + setSearchParams(params); + } + return ( ); diff --git a/src/components/PeoplePage.tsx b/src/components/PeoplePage.tsx index 4c8713b16..f246e29b3 100644 --- a/src/components/PeoplePage.tsx +++ b/src/components/PeoplePage.tsx @@ -1,35 +1,133 @@ +import { useEffect, useState } from 'react'; +import { useParams, useSearchParams } from 'react-router-dom'; import { PeopleFilters } from './PeopleFilters'; import { Loader } from './Loader'; import { PeopleTable } from './PeopleTable'; +import { PersonType } from '../types'; +import { getPeople } from '../api'; + +const peopleOnServer = (people: PersonType[]) => { + return people.map(person => { + const mother = people.find(pers => pers.name === person.motherName); + const father = people.find(pers => pers.name === person.fatherName); + + return ({ + ...person, + mother, + father, + }); + }); +}; export const PeoplePage = () => { + const [people, setPeople] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(false); + const [searchParams] = useSearchParams(); + const { slug } = useParams(); + + useEffect(() => { + setLoading(true); + getPeople() + .then(peopleOnServer) + .then(setPeople) + .catch(() => setError(true)) + .finally(() => setLoading(false)); + }, []); + + const preparedPeople = () => { + const sort = searchParams.get('sort'); + const order = searchParams.get('order'); + const sex = searchParams.get('sex'); + const query = searchParams.get('query')?.trim().toLocaleLowerCase(); + const centuries = searchParams.getAll('centuries'); + let prepared = [...people]; + + if (query) { + prepared = prepared.filter(elem => elem.name.toLowerCase() + .includes(query) + || elem.motherName?.toLowerCase().includes(query) + || elem.fatherName?.toLowerCase().includes(query)); + } + + if (sex) { + prepared = prepared.filter(elem => elem.sex === sex); + } + + if (centuries && centuries.length > 0) { + prepared = prepared.filter(elem => ( + centuries?.includes(Math.ceil(elem.born / 100).toString()))); + } + + if (sort) { + switch (sort) { + case 'name': + case 'sex': + { + prepared.sort((a, b) => a[sort].localeCompare(b[sort])); + break; + } + + case 'born': + case 'died': { + prepared.sort((a, b) => a[sort] - b[sort]); + break; + } + + default: + } + } + + if (order) { + prepared.reverse(); + } + + return prepared; + }; + return ( <>

People Page

+ {loading + ? ( + + ) + : ( +
+
+
+ +
-
-
-
- -
- -
-
- +
+
-

Something went wrong

+ {error && ( +

+ Something went wrong +

+ )} -

- There are no people on the server -

+ {!loading && !people.length && ( +

+ There are no people on the server +

+ )} -

There are no people matching the current search criteria

+ {(!preparedPeople().length && !loading) && ( +

+ There are no people matching the current search criteria +

+ )} - + {people.length > 0 && ( + + )} +
+
-
-
+ )} ); }; diff --git a/src/components/PeopleTable.tsx b/src/components/PeopleTable.tsx index b8275e8e0..13a1dfcd4 100644 --- a/src/components/PeopleTable.tsx +++ b/src/components/PeopleTable.tsx @@ -1,5 +1,53 @@ -/* eslint-disable jsx-a11y/control-has-associated-label */ -export const PeopleTable = () => { +import { useSearchParams, Link } from 'react-router-dom'; +import cn from 'classnames'; +import { SearchLink } from './SearchLink'; +import { SearchParams } from '../utils/searchHelper'; +import { PersonType } from '../types'; + +type Props = { + people: PersonType[]; + Slug?: string; +}; + +export const PeopleTable: React.FC = ({ people, Slug }) => { + const [searchParams] = useSearchParams(); + const sort = searchParams.get('sort'); + const order = searchParams.get('order'); + + const sortParams = (param: string) => { + if (sort !== param) { + return { + sort: param, + order: null, + }; + } + + if (sort === param && order !== 'desc') { + return { order: 'desc' }; + } + + return { + sort: null, + order: null, + }; + }; + + const personLink = (person: PersonType) => { + const { name, sex, slug } = person; + + return ( + + {name} + + ); + }; + return ( { @@ -57,627 +133,35 @@ export const PeopleTable = () => { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + {people.map(person => { + return ( + + + + + + + + + + ); + })}
Name - + - + - + Sex - + - + - + Born - + - + - + Died - + - + - +
- 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 -
+ {personLink(person)} + {person.sex}{person.born}{person.died} + {person.mother + ? personLink(person.mother) + : person.motherName || '-'} + + {person.father + ? personLink(person.father) + : person.fatherName || '-'} +
); diff --git a/src/components/Root.tsx b/src/components/Root.tsx new file mode 100644 index 000000000..822532d5b --- /dev/null +++ b/src/components/Root.tsx @@ -0,0 +1,29 @@ +import { + Navigate, + Route, + HashRouter as Router, + Routes, +} from 'react-router-dom'; + +import { App } from '../App'; +import { PeoplePage } from './PeoplePage'; +import { NotFoundPage } from './NotFoundPage'; + +export const Root = () => ( + + + }> + Home Page} /> + } /> + + } /> + + + } + /> + + + +); diff --git a/src/index.tsx b/src/index.tsx index 8cb63aeff..c1b0d16b5 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,14 +1,9 @@ import { createRoot } from 'react-dom/client'; -import { HashRouter as Router } from 'react-router-dom'; import 'bulma/css/bulma.css'; import '@fortawesome/fontawesome-free/css/all.css'; -import { App } from './App'; +import { Root } from './components/Root'; createRoot(document.getElementById('root') as HTMLDivElement) - .render( - - - , - ); + .render(); diff --git a/src/types/Person.ts b/src/types/Person.ts index 952873d1d..c25f0ad96 100644 --- a/src/types/Person.ts +++ b/src/types/Person.ts @@ -1,11 +1,11 @@ -export interface Person { - name: string, - sex: string, - born: number, - died: number, - fatherName: string | null, - motherName: string | null, - slug: string, - mother?: Person, - father?: Person, +export interface PersonType { + name: string; + sex: string; + born: number; + died: number; + fatherName: string | null; + motherName: string | null; + slug: string; + mother?: PersonType; + father?: PersonType; } diff --git a/src/types/serchENUM.ts b/src/types/serchENUM.ts new file mode 100644 index 000000000..a26a7ea1e --- /dev/null +++ b/src/types/serchENUM.ts @@ -0,0 +1,5 @@ +export const searchENUM = { + Sex: 'sex', + Centuries: 'centuries', + Query: 'query', +};