diff --git a/README.md b/README.md
index b8068748cc..2555d23fbb 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://aniashev.github.io/react_dynamic-list-of-todos/) and add it to the PR description.
diff --git a/package-lock.json b/package-lock.json
index 7c46c1846e..93d9f605fe 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2049,49 +2049,17 @@
}
},
"@mate-academy/eslint-config-react-typescript": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/@mate-academy/eslint-config-react-typescript/-/eslint-config-react-typescript-1.0.5.tgz",
- "integrity": "sha512-dpPrhYUdJZMh5jNSQHRpKx2at/nDLuqG2EmaqdveK2vvzW7jZqiiUE1wPVrG5uQENanRYU7gTVzSTL4r/P+ifA==",
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/@mate-academy/eslint-config-react-typescript/-/eslint-config-react-typescript-1.0.12.tgz",
+ "integrity": "sha512-IgvWzeZ5FYk00BB4bdN8CzvInohyzlLfYNy0qAA2NxjdbgwkEPKiY7XE8e8lsT7Y3U1EvelOn8uoB8z63XRdgA==",
"dev": true,
"requires": {
- "@typescript-eslint/eslint-plugin": "^4.29.0",
"@typescript-eslint/parser": "^4.29.0",
"eslint-config-airbnb-typescript": "^12.3.1",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsx-a11y": "^6.4.1",
- "eslint-plugin-react": "^6.10.3",
+ "eslint-plugin-react": "^7.27.1",
"eslint-plugin-react-hooks": "^4.2.0"
- },
- "dependencies": {
- "doctrine": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
- "integrity": "sha512-lsGyRuYr4/PIB0txi+Fy2xOMI2dGaTguCaotzFGkVZuKR5usKfcRWIFKNM3QNrU7hh/+w2bwTW+ZeXPK5l8uVg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "isarray": "^1.0.0"
- }
- },
- "eslint-plugin-react": {
- "version": "6.10.3",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz",
- "integrity": "sha512-vFfMSxJynKlgOhIVjhlZyibVUg442Aiv3482XPkgdYV90T8nD2QvxGXILZGwZHYMQ/l+A/De14O9D0qjDelSrg==",
- "dev": true,
- "requires": {
- "array.prototype.find": "^2.0.1",
- "doctrine": "^1.2.2",
- "has": "^1.0.1",
- "jsx-ast-utils": "^1.3.4",
- "object.assign": "^4.0.4"
- }
- },
- "jsx-ast-utils": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz",
- "integrity": "sha512-0LwSmMlQjjUdXsdlyYhEfBJCn2Chm0zgUBmfmf1++KUULh+JOdlzrZfiwe2zmlVJx44UF+KX/B/odBoeK9hxmw==",
- "dev": true
- }
}
},
"@mate-academy/scripts": {
@@ -3503,126 +3471,6 @@
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
},
- "array.prototype.find": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.2.0.tgz",
- "integrity": "sha512-sn40qmUiLYAcRb/1HsIQjTTZ1kCy8II8VtZJpMn2Aoen9twULhbWXisfh3HimGqMlHGUul0/TfKCnXg42LuPpQ==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.3",
- "es-abstract": "^1.19.4",
- "es-shim-unscopables": "^1.0.0"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.20.1",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz",
- "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "function.prototype.name": "^1.1.5",
- "get-intrinsic": "^1.1.1",
- "get-symbol-description": "^1.0.0",
- "has": "^1.0.3",
- "has-property-descriptors": "^1.0.0",
- "has-symbols": "^1.0.3",
- "internal-slot": "^1.0.3",
- "is-callable": "^1.2.4",
- "is-negative-zero": "^2.0.2",
- "is-regex": "^1.1.4",
- "is-shared-array-buffer": "^1.0.2",
- "is-string": "^1.0.7",
- "is-weakref": "^1.0.2",
- "object-inspect": "^1.12.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.2",
- "regexp.prototype.flags": "^1.4.3",
- "string.prototype.trimend": "^1.0.5",
- "string.prototype.trimstart": "^1.0.5",
- "unbox-primitive": "^1.0.2"
- }
- },
- "has-bigints": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
- "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
- "dev": true
- },
- "regexp.prototype.flags": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
- "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.3",
- "functions-have-names": "^1.2.2"
- }
- },
- "string.prototype.trimend": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz",
- "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.19.5"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
- "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
- "dev": true,
- "requires": {
- "has-property-descriptors": "^1.0.0",
- "object-keys": "^1.1.1"
- }
- }
- }
- },
- "string.prototype.trimstart": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz",
- "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.19.5"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
- "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
- "dev": true,
- "requires": {
- "has-property-descriptors": "^1.0.0",
- "object-keys": "^1.1.1"
- }
- }
- }
- },
- "unbox-primitive": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
- "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "has-bigints": "^1.0.2",
- "has-symbols": "^1.0.3",
- "which-boxed-primitive": "^1.0.2"
- }
- }
- }
- },
"array.prototype.flat": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz",
@@ -6842,15 +6690,6 @@
"unbox-primitive": "^1.0.1"
}
},
- "es-shim-unscopables": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
- "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
"es-to-primitive": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
@@ -9147,29 +8986,11 @@
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
- "function.prototype.name": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
- "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.3",
- "es-abstract": "^1.19.0",
- "functions-have-names": "^1.2.2"
- }
- },
"functional-red-black-tree": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc="
},
- "functions-have-names": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
- "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
- "dev": true
- },
"gauge": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
@@ -9631,15 +9452,6 @@
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
- "has-property-descriptors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
- "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
- "dev": true,
- "requires": {
- "get-intrinsic": "^1.1.1"
- }
- },
"has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
diff --git a/src/App.tsx b/src/App.tsx
index d46111e825..d2993a9548 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,5 +1,5 @@
/* eslint-disable max-len */
-import React from 'react';
+import React, { useEffect, useMemo, useState } from 'react';
import 'bulma/css/bulma.css';
import '@fortawesome/fontawesome-free/css/all.css';
@@ -7,8 +7,52 @@ 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 { getTodos } from './api';
+import { Status } from './types/Status';
export const App: React.FC = () => {
+ const [todos, setTodos] = useState([]);
+ const [selectedTodo, setSelectedTodo] = useState(null);
+ const [query, setQuery] = useState('');
+ const [filter, setFilter] = useState(Status.all);
+ const [loading, setLoading] = useState(false);
+
+ useEffect(() => {
+ setLoading(true);
+ getTodos()
+ .then(setTodos)
+ .finally(() => setLoading(false));
+ }, []);
+
+ const handleCloseModal = () => {
+ setSelectedTodo(null);
+ };
+
+ const filterTodosByStatus = (statusTodos: Todo[], status: Status) => {
+ switch (status) {
+ case Status.all:
+ return statusTodos;
+ case Status.completed:
+ return statusTodos.filter(todo => todo.completed);
+ case Status.active:
+ return statusTodos.filter(todo => !todo.completed);
+ default:
+ return statusTodos;
+ }
+ };
+
+ const filterTodosByQuery = (queryTodos: Todo[], givenQuery: string) => {
+ return queryTodos.filter(todo => todo.title.toLowerCase().includes(givenQuery.toLowerCase()));
+ };
+
+ const displayedTodos = useMemo(() => {
+ const todosByStatus = filterTodosByStatus(todos, filter);
+ const todosByQuery = filterTodosByQuery(todosByStatus, query);
+
+ return todosByQuery;
+ }, [todos, filter, query]);
+
return (
<>
@@ -17,18 +61,19 @@ export const App: React.FC = () => {
Todos:
-
+
-
-
-
+ {loading && }
+ {!loading && todos.length > 0 && (
+
+ )}
-
-
+ { selectedTodo
+ && }
>
);
};
diff --git a/src/components/TodoFilter/TodoFilter.tsx b/src/components/TodoFilter/TodoFilter.tsx
index c5ea5a5015..9282141ee2 100644
--- a/src/components/TodoFilter/TodoFilter.tsx
+++ b/src/components/TodoFilter/TodoFilter.tsx
@@ -1,34 +1,68 @@
-export const TodoFilter = () => (
-
-);
+
+
+
+ {query && (
+
+ {/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
+
+
+ )}
+
+
+ );
+};
diff --git a/src/components/TodoList/TodoList.tsx b/src/components/TodoList/TodoList.tsx
index 84dbcf3c0e..903f30c7f8 100644
--- a/src/components/TodoList/TodoList.tsx
+++ b/src/components/TodoList/TodoList.tsx
@@ -1,100 +1,74 @@
import React from 'react';
+import { Todo } from '../../types/Todo';
-export const TodoList: React.FC = () => (
-
-
-
- # |
-
-
-
-
- |
- Title |
- |
-
-
+type Props = {
+ todo: Todo[];
+ selectedTodo: Todo | null;
+ setSelectedTodo: (value: Todo) => void;
+};
-
-
- 1 |
- |
-
- delectus aut autem
- |
-
- |
+
+
+ {todo.map(t => (
+
+ {t.id} |
+ {t.completed ? (
+
+
+
+
+ |
+ ) : (
+ |
+ )}
+
+
+ {t.title}
+
+ |
+
+ setSelectedTodo(t)}
+ >
+
+
+
+
+ |
+
+ ))}
+
+
+ );
+};
diff --git a/src/components/TodoModal/TodoModal.tsx b/src/components/TodoModal/TodoModal.tsx
index a1166885ca..06db0bbaf9 100644
--- a/src/components/TodoModal/TodoModal.tsx
+++ b/src/components/TodoModal/TodoModal.tsx
@@ -1,12 +1,30 @@
-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';
+
+type Props = {
+ selectedTodo: Todo,
+ handleCloseModal: () => void;
+};
+
+export const TodoModal: React.FC = ({
+ selectedTodo,
+ handleCloseModal,
+}) => {
+ const [user, setUser] = useState();
+
+ useEffect(() => {
+ getUser(selectedTodo.userId)
+ .then(setUser);
+ }, []);
-export const TodoModal: React.FC = () => {
return (
- {true ? (
+ {!user ? (
) : (
@@ -15,7 +33,7 @@ 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 +41,30 @@ export const TodoModal: React.FC = () => {
type="button"
className="delete"
data-cy="modal-close"
+ onClick={handleCloseModal}
/>
- quis ut nam facilis et officia qui
+ {selectedTodo.title}
{/* Done */}
- Planned
+
+ {selectedTodo.completed
+ ? 'Done'
+ : 'Planned'}
+
{' by '}
-
- Leanne Graham
+
+ {user.name}
diff --git a/src/types/Status.ts b/src/types/Status.ts
new file mode 100644
index 0000000000..07d5c83567
--- /dev/null
+++ b/src/types/Status.ts
@@ -0,0 +1,5 @@
+export enum Status {
+ all = 'all',
+ completed = 'completed',
+ active = 'active',
+}