-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add task solution #1036
base: master
Are you sure you want to change the base?
add task solution #1036
Changes from 1 commit
c0321b6
d84b94b
6c484f8
f1e3aaf
b4b2052
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,61 @@ | ||
/* eslint-disable no-console */ | ||
import 'bulma/css/bulma.css'; | ||
import '@fortawesome/fontawesome-free/css/all.css'; | ||
|
||
import { Loader, TodoFilter, TodoList, TodoModal } from './components'; | ||
|
||
export const App = () => ( | ||
<> | ||
<div className="section"> | ||
<div className="container"> | ||
<div className="box"> | ||
<h1 className="title">Todos:</h1> | ||
import { useEffect, useState } from 'react'; | ||
import { getTodos } from './api'; | ||
import { setTodos } from './features/todos'; | ||
import { useDispatch, useSelector } from 'react-redux'; | ||
import { RootState } from './app/store'; | ||
|
||
<div className="block"> | ||
<TodoFilter /> | ||
</div> | ||
export const App = () => { | ||
const dispatch = useDispatch(); | ||
const currentTodo = useSelector((state: RootState) => state.currentTodo.todo); | ||
const todos = useSelector((state: RootState) => state.todos.todos); | ||
|
||
const [isLoading, setIsLoading] = useState(false); | ||
|
||
useEffect(() => { | ||
const fetchTodos = async () => { | ||
setIsLoading(true); | ||
try { | ||
const response = await getTodos(); | ||
|
||
dispatch(setTodos(response)); | ||
} catch (error) { | ||
console.error('Error fetching todos:', error); | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; | ||
|
||
fetchTodos(); | ||
}, [dispatch]); | ||
|
||
const noTodos = !todos.length; | ||
const modalIsVisible = !!currentTodo; | ||
|
||
return ( | ||
<> | ||
<div className="section"> | ||
<div className="container"> | ||
<div className="box"> | ||
<h1 className="title">Todos:</h1> | ||
|
||
<div className="block"> | ||
<TodoFilter /> | ||
</div> | ||
|
||
<div className="block"> | ||
<Loader /> | ||
<TodoList /> | ||
<div className="block"> | ||
{isLoading ? <Loader /> : !noTodos ? <TodoList /> : null} | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<TodoModal /> | ||
</> | ||
); | ||
{modalIsVisible && <TodoModal />} | ||
</> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,55 @@ | ||
/* eslint-disable */ | ||
import React from 'react'; | ||
import React, { useEffect, useState } from 'react'; | ||
import { useDispatch, useSelector } from 'react-redux'; | ||
import { RootState } from '../../app/store'; | ||
import classNames from 'classnames'; | ||
import { setCurrentTodo } from '../../features/currentTodo'; | ||
import { Todo } from '../../types/Todo'; | ||
import { FilterStatus } from '../../features/filter'; | ||
|
||
export const TodoList: React.FC = () => { | ||
const todos = useSelector((state: RootState) => state.todos.todos); | ||
const currentTodo = useSelector((state: RootState) => state.currentTodo.todo); | ||
const { query, status } = useSelector((state: RootState) => state.filter); | ||
const dispatch = useDispatch(); | ||
|
||
const filterTodos = (query: string, status: FilterStatus, todos: Todo[]) => { | ||
return todos.filter(todo => { | ||
const matchesQuery = todo.title | ||
.toLowerCase() | ||
.includes(query.toLowerCase()); | ||
const matchesStatus = | ||
status === 'all' || | ||
(status === 'active' && !todo.completed) || | ||
(status === 'completed' && todo.completed); | ||
|
||
return matchesQuery && matchesStatus; | ||
}); | ||
}; | ||
|
||
const [visibleTodos, setVisibleTodos] = useState(todos); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So todos, query, and status are stored in state (redux state in this case). Taking it into account - the re-render will happen on any of these variables change. It means there is no reason to store visible todos in state - store them in common variable (and useMemo) |
||
|
||
useEffect(() => { | ||
setVisibleTodos(filterTodos(query, status, todos)); | ||
}, [query, status, todos]); | ||
|
||
if (!todos) { | ||
return; | ||
} | ||
|
||
const handleSetTodo = (todo: Todo) => { | ||
dispatch(setCurrentTodo(todo)); | ||
}; | ||
|
||
const noVisibleTodos = !visibleTodos.length; | ||
|
||
return ( | ||
<> | ||
<p className="notification is-warning"> | ||
There are no todos matching current filter criteria | ||
</p> | ||
{noVisibleTodos && ( | ||
<p className="notification is-warning"> | ||
There are no todos matching current filter criteria | ||
</p> | ||
)} | ||
|
||
<table className="table is-narrow is-fullwidth"> | ||
<thead> | ||
|
@@ -25,6 +68,59 @@ export const TodoList: React.FC = () => { | |
</thead> | ||
|
||
<tbody> | ||
{visibleTodos.map(todo => ( | ||
<tr | ||
data-cy="todo" | ||
className={classNames('', { | ||
'has-background-info-light': todo.id === currentTodo?.id, | ||
})} | ||
> | ||
<td className="is-vcentered">{todo.id}</td> | ||
<td className="is-vcentered"> | ||
{todo.completed && ( | ||
<span className="icon" data-cy="iconCompleted"> | ||
<i className="fas fa-check" /> | ||
</span> | ||
)} | ||
</td> | ||
|
||
<td className="is-vcentered is-expanded"> | ||
<p | ||
className={classNames('', { | ||
'has-text-danger': !todo.completed, | ||
'has-text-success': todo.completed, | ||
})} | ||
> | ||
{todo.title} | ||
</p> | ||
</td> | ||
|
||
<td className="has-text-right is-vcentered"> | ||
<button | ||
data-cy="selectButton" | ||
className="button" | ||
type="button" | ||
onClick={() => handleSetTodo(todo)} | ||
> | ||
<span className="icon"> | ||
<i | ||
className={classNames('far', { | ||
'fa-eye': todo.id !== currentTodo?.id, | ||
'fa-eye-slash': todo.id === currentTodo?.id, | ||
})} | ||
/> | ||
</span> | ||
</button> | ||
</td> | ||
</tr> | ||
))} | ||
</tbody> | ||
</table> | ||
</> | ||
); | ||
}; | ||
|
||
/* | ||
<tr data-cy="todo"> | ||
<td className="is-vcentered">1</td> | ||
<td className="is-vcentered"> </td> | ||
|
@@ -217,8 +313,4 @@ export const TodoList: React.FC = () => { | |
</button> | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</> | ||
); | ||
}; | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.