Skip to content

Commit

Permalink
Solution
Browse files Browse the repository at this point in the history
  • Loading branch information
opalahecha committed Sep 23, 2024
1 parent 71932b1 commit 364c738
Show file tree
Hide file tree
Showing 10 changed files with 284 additions and 274 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ using the Redux. It should look and work identically, so use the same markup.
- implement `features/todos` storing an array of todos;
- load the todos in the `App` on page load (don't use Redux Thunk for now);
- `useAppSelector` already aware of `RootState` so you can write selectors in your
components (no need to write them in the store file)
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 `<your_account>` with your Github username in the [DEMO LINK](https://<your_account>.github.io/react_redux-list-of-todos/)
- Replace `<your_account>` with your Github username in the [DEMO LINK](https://opalahecha.github.io/react_redux-list-of-todos/)
- Follow the [React task guideline](https://github.com/mate-academy/react_task-guideline#react-tasks-guideline)
74 changes: 57 additions & 17 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,66 @@
import React, { useEffect, useState } from 'react';
import 'bulma/css/bulma.css';
import '@fortawesome/fontawesome-free/css/all.css';
import { Loader, TodoFilter, TodoList, TodoModal } from './components';
import { useAppDispatch, useAppSelector } from './app/hooks';
import { getTodos } from './api';
import { setTodos } from './features/todos';

export const App = () => (
<>
<div className="section">
<div className="container">
<div className="box">
<h1 className="title">Todos:</h1>
export const App: React.FC = () => {
const todos = useAppSelector(state => state.todos);
const filterStatus = useAppSelector(state => state.filter.status);
const query = useAppSelector(state => state.filter.query);
const currentTodo = useAppSelector(state => state.currentTodo);
const [isLoading, setIsLoading] = useState(false);
const dispatch = useAppDispatch();

<div className="block">
<TodoFilter />
</div>
useEffect(() => {
setIsLoading(true);
getTodos()
.then(getTodo => dispatch(setTodos(getTodo)))
.finally(() => setIsLoading(false));
}, [dispatch]);

const filteredTodos = todos
.filter(todo => {
if (filterStatus === 'active') {
return !todo.completed;
}

if (filterStatus === 'completed') {
return todo.completed;
}

return true;
})
.filter(todo => todo.title.toLowerCase().includes(query.toLowerCase()));

<div className="block">
<Loader />
<TodoList />
return (
<>
<div className="section">
<div className="container">
<div className="box">
<h1 className="title">Todos:</h1>

<div className="block">
<TodoFilter />
</div>

<div className="block">
{isLoading ? (
<Loader />
) : (
<TodoList
todos={filteredTodos}
selectedTodoId={currentTodo ? currentTodo.id : null}
/>
)}
</div>
</div>
</div>
</div>
</div>

<TodoModal />
</>
);
{currentTodo && <TodoModal />}
</div>
</>
);
};
5 changes: 5 additions & 0 deletions src/app/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from './store';

export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
14 changes: 12 additions & 2 deletions src/app/store.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import { combineSlices, configureStore } from '@reduxjs/toolkit';
import {
combineReducers as combineSlices,
configureStore,
} from '@reduxjs/toolkit';
import { filterSlice } from '../features/filter';
import { todosSlice } from '../features/todos';
import { currentTodoSlice } from '../features/currentTodo';

const rootReducer = combineSlices();
const rootReducer = combineSlices({
filter: filterSlice.reducer,
todos: todosSlice.reducer,
currentTodo: currentTodoSlice.reducer,
});

export const store = configureStore({
reducer: rootReducer,
Expand Down
29 changes: 23 additions & 6 deletions src/components/TodoFilter/TodoFilter.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import React from 'react';
import { useAppDispatch, useAppSelector } from '../../app/hooks';
import { setQuery, setStatus } from '../../features/filter';
import { Status } from '../../types/Status';

export const TodoFilter: React.FC = () => {
const dispatch = useAppDispatch();
const query = useAppSelector(state => state.filter.query);

const handleQuery = (event: React.ChangeEvent<HTMLInputElement>) =>
dispatch(setQuery(event.target.value));

const handleStatus = (event: React.ChangeEvent<HTMLSelectElement>) =>
dispatch(setStatus(event.target.value as Status));

return (
<form
className="field has-addons"
onSubmit={event => event.preventDefault()}
>
<p className="control">
<span className="select">
<select data-cy="statusSelect">
<select data-cy="statusSelect" onChange={handleStatus}>
<option value="all">All</option>
<option value="active">Active</option>
<option value="completed">Completed</option>
Expand All @@ -22,18 +34,23 @@ export const TodoFilter: React.FC = () => {
type="text"
className="input"
placeholder="Search..."
value={query}
onChange={handleQuery}
/>
<span className="icon is-left">
<i className="fas fa-magnifying-glass" />
</span>

<span className="icon is-right" style={{ pointerEvents: 'all' }}>
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
<button
data-cy="clearSearchButton"
type="button"
className="delete"
/>
{query && (
<button
data-cy="clearSearchButton"
type="button"
className="delete"
onClick={() => dispatch(setQuery(''))}
/>
)}
</span>
</p>
</form>
Expand Down
Loading

0 comments on commit 364c738

Please sign in to comment.