Skip to content

Commit

Permalink
add task solution
Browse files Browse the repository at this point in the history
  • Loading branch information
AnnaViolin23 committed Nov 30, 2023
1 parent a7bec0e commit 1ffa8b4
Show file tree
Hide file tree
Showing 16 changed files with 379 additions and 742 deletions.
7 changes: 2 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { PeoplePage } from './components/PeoplePage';
import { Outlet } from 'react-router-dom';
import { Navbar } from './components/Navbar';

import './App.scss';

export const App = () => {
Expand All @@ -10,9 +9,7 @@ export const App = () => {

<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
35 changes: 35 additions & 0 deletions src/PeopleRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useParams } from 'react-router-dom';
import classNames from 'classnames';
import { PersonLink } from './components/PersonLink';
import { Person } from './types';

type Props = {
person: Person;
};

export const PeopleRow: React.FC<Props> = ({ person }) => {
const {
sex, born, died, fatherName, motherName, father, mother,
} = person;
const { slug } = useParams();

return (
<tr
data-cy="person"
className={classNames({
'has-background-warning': person.slug === slug,
})}
>

<td aria-label="person">
<PersonLink person={person} />
</td>

<td>{sex}</td>
<td>{born}</td>
<td>{died}</td>
<td>{mother ? (<PersonLink person={mother} />) : (motherName || '-')}</td>
<td>{father ? (<PersonLink person={father} />) : (fatherName || '-')}</td>
</tr>
);
};
29 changes: 29 additions & 0 deletions src/Root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
Navigate,
Route,
HashRouter as Router,
Routes,
} from 'react-router-dom';
import { HomePage } from './components/HomePage';
import { PeoplePage } from './components/PeoplePage';
import { NotFoundPage } from './components/NotFoundPage';
import { App } from './App';

export const Root = () => {
return (
<Router>
<Routes>
<Route>
<Route path="/" element={<App />}>
<Route index element={<HomePage />} />
<Route path="home" element={<Navigate to="/" replace />} />
<Route path="people">
<Route path=":slug?" element={<PeoplePage />} />
</Route>
<Route path="*" element={<NotFoundPage />} />
</Route>
</Route>
</Routes>
</Router>
);
};
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>
);
};
22 changes: 16 additions & 6 deletions src/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import classNames from 'classnames';
import { NavLink } from 'react-router-dom';

export const Navbar = () => {
const activeClassName = ({ isActive }: { isActive: boolean }) => {
return classNames('navbar-item', {
'has-background-grey-lighter': isActive,
});
};

return (
<nav
data-cy="nav"
Expand All @@ -8,15 +17,16 @@ export const Navbar = () => {
>
<div className="container">
<div className="navbar-brand">
<a className="navbar-item" href="#/">Home</a>
<NavLink className={activeClassName} to="/">
Home
</NavLink>

<a
aria-current="page"
className="navbar-item has-background-grey-lighter"
href="#/people"
<NavLink
className={activeClassName}
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: React.FC = () => {
return (
<h1 className="title">Page not found</h1>
);
};
107 changes: 58 additions & 49 deletions src/components/PeopleFilters.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,38 @@
import React from 'react';
import { Link, useSearchParams } from 'react-router-dom';
import classNames from 'classnames';
import { getSearchWith } from '../utils/searchHelper';
import { SexFilter } from '../types/SexFilter';
import { SearchLink } from './SearchLink';

const CENTURIES = ['16', '17', '18', '19', '20'];

export const PeopleFilters = () => {
const [searchParams, setSearchParams] = useSearchParams();
const sex = searchParams.get('sex') || '';
const query = searchParams.get('query') || '';
const centuries = searchParams.getAll('centuries') || [];

const handleSearh = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearchParams(getSearchWith(searchParams, {
query: event.target.value || null,
}));
};

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>
{Object.entries(SexFilter).map(([key, value]) => (
<SearchLink
key={key}
params={{ sex: value || null }}
className={classNames({ 'is-active': sex === value })}
>
{key}
</SearchLink>
))}
</p>

<div className="panel-block">
Expand All @@ -16,6 +42,8 @@ export const PeopleFilters = () => {
type="search"
className="input"
placeholder="Search"
value={query}
onChange={handleSearh}
/>

<span className="icon is-left">
Expand All @@ -27,66 +55,47 @@ export const PeopleFilters = () => {
<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>
{CENTURIES.map(century => (
<SearchLink
key={century}
params={{
centuries: centuries.includes(century)
? centuries.filter(age => century !== age)
: [...centuries, century],
}}
data-cy="century"
className={classNames(
'button mr-1', {
'is-info': centuries.includes(century),
},
)}
>
{century}
</SearchLink>
))}
</div>

<div className="level-right ml-4">
<a
<SearchLink
params={{ centuries: null }}
data-cy="centuryALL"
className="button is-success is-outlined"
href="#/people"
className={classNames('button is-success', {
'is-outline': centuries.length,
})}
>
All
</a>
</SearchLink>
</div>
</div>
</div>

<div className="panel-block">
<a
<Link
className="button is-link is-outlined is-fullwidth"
href="#/people"
to={{ search: '' }}
>
Reset all filters
</a>
</Link>
</div>
</nav>
);
Expand Down
60 changes: 49 additions & 11 deletions src/components/PeoplePage.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,70 @@
import { PeopleFilters } from './PeopleFilters';
import { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { Loader } from './Loader';
import { Person } from '../types';
import { getPeople } from '../api';
import { PeopleFilters } from './PeopleFilters';
import { getPreparedPeople } from '../helpers/getPreparedPeople';
import { preparePeople } from '../helpers/preparePeople';
import { PeopleTable } from './PeopleTable';

export const PeoplePage = () => {
export const PeoplePage: React.FC = () => {
const [people, setPeople] = useState<Person[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [error, setError] = useState<boolean>(false);

const [searchParams] = useSearchParams();
const sort = searchParams.get('sort') || '';
const order = searchParams.get('order') || '';
const sex = searchParams.get('sex') || '';
const query = searchParams.get('query') || '';
const centuries = searchParams.getAll('centuries') || [];

const visiblePeople = getPreparedPeople(people, {
sort, order, sex, query, centuries: centuries.join(','),
});

useEffect(() => {
setIsLoading(true);
setError(false);

getPeople()
.then(peopleFromServer => setPeople(preparePeople(peopleFromServer)))
.catch(() => setError(true))
.finally(() => setIsLoading(false));
}, []);

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

<div className="block">
<div className="columns is-desktop is-flex-direction-row-reverse">

{!isLoading && (
<div className="column is-7-tablet is-narrow-desktop">
<PeopleFilters />
</div>
)}

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

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

{!isLoading && !error && !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>

<PeopleTable />
</div>
{!isLoading && !error && !!people.length && (
<PeopleTable people={visiblePeople} />
)}
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 1ffa8b4

Please sign in to comment.