diff --git a/README.md b/README.md index b8068748cc..7ab23b48ac 100644 --- a/README.md +++ b/README.md @@ -29,4 +29,4 @@ loaded and show them using `TodoList` (check the code in the `api.ts`); - 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_dynamic-list-of-todos/) and add it to the PR description. +- Replace `` with your Github username in the [DEMO LINK](https://dpidlutska.github.io/react_dynamic-list-of-todos/) and add it to the PR description. diff --git a/src/App.tsx b/src/App.tsx index d46111e825..ebc2eea82f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ /* eslint-disable max-len */ -import React from 'react'; +import React, { useState, useEffect, useMemo } from 'react'; import 'bulma/css/bulma.css'; import '@fortawesome/fontawesome-free/css/all.css'; @@ -7,8 +7,34 @@ import { TodoList } from './components/TodoList'; import { TodoFilter } from './components/TodoFilter'; import { TodoModal } from './components/TodoModal'; import { Loader } from './components/Loader'; +import { Todo } from './types/Todo'; +import { TodosFilter } from './types/TodosFilter'; +import { getTodos } from './api'; +import { getPreparedTodos } from './services/todos'; export const App: React.FC = () => { + const [todos, setTodos] = useState([]); + const [query, setQuery] = useState(''); + const [filter, setFilter] = useState(TodosFilter.All); + const [isLoading, setIsLoading] = useState(false); + const [selectedTodo, setSelectedTodo] = useState(null); + + useEffect(() => { + setIsLoading(true); + + getTodos() + .then(setTodos) + .catch(error => { + // eslint-disable-next-line no-console + console.log(error); + }) + .finally(() => setIsLoading(false)); + }, []); + + const filteredTodos = useMemo(() => { + return getPreparedTodos(todos, query, filter); + }, [todos, query, filter]); + return ( <>
@@ -17,18 +43,37 @@ export const App: React.FC = () => {

Todos:

- +
- - + {isLoading + ? ( + + ) : ( + + )} +
- + {selectedTodo && ( + + )} ); }; diff --git a/src/components/TodoFilter/TodoFilter.tsx b/src/components/TodoFilter/TodoFilter.tsx index c5ea5a5015..40d8c47067 100644 --- a/src/components/TodoFilter/TodoFilter.tsx +++ b/src/components/TodoFilter/TodoFilter.tsx @@ -1,34 +1,73 @@ -export const TodoFilter = () => ( -
-

- - - -

+import React from 'react'; +import { TodosFilter } from '../../types/TodosFilter'; -

- - - - +type Props = { + query: string; + filter: TodosFilter; + onQueryChange: (value: string) => void; + onFilterChange: (value: TodosFilter) => void; +}; - - {/* eslint-disable-next-line jsx-a11y/control-has-associated-label */} - + + + ); +}; diff --git a/src/components/TodoItem/index.ts b/src/components/TodoItem/index.ts new file mode 100644 index 0000000000..21f4abac39 --- /dev/null +++ b/src/components/TodoItem/index.ts @@ -0,0 +1 @@ +export * from './TodoItem'; diff --git a/src/components/TodoList/TodoList.tsx b/src/components/TodoList/TodoList.tsx index 84dbcf3c0e..1b3649b145 100644 --- a/src/components/TodoList/TodoList.tsx +++ b/src/components/TodoList/TodoList.tsx @@ -1,6 +1,18 @@ import React from 'react'; +import { TodoItem } from '../TodoItem'; +import { Todo } from '../../types/Todo'; -export const TodoList: React.FC = () => ( +type Props = { + todos: Todo[]; + selectedTodo: Todo | null; + onSelectTodo: (todo: Todo) => void; +}; + +export const TodoList: React.FC = ({ + todos, + selectedTodo, + onSelectTodo, +}) => ( @@ -16,85 +28,14 @@ export const TodoList: React.FC = () => ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + {todos.map(todo => ( + + ))}
1 - -

delectus aut autem

-
- -
2 - -

quis ut nam facilis et officia qui

-
- -
1 - -

delectus aut autem

-
- -
6 - -

- qui ullam ratione quibusdam voluptatem quia omnis -

-
- -
8 - - - - -

quo adipisci enim quam ut ab

-
- -
); diff --git a/src/components/TodoModal/TodoModal.tsx b/src/components/TodoModal/TodoModal.tsx index a1166885ca..cb75863e62 100644 --- a/src/components/TodoModal/TodoModal.tsx +++ b/src/components/TodoModal/TodoModal.tsx @@ -1,12 +1,37 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import { Loader } from '../Loader'; +import { Todo } from '../../types/Todo'; +import { User } from '../../types/User'; +import { getUser } from '../../api'; + +type Props = { + selectedTodo: Todo | null; + onSelectTodo: (todo: Todo | null) => void; +}; + +export const TodoModal: React.FC = ({ selectedTodo, onSelectTodo }) => { + const [selectedUser, setSelectedUser] = useState(null); + const [loading, setLoading] = useState(false); + + useEffect(() => { + setLoading(true); + + if (selectedTodo) { + getUser(selectedTodo.userId) + .then(setSelectedUser) + .catch(error => { + // eslint-disable-next-line no-console + console.log(error); + }) + .finally(() => setLoading(false)); + } + }, []); -export const TodoModal: React.FC = () => { return (

- {true ? ( + {loading ? ( ) : (
@@ -15,30 +40,34 @@ export const TodoModal: React.FC = () => { className="modal-card-title has-text-weight-medium" data-cy="modal-header" > - Todo #2 + {`Todo #${selectedTodo?.id}`}
- {/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}