diff --git a/README.md b/README.md index b8068748cc..c3272d606f 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://DenysKolbasin.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..30135285fe 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,14 +1,42 @@ /* eslint-disable max-len */ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import 'bulma/css/bulma.css'; import '@fortawesome/fontawesome-free/css/all.css'; import { TodoList } from './components/TodoList'; import { TodoFilter } from './components/TodoFilter'; -import { TodoModal } from './components/TodoModal'; import { Loader } from './components/Loader'; +import { getTodos } from './api'; +import { Todo } from './types/Todo'; +import { TodoModal } from './components/TodoModal'; + +enum FilterStatus { + All = 'all', + Completed = 'completed', + Active = 'active', +} export const App: React.FC = () => { + const [todos, setTodos] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [selectedTodo, setSelectedTodo] = useState(null); + const [query, setQuery] = useState(''); + const [status, setStatus] = useState(FilterStatus.All); + + useEffect(() => { + setIsLoading(true); + getTodos() + .then(setTodos) + .finally(() => setIsLoading(false)); + }, []); + + const filteredTodos = todos.filter((todo) => ( + (status === FilterStatus.All + || (status === FilterStatus.Completed && todo.completed) + || (status === FilterStatus.Active && !todo.completed)) + && todo.title.toLowerCase().includes(query.toLowerCase()) + )); + return ( <>
@@ -17,18 +45,36 @@ export const App: React.FC = () => {

Todos:

- +
- - + {isLoading && } + + {!isLoading && filteredTodos.length > 0 && ( + + )}
- - + {selectedTodo && ( + setSelectedTodo(null)} + /> + )} ); }; diff --git a/src/api.ts b/src/api.ts index 11b6f7280e..4f1fe41370 100644 --- a/src/api.ts +++ b/src/api.ts @@ -4,8 +4,6 @@ import { User } from './types/User'; // eslint-disable-next-line max-len const BASE_URL = 'https://mate-academy.github.io/react_dynamic-list-of-todos/api'; -// This function creates a promise -// that is resolved after a given delay function wait(delay: number): Promise { return new Promise(resolve => { setTimeout(resolve, delay); @@ -16,7 +14,6 @@ function get(url: string): Promise { // eslint-disable-next-line prefer-template const fullURL = BASE_URL + url + '.json'; - // we add some delay to see how the loader works return wait(300) .then(() => fetch(fullURL)) .then(res => res.json()); diff --git a/src/components/TodoFilter/TodoFilter.tsx b/src/components/TodoFilter/TodoFilter.tsx index c5ea5a5015..179e9ffca8 100644 --- a/src/components/TodoFilter/TodoFilter.tsx +++ b/src/components/TodoFilter/TodoFilter.tsx @@ -1,34 +1,59 @@ -export const TodoFilter = () => ( -
-

- - - -

+import React from 'react'; -

- - - - +interface Props { + query: string; + setQuery: (query: string) => void; + status: string; + setStatus: (status: string) => 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..bc26c8188f 100644 --- a/src/components/TodoList/TodoList.tsx +++ b/src/components/TodoList/TodoList.tsx @@ -1,100 +1,42 @@ import React from 'react'; +import { Todo } from '../../types/Todo'; +import { TodoItem } from '../TodoItem'; -export const TodoList: React.FC = () => ( - - - - - - - - - +interface Props { + todos: Todo[]; + onSelect?: (todo: Todo) => void; + selectedTodo: Todo | null; +} - - - - - + {todos.map((todo) => ( + + ))} + +
# - - - - Title
1 - -

delectus aut autem

-
-
+ ); +}; diff --git a/src/components/TodoModal/TodoModal.tsx b/src/components/TodoModal/TodoModal.tsx index a1166885ca..3af07c991b 100644 --- a/src/components/TodoModal/TodoModal.tsx +++ b/src/components/TodoModal/TodoModal.tsx @@ -1,12 +1,35 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Loader } from '../Loader'; +import { getUser } from '../../api'; +import { User } from '../../types/User'; +import { Todo } from '../../types/Todo'; + +interface Props { + userId: number; + onClose: () => void; + selectedTodo: Todo | null; +} + +export const TodoModal: React.FC = ({ + userId, + onClose = () => { }, + selectedTodo, +}) => { + const [isLoading, setIsLoading] = useState(false); + const [user, setUser] = useState(null); + + useEffect(() => { + setIsLoading(true); + getUser(userId) + .then(setUser) + .finally(() => setIsLoading(false)); + }, [userId]); -export const TodoModal: React.FC = () => { return (

- {true ? ( + {isLoading ? ( ) : (
@@ -15,7 +38,8 @@ 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 */} @@ -23,22 +47,26 @@ export const TodoModal: React.FC = () => { type="button" className="delete" data-cy="modal-close" + onClick={onClose} />

- quis ut nam facilis et officia qui + {selectedTodo?.title}

- {/* Done */} - Planned + {selectedTodo?.completed ? ( + Done + ) : ( + Planned + )} {' by '} - - Leanne Graham + + {user?.name}