Skip to content

Commit

Permalink
table_advansed
Browse files Browse the repository at this point in the history
  • Loading branch information
MaksymKos committed Dec 5, 2023
1 parent a7bec0e commit 1386636
Show file tree
Hide file tree
Showing 13 changed files with 438 additions and 725 deletions.
10 changes: 4 additions & 6 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div data-cy="app">
<Navbar />
<NavBar />

<div className="section">
<div className="container">
<h1 className="title">Home Page</h1>
<h1 className="title">Page not found</h1>
<PeoplePage />
<Outlet />
</div>
</div>
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -7,9 +7,9 @@ function wait(delay: number) {
return new Promise(resolve => setTimeout(resolve, delay));
}

export async function getPeople(): Promise<Person[]> {
export async function getPeople(): Promise<PersonType[]> {
// keep this delay for testing purpose
return wait(500)
.then(() => fetch(API_URL))
.then(response => response.json());
.then((response) => response.json());
}
5 changes: 5 additions & 0 deletions src/components/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const HomePage = () => {
return (
<h1 className="title">Home Page</h1>
);
};
25 changes: 18 additions & 7 deletions src/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<nav
data-cy="nav"
Expand All @@ -8,15 +15,19 @@ export const Navbar = () => {
>
<div className="container">
<div className="navbar-brand">
<a className="navbar-item" href="#/">Home</a>
<NavLink
className={handleNavActive}
to="/"
>
Home
</NavLink>

<a
aria-current="page"
className="navbar-item has-background-grey-lighter"
href="#/people"
<NavLink
className={handleNavActive}
to="/people"
>
People
</a>
</NavLink>
</div>
</div>
</nav>
Expand Down
5 changes: 5 additions & 0 deletions src/components/NotFoundPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const NotFoundPage = () => {
return (
<h1 className="title">Page not found</h1>
);
};
118 changes: 69 additions & 49 deletions src/components/PeopleFilters.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,50 @@
import { useSearchParams } from 'react-router-dom';
import { ChangeEvent } from 'react';
import cn from 'classnames';
import { SearchLink } from './SearchLink';
import { getSearchWith } from '../utils/searchHelper';

export const PeopleFilters = () => {
const [searchParams, setSearchParams] = useSearchParams();
const centuriesList = ['16', '17', '18', '19', '20'];

const sex = searchParams.get('sex');
const centuries = searchParams.getAll('centuries');
const query = searchParams.get('query') || '';

function handleQuery(event: ChangeEvent<HTMLInputElement>) {
const params = (event.target.value === '')
? getSearchWith(searchParams, { query: null })
: getSearchWith(searchParams, { query: event.target.value });

setSearchParams(params);
}

return (
<nav className="panel">
<p className="panel-heading">Filters</p>

<p className="panel-tabs" data-cy="SexFilter">
<a className="is-active" href="#/people">All</a>
<a className="" href="#/people?sex=m">Male</a>
<a className="" href="#/people?sex=f">Female</a>
<SearchLink
params={{ sex: null }}
className={cn({ 'is-active': !sex })}
>
All
</SearchLink>

<SearchLink
params={{ sex: 'm' }}
className={cn({ 'is-active': sex === 'm' })}
>
Male
</SearchLink>

<SearchLink
params={{ sex: 'f' }}
className={cn({ 'is-active': sex === 'f' })}
>
Female
</SearchLink>
</p>

<div className="panel-block">
Expand All @@ -16,77 +54,59 @@ export const PeopleFilters = () => {
type="search"
className="input"
placeholder="Search"
value={query}
onChange={e => handleQuery(e)}
/>

<span className="icon is-left">
<i className="fas fa-search" aria-hidden="true" />
</span>
</p>
</div>

<div className="panel-block">
<div className="level is-flex-grow-1 is-mobile" data-cy="CenturyFilter">
<div className="level-left">
<a
data-cy="century"
className="button mr-1"
href="#/people?centuries=16"
>
16
</a>

<a
data-cy="century"
className="button mr-1 is-info"
href="#/people?centuries=17"
>
17
</a>

<a
data-cy="century"
className="button mr-1 is-info"
href="#/people?centuries=18"
>
18
</a>

<a
data-cy="century"
className="button mr-1 is-info"
href="#/people?centuries=19"
>
19
</a>

<a
data-cy="century"
className="button mr-1"
href="#/people?centuries=20"
>
20
</a>
{centuriesList.map(century => (
<SearchLink
key={century}
params={{
centuries: centuries.includes(century)
? centuries.filter(c => c !== century)
: [...centuries, century],
}}
className={cn('button mr-1', {
'is-info': centuries.includes(century),
})}
>
{century}
</SearchLink>
))}
</div>

<div className="level-right ml-4">
<a
<SearchLink
data-cy="centuryALL"
className="button is-success is-outlined"
href="#/people"
params={{ centuries: null }}
>
All
</a>
</SearchLink>
</div>
</div>
</div>

<div className="panel-block">
<a
<SearchLink
className="button is-link is-outlined is-fullwidth"
href="#/people"
params={{
sex: null,
query: null,
centuries: null,
}}
>
Reset all filters
</a>
</SearchLink>

</div>
</nav>
);
Expand Down
118 changes: 110 additions & 8 deletions src/components/PeoplePage.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,102 @@
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<PersonType[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const [searchParams] = useSearchParams();
const { slug } = useParams();

useEffect(() => {
setLoading(true);
getPeople()
.then(peopleOnServer)
.then(preparedPeople => setPeople(preparedPeople))
.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':
{
prepared.sort((a, b) => a.name.localeCompare(b.name));
break;
}

case 'sex':
{
prepared.sort((a, b) => a.sex.localeCompare(b.sex));
break;
}

case 'born': {
prepared.sort((a, b) => a.born - b.born);
break;
}

case 'died': {
prepared.sort((a, b) => a.died - b.died);
break;
}

default:
}
}

if (order) {
prepared.reverse();
}

return prepared;
};

return (
<>
<h1 className="title">People Page</h1>

<div className="block">
<div className="columns is-desktop is-flex-direction-row-reverse">
<div className="column is-7-tablet is-narrow-desktop">
Expand All @@ -15,17 +105,29 @@ export const PeoplePage = () => {

<div className="column">
<div className="box table-container">
<Loader />
{loading && (
<Loader />
)}

<p data-cy="peopleLoadingError">Something went wrong</p>
{error && (
<p data-cy="peopleLoadingError" className="has-text-danger">
Something went wrong
</p>
)}

<p data-cy="noPeopleMessage">
There are no people on the server
</p>
{!loading && !people.length && (
<p data-cy="noPeopleMessage">
There are no people on the server
</p>
)}

<p>There are no people matching the current search criteria</p>
{(!preparedPeople().length && !loading) && (
<p>There are no people matching the current search criteria</p>
)}

<PeopleTable />
{people.length > 0 && (
<PeopleTable people={preparedPeople()} Slug={slug} />
)}
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 1386636

Please sign in to comment.