From 29bd04b495a1ccac3af4795ec29d1cf352cd24b1 Mon Sep 17 00:00:00 2001 From: naveed-shoukat Date: Sun, 20 Nov 2022 21:13:30 +0300 Subject: [PATCH 1/2] Basic Task solution implemented --- README.md | 6 +- src/App.tsx | 42 ++-- src/AppContext.tsx | 76 ++++++++ src/api.ts | 15 ++ .../CenturyFilterLink/CenturyFilterLink.tsx | 35 ++++ src/components/CenturyFilterLink/index.ts | 1 + .../FilteringForm/FilteringForm.tsx | 182 ++++++++++++++++++ src/components/FilteringForm/index.ts | 1 + src/components/Navigation/Navigation.tsx | 34 ++++ src/components/Navigation/index.ts | 1 + src/components/PeoplePage/PeoplePage.tsx | 125 ++++++++++++ src/components/PeoplePage/index.ts | 1 + src/components/PeopleTable/PeopleTable.tsx | 132 +++++++++++++ src/components/PeopleTable/index.ts | 1 + .../PeopleTableHeader/PeopleTableHeader.tsx | 61 ++++++ src/components/PeopleTableHeader/index.ts | 1 + .../PeopleTableHeaderRow.tsx | 18 ++ src/components/PeopleTableHeaderRow/index.ts | 1 + src/components/PersonName/PersonName.tsx | 28 +++ src/components/PersonName/index.ts | 1 + src/components/PersonRow/PersonRow.tsx | 47 +++++ src/components/PersonRow/index.ts | 1 + src/index.tsx | 69 ++++++- src/types/Person.ts | 2 + src/types/TableHeaderField.ts | 8 + 25 files changed, 855 insertions(+), 34 deletions(-) create mode 100644 src/AppContext.tsx create mode 100644 src/api.ts create mode 100644 src/components/CenturyFilterLink/CenturyFilterLink.tsx create mode 100644 src/components/CenturyFilterLink/index.ts create mode 100644 src/components/FilteringForm/FilteringForm.tsx create mode 100644 src/components/FilteringForm/index.ts create mode 100644 src/components/Navigation/Navigation.tsx create mode 100644 src/components/Navigation/index.ts create mode 100644 src/components/PeoplePage/PeoplePage.tsx create mode 100644 src/components/PeoplePage/index.ts create mode 100644 src/components/PeopleTable/PeopleTable.tsx create mode 100644 src/components/PeopleTable/index.ts create mode 100644 src/components/PeopleTableHeader/PeopleTableHeader.tsx create mode 100644 src/components/PeopleTableHeader/index.ts create mode 100644 src/components/PeopleTableHeaderRow/PeopleTableHeaderRow.tsx create mode 100644 src/components/PeopleTableHeaderRow/index.ts create mode 100644 src/components/PersonName/PersonName.tsx create mode 100644 src/components/PersonName/index.ts create mode 100644 src/components/PersonRow/PersonRow.tsx create mode 100644 src/components/PersonRow/index.ts create mode 100644 src/types/TableHeaderField.ts diff --git a/README.md b/README.md index f1183f65..a8d546d6 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # React - People table - Replace `` with your Github username in the - [DEMO LINK](https://.github.io/react_people-table/) + [DEMO LINK](https://naveed-shoukat.github.io/react_people-table/) - Follow the [React task guideline](https://github.com/mate-academy/react_task-guideline#react-tasks-guideline) ## If you don't use **Typescript** 1. Rename `.tsx` files to `.jsx` -1. use `eslint-config-react` in `.eslintrs.js` +1. use `eslint-config-react` in `.eslintrs.js` ## Basic tasks 1. Install all the NPM packages you need and types for them. @@ -91,3 +91,5 @@ - make `mother` and `father` field optional - update the list of `mothers` and `fathers` according to the entered `born` year (they must be alive) (selects should be empty and disabled before the born year was entered) + +Here is [the working example](https://mate-academy.github.io/react_people-table-advanced/) diff --git a/src/App.tsx b/src/App.tsx index f6361547..cd900119 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,39 +1,19 @@ import React from 'react'; +import { Outlet } from 'react-router-dom'; +import { Navigation } from './components/Navigation'; import '@fortawesome/fontawesome-free/css/all.css'; import 'bulma/css/bulma.css'; import './App.scss'; -import peopleFromServer from './people.json'; +export const App: React.FC = () => ( +
+ -export class App extends React.Component { - state = {}; - - render() { - return ( -
-

People table

- - - - - - - - - - - - {peopleFromServer.map(person => ( - - - - - - ))} - -
namesexborn
{person.name}{person.sex}{person.born}
+
+
+
- ); - } -} +
+
+); diff --git a/src/AppContext.tsx b/src/AppContext.tsx new file mode 100644 index 00000000..82efad04 --- /dev/null +++ b/src/AppContext.tsx @@ -0,0 +1,76 @@ +import React, { ReactNode, Reducer, useReducer } from 'react'; +import { Person } from './types/Person'; + +export enum ReducerActions { + setPeople = 'setPeople', + setIsPeopleLoading = 'setIsPeopleLoading', + setHasPeopleLoadingError = 'setHasPeopleLoadingError', +} + +type Props = { + children: ReactNode; +}; + +type DispatchContextType = (action: DispatchActions) => void; + +interface State { + people: Person[] | null; + isPeopleLoading: boolean; + hasPeopleLoadingError: boolean; +} +const initialState: State = { + people: null, + isPeopleLoading: false, + hasPeopleLoadingError: false, +}; + +interface DispatchActions { + type: ReducerActions, + // eslint-disable-next-line + payload: any, +} + +const reducerCallBack: Reducer = (state, action) => { + const { type, payload } = action; + + switch (type) { + case ReducerActions.setPeople: + return { + ...state, + people: payload, + }; + + case ReducerActions.setIsPeopleLoading: + return { + ...state, + isPeopleLoading: payload, + }; + + case ReducerActions.setHasPeopleLoadingError: + return { + ...state, + hasPeopleLoadingError: payload, + }; + + default: + return state; + } +}; + +export const DispatchContext = React.createContext< +DispatchContextType +>(() => {}); + +export const StateContext = React.createContext(initialState); + +export const StateProvider: React.FC = ({ children }) => { + const [state, dispatch] = useReducer(reducerCallBack, initialState); + + return ( + + + {children} + + + ); +}; diff --git a/src/api.ts b/src/api.ts new file mode 100644 index 00000000..03bd2e42 --- /dev/null +++ b/src/api.ts @@ -0,0 +1,15 @@ +import { Person } from './types/Person'; + +// eslint-disable-next-line max-len +const API_URL = 'https://mate-academy.github.io/react_people-table/api/people.json'; + +function wait(delay: number) { + return new Promise(resolve => setTimeout(resolve, delay)); +} + +export function getPeople(): Promise { + // keep this delay for testing purpose + return wait(500) + .then(() => fetch(API_URL)) + .then(response => response.json()); +} diff --git a/src/components/CenturyFilterLink/CenturyFilterLink.tsx b/src/components/CenturyFilterLink/CenturyFilterLink.tsx new file mode 100644 index 00000000..a232774c --- /dev/null +++ b/src/components/CenturyFilterLink/CenturyFilterLink.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import cn from 'classnames'; +import { Link, useLocation } from 'react-router-dom'; + +type Props = { + century: string, + handelCentury: (querytype: string, value: string) => void; +}; + +export const CenturyFilterLink: React.FC = (props) => { + const { century, handelCentury } = props; + const { search } = useLocation(); + const isActive = search.includes(`century=${century}`); + + return ( + { + event.preventDefault(); + handelCentury('century', century); + }} + > + {century} + + ); +}; diff --git a/src/components/CenturyFilterLink/index.ts b/src/components/CenturyFilterLink/index.ts new file mode 100644 index 00000000..5a0c1c12 --- /dev/null +++ b/src/components/CenturyFilterLink/index.ts @@ -0,0 +1 @@ +export * from './CenturyFilterLink'; diff --git a/src/components/FilteringForm/FilteringForm.tsx b/src/components/FilteringForm/FilteringForm.tsx new file mode 100644 index 00000000..4d3f6bcb --- /dev/null +++ b/src/components/FilteringForm/FilteringForm.tsx @@ -0,0 +1,182 @@ +import React from 'react'; +import { Link, useSearchParams } from 'react-router-dom'; +import cn from 'classnames'; +import { CenturyFilterLink } from '../CenturyFilterLink'; + +export const FilteringForm: React.FC = () => { + const [searchParams, setSearchParams] = useSearchParams(); + + const handelFilterSearchURL = (filterType: string, value: string) => { + if (filterType === 'sex' || filterType === 'query') { + const prevValue = searchParams.get(filterType); + + if (!prevValue) { + searchParams.append(filterType, value); + setSearchParams(searchParams); + + return; + } + + if (!value) { + searchParams.delete(filterType); + setSearchParams(searchParams); + + return; + } + + searchParams.set(filterType, value); + setSearchParams(searchParams); + } + + if (filterType === 'century') { + const prevValue = searchParams.getAll(filterType); + + if (prevValue.length === 0) { + searchParams.append(filterType, value); + setSearchParams(searchParams); + + return; + } + + const newCenturyValue = prevValue.includes(value) + ? prevValue.filter(item => item !== value) + : [...prevValue, value]; + + searchParams.delete(filterType); + + if (!newCenturyValue.length) { + setSearchParams(searchParams); + + return; + } + + newCenturyValue.forEach(val => searchParams.append(filterType, val)); + + setSearchParams(searchParams); + } + }; + + return ( +
+