From a603430b6668e967d14b69c08038c60e534480e7 Mon Sep 17 00:00:00 2001 From: valentyn19939 Date: Sun, 22 Sep 2024 16:17:27 +0200 Subject: [PATCH] add task solution --- src/App.tsx | 51 ++-- src/app/hooks.ts | 4 + src/app/store.ts | 5 +- src/components/TodoFilter/TodoFilter.tsx | 50 +++- src/components/TodoList/TodoList.tsx | 329 ++++++++--------------- src/components/TodoModal/TodoModal.tsx | 87 +++--- src/features/currentTodo.ts | 9 +- src/features/filter.ts | 26 +- src/features/todos.ts | 10 +- src/types/Status.ts | 6 +- 10 files changed, 281 insertions(+), 296 deletions(-) create mode 100644 src/app/hooks.ts diff --git a/src/App.tsx b/src/App.tsx index e10753461..8c04f8bba 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,26 +1,43 @@ 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 { useDispatch } from 'react-redux'; +import { Todo } from './types/Todo'; +import { todosSlice } from './features/todos'; +import { getTodos } from './api'; +import { useAppSelector } from './app/hooks'; -export const App = () => ( - <> -
-
-
-

Todos:

+export const App = () => { + const [loading, setLoading] = useState(false); + const currentTodo = useAppSelector(state => state.currentTodo); + const dispatch = useDispatch(); -
- -
+ useEffect(() => { + setLoading(true); + getTodos() + .then((data: Todo[]) => dispatch(todosSlice.actions.setTodos(data))) + .finally(() => setLoading(false)); + }, [dispatch]); + + return ( + <> +
+
+
+

Todos:

-
- - +
+ +
+
+ {loading && } + {!loading && } +
-
- - - -); + {currentTodo && } + + ); +}; diff --git a/src/app/hooks.ts b/src/app/hooks.ts new file mode 100644 index 000000000..99763399a --- /dev/null +++ b/src/app/hooks.ts @@ -0,0 +1,4 @@ +import { TypedUseSelectorHook, useSelector } from 'react-redux'; +import { RootState } from './store'; + +export const useAppSelector: TypedUseSelectorHook = useSelector; diff --git a/src/app/store.ts b/src/app/store.ts index 3a9b384be..397dee4aa 100644 --- a/src/app/store.ts +++ b/src/app/store.ts @@ -1,6 +1,9 @@ import { combineSlices, configureStore } from '@reduxjs/toolkit'; +import { todosSlice } from '../features/todos'; +import { currentTodoSlice } from '../features/currentTodo'; +import { filterSlice } from '../features/filter'; -const rootReducer = combineSlices(); +const rootReducer = combineSlices(todosSlice, currentTodoSlice, filterSlice); export const store = configureStore({ reducer: rootReducer, diff --git a/src/components/TodoFilter/TodoFilter.tsx b/src/components/TodoFilter/TodoFilter.tsx index c1b574ee7..3f9dedb59 100644 --- a/src/components/TodoFilter/TodoFilter.tsx +++ b/src/components/TodoFilter/TodoFilter.tsx @@ -1,6 +1,22 @@ import React from 'react'; +import { useAppSelector } from '../../app/hooks'; +import { useDispatch } from 'react-redux'; +import { filterSlice } from '../../features/filter'; +import { Status } from '../../types/Status'; export const TodoFilter: React.FC = () => { + const filter = useAppSelector(state => state.filter); + const query = filter.query; + const dispatch = useDispatch(); + + const handleSelect = (event: React.ChangeEvent) => { + dispatch(filterSlice.actions.setStatus(event.target.value)); + }; + + const ChangeQuery = (event: React.ChangeEvent) => { + dispatch(filterSlice.actions.setQuery(event.target.value)); + }; + return (
{ >

- + + +

@@ -19,22 +39,26 @@ export const TodoFilter: React.FC = () => {

- - - {/* eslint-disable-next-line jsx-a11y/control-has-associated-label */} -

-
- ); + )} +
+); }; diff --git a/src/features/currentTodo.ts b/src/features/currentTodo.ts index 9e69e2240..05598ffbc 100644 --- a/src/features/currentTodo.ts +++ b/src/features/currentTodo.ts @@ -1,10 +1,13 @@ -import { createSlice } from '@reduxjs/toolkit'; +import { PayloadAction, Slice, createSlice } from '@reduxjs/toolkit'; import { Todo } from '../types/Todo'; const initialState = null as Todo | null; -export const currentTodoSlice = createSlice({ +export const currentTodoSlice: Slice = createSlice({ name: 'currentTodo', initialState, - reducers: {}, + reducers: { + selectTodo: (_, { payload }: PayloadAction) => payload, + closeTodo: () => null, + }, }); diff --git a/src/features/filter.ts b/src/features/filter.ts index d0eaf4640..ff84b598a 100644 --- a/src/features/filter.ts +++ b/src/features/filter.ts @@ -1,12 +1,26 @@ -import { createSlice } from '@reduxjs/toolkit'; +import { PayloadAction, Slice, createSlice } from '@reduxjs/toolkit'; +import { Status } from '../types/Status'; -const initialState = { - query: '', - status: 'all', +type FilterState = { + query: string; + status: Status; }; -export const filterSlice = createSlice({ +const initialState: FilterState = { + query: '', + status: Status.All, +}; +export const filterSlice: Slice = createSlice({ name: 'filter', initialState, - reducers: {}, + reducers: { + setStatus: (state, { payload }: PayloadAction) => ({ + ...state, + status: payload, + }), + setQuery: (state, { payload }: PayloadAction) => ({ + ...state, + query: payload, + }), + }, }); diff --git a/src/features/todos.ts b/src/features/todos.ts index 4555dea8d..ccd3c9d20 100644 --- a/src/features/todos.ts +++ b/src/features/todos.ts @@ -1,8 +1,12 @@ -import { createSlice } from '@reduxjs/toolkit'; +import { PayloadAction, Slice, createSlice } from '@reduxjs/toolkit'; import { Todo } from '../types/Todo'; -export const todosSlice = createSlice({ +export const todosSlice: Slice = createSlice({ name: 'todos', initialState: [] as Todo[], - reducers: {}, + reducers: { + setTodos(todos, { payload }: PayloadAction) { + todos.push(...payload); + }, + }, }); diff --git a/src/types/Status.ts b/src/types/Status.ts index 6eb05b9b2..2ec4b8714 100644 --- a/src/types/Status.ts +++ b/src/types/Status.ts @@ -1 +1,5 @@ -export type Status = 'all' | 'active' | 'completed'; +export enum Status { + All = 'all', + Active = 'active', + Completed = 'completed', +}