From 127c319e60c81f553e8552c41398f93f5425523b Mon Sep 17 00:00:00 2001 From: Alex Pakhomov Date: Wed, 25 Sep 2024 20:18:40 +0200 Subject: [PATCH 1/3] add task solution --- src/App.tsx | 46 ++-- src/app/store.ts | 15 +- src/components/TodoFilter/TodoFilter.tsx | 41 ++- src/components/TodoList/TodoList.tsx | 305 ++++++++--------------- src/components/TodoModal/TodoModal.tsx | 85 ++++--- src/features/currentTodo.ts | 14 +- src/features/filter.ts | 13 +- src/features/todos.ts | 18 +- src/utils/hooks.tsx | 5 + 9 files changed, 277 insertions(+), 265 deletions(-) create mode 100644 src/utils/hooks.tsx diff --git a/src/App.tsx b/src/App.tsx index e10753461..1ee703b61 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,26 +1,40 @@ import 'bulma/css/bulma.css'; import '@fortawesome/fontawesome-free/css/all.css'; import { Loader, TodoFilter, TodoList, TodoModal } from './components'; +import { useEffect, useState } from 'react'; +import { getTodos } from './api'; +import { useAppDispatch, useAppSelector } from './utils/hooks'; +import { setTodos } from './features/todos'; -export const App = () => ( - <> -
-
-
-

Todos:

+export const App = () => { + const [isLoading, setIsLoading] = useState(true); + const currentTodo = useAppSelector(state => state.currentTodo.currentTodo); + const dispatch = useAppDispatch(); -
- -
+ useEffect(() => { + getTodos().then(todosFromServer => { + setIsLoading(false); + dispatch(setTodos(todosFromServer)); + }); + }, [dispatch]); + + return ( + <> +
+
+
+

Todos:

+ +
+ +
-
- - +
{isLoading ? : }
-
- - -); + {currentTodo && } + + ); +}; diff --git a/src/app/store.ts b/src/app/store.ts index 3a9b384be..b865b6bfa 100644 --- a/src/app/store.ts +++ b/src/app/store.ts @@ -1,10 +1,15 @@ -import { combineSlices, configureStore } from '@reduxjs/toolkit'; - -const rootReducer = combineSlices(); +import { configureStore } from '@reduxjs/toolkit'; +import currentTodo from '../features/currentTodo'; +import filterSlice from '../features/filter'; +import todosSlice from '../features/todos'; export const store = configureStore({ - reducer: rootReducer, + reducer: { + currentTodo: currentTodo, + filter: filterSlice, + todos: todosSlice, + }, }); -export type RootState = ReturnType; +export type RootState = ReturnType; export type AppDispatch = typeof store.dispatch; diff --git a/src/components/TodoFilter/TodoFilter.tsx b/src/components/TodoFilter/TodoFilter.tsx index c1b574ee7..84f8f9ef1 100644 --- a/src/components/TodoFilter/TodoFilter.tsx +++ b/src/components/TodoFilter/TodoFilter.tsx @@ -1,6 +1,20 @@ import React from 'react'; +import { useAppDispatch, useAppSelector } from '../../utils/hooks'; +import { queryFilter, statusFilter } from '../../features/filter'; export const TodoFilter: React.FC = () => { + const dispatch = useAppDispatch(); + const status = useAppSelector(state => state.filter.status); + const query = useAppSelector(state => state.filter.query); + + const handleCompleted = (event: React.ChangeEvent) => { + dispatch(statusFilter(event.target.value)); + }; + + const handleQuery = (event: React.ChangeEvent) => { + dispatch(queryFilter(event.target.value)); + }; + return (
{ >

- @@ -22,19 +40,24 @@ export const TodoFilter: React.FC = () => { type="text" className="input" placeholder="Search..." + value={query} + onChange={handleQuery} /> - - {/* eslint-disable-next-line jsx-a11y/control-has-associated-label */} -

+

+ {currentTodo.completed ? ( + Done + ) : ( + Planned + )} + {' by '} + {user.name} +

+
+
+ ) + )} ); }; diff --git a/src/features/currentTodo.ts b/src/features/currentTodo.ts index 9e69e2240..a0b44dc20 100644 --- a/src/features/currentTodo.ts +++ b/src/features/currentTodo.ts @@ -1,10 +1,20 @@ +/* eslint-disable no-param-reassign */ import { createSlice } from '@reduxjs/toolkit'; import { Todo } from '../types/Todo'; -const initialState = null as Todo | null; +const initialState = { + currentTodo: null as Todo | null, +}; export const currentTodoSlice = createSlice({ name: 'currentTodo', initialState, - reducers: {}, + reducers: { + setTodo: (state, action) => { + state.currentTodo = action.payload; + }, + }, }); + +export const { setTodo } = currentTodoSlice.actions; +export default currentTodoSlice.reducer; diff --git a/src/features/filter.ts b/src/features/filter.ts index d0eaf4640..d302e774c 100644 --- a/src/features/filter.ts +++ b/src/features/filter.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-param-reassign */ import { createSlice } from '@reduxjs/toolkit'; const initialState = { @@ -8,5 +9,15 @@ const initialState = { export const filterSlice = createSlice({ name: 'filter', initialState, - reducers: {}, + reducers: { + queryFilter: (state, action) => { + state.query = action.payload; + }, + statusFilter: (state, action) => { + state.status = action.payload; + }, + }, }); + +export const { queryFilter, statusFilter } = filterSlice.actions; +export default filterSlice.reducer; diff --git a/src/features/todos.ts b/src/features/todos.ts index 4555dea8d..8f0e07c24 100644 --- a/src/features/todos.ts +++ b/src/features/todos.ts @@ -1,8 +1,22 @@ +/* eslint-disable no-param-reassign */ import { createSlice } from '@reduxjs/toolkit'; import { Todo } from '../types/Todo'; +import { RootState } from '../app/store'; + +const initialState = { + todos: [] as Todo[], +}; export const todosSlice = createSlice({ name: 'todos', - initialState: [] as Todo[], - reducers: {}, + initialState: initialState, + reducers: { + setTodos: (state, action) => { + state.todos = action.payload; + }, + }, }); + +export const { setTodos } = todosSlice.actions; +export default todosSlice.reducer; +export const selectTodos = (state: RootState) => state.todos.todos; diff --git a/src/utils/hooks.tsx b/src/utils/hooks.tsx new file mode 100644 index 000000000..96835f4af --- /dev/null +++ b/src/utils/hooks.tsx @@ -0,0 +1,5 @@ +import { useDispatch, useSelector } from 'react-redux'; +import { AppDispatch, RootState } from '../app/store'; + +export const useAppDispatch = useDispatch.withTypes(); +export const useAppSelector = useSelector.withTypes(); From 0f155b97b1f3f30a46eeebb59b1827bc3fbcfff4 Mon Sep 17 00:00:00 2001 From: Alex Pakhomov Date: Wed, 25 Sep 2024 20:20:17 +0200 Subject: [PATCH 2/3] add readme file --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8aca9f189..f839f7e2e 100644 --- a/README.md +++ b/README.md @@ -17,5 +17,5 @@ components (no need to write them in the store file) ## Instructions - Install Prettier Extention and use this [VSCode settings](https://mate-academy.github.io/fe-program/tools/vscode/settings.json) to enable format on save. -- Replace `` with your Github username in the [DEMO LINK](https://.github.io/react_redux-list-of-todos/) +- Replace `` with your Github username in the [DEMO LINK](https://pakhomovalex.github.io/react_redux-list-of-todos/) - Follow the [React task guideline](https://github.com/mate-academy/react_task-guideline#react-tasks-guideline) From 08f4e33685d927ffd1138dcb3be58f7af95e0989 Mon Sep 17 00:00:00 2001 From: Alex Pakhomov Date: Wed, 25 Sep 2024 20:21:44 +0200 Subject: [PATCH 3/3] remove a comment --- src/components/TodoList/TodoList.tsx | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/components/TodoList/TodoList.tsx b/src/components/TodoList/TodoList.tsx index 52948cb2a..3cb130071 100644 --- a/src/components/TodoList/TodoList.tsx +++ b/src/components/TodoList/TodoList.tsx @@ -103,22 +103,6 @@ export const TodoList = () => { ); })} - {/* - 3 - - - -

fugiat veniam minus

- - - - - - */} )}