-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d345046
commit 3ef1270
Showing
22 changed files
with
811 additions
and
634 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,20 @@ | ||
import { Todo } from '../types/Todo'; | ||
import { client } from '../utils/fetchClient'; | ||
|
||
export const USER_ID = 2161; | ||
export const USER_ID = 2164; | ||
|
||
export const getTodos = () => { | ||
return client.get<Todo[]>(`/todos?userId=${USER_ID}`); | ||
}; | ||
|
||
export const addTodo = ({ title, completed }: Omit<Todo, 'id' | 'userId'>) => { | ||
return client.post<Todo>('/todos', { title, completed, userId: USER_ID }); | ||
export const addTodo = (newTodo: Omit<Todo, 'id' | 'userId'>) => { | ||
return client.post<Todo>(`/todos`, { ...newTodo, userId: USER_ID }); | ||
}; | ||
|
||
export const deleteTodo = (id: number) => { | ||
return client.delete(`/todos/${id}`); | ||
export const deleteTodo = (todoId: number) => { | ||
return client.delete(`/todos/${todoId}`); | ||
}; | ||
|
||
export const changeTodoCompleted = ({ | ||
id, | ||
completed, | ||
}: Omit<Todo, 'userId' | 'title'>) => { | ||
return client.patch(`/todos/${id}`, { completed }); | ||
}; | ||
|
||
export const changeTodoTitle = ({ | ||
id, | ||
title, | ||
}: Omit<Todo, 'userId' | 'completed'>) => { | ||
return client.patch(`/todos/${id}`, { title }); | ||
export const updateTodo = (todo: Todo) => { | ||
return client.patch<Todo>(`/todos/${todo.id}`, todo); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import React, { Dispatch, SetStateAction, useEffect } from 'react'; | ||
import { ErrorType } from '../types/ErrorTypes'; | ||
import classNames from 'classnames'; | ||
|
||
type Props = { | ||
error: ErrorType; | ||
setError: Dispatch<SetStateAction<ErrorType>>; | ||
}; | ||
|
||
export const ErrorNotification: React.FC<Props> = props => { | ||
const { error, setError } = props; | ||
|
||
useEffect(() => { | ||
if (error === ErrorType.Empty) { | ||
return; | ||
} | ||
|
||
const timerId = setTimeout(() => { | ||
setError(ErrorType.Empty); | ||
}, 3000); | ||
|
||
return () => { | ||
clearTimeout(timerId); | ||
}; | ||
}, [error, setError]); | ||
|
||
return ( | ||
<div | ||
data-cy="ErrorNotification" | ||
className={classNames( | ||
'notification is-danger is-light has-text-weight-normal', | ||
{ hidden: error === ErrorType.Empty }, | ||
)} | ||
> | ||
<button | ||
data-cy="HideErrorButton" | ||
type="button" | ||
className="delete" | ||
onClick={() => setError(ErrorType.Empty)} | ||
/> | ||
{error} | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import classNames from 'classnames'; | ||
import { FILTER_BY } from '../constants/constants'; | ||
import React from 'react'; | ||
|
||
interface Props { | ||
filter: string; | ||
onFilter: (v: string) => void; | ||
} | ||
|
||
export const Filter: React.FC<Props> = ({ | ||
filter: currentFilter, | ||
onFilter, | ||
}) => { | ||
const getNameFilter = (str: string) => | ||
str.slice(0, 1).toUpperCase() + str.slice(1); | ||
|
||
return ( | ||
<nav className="filter" data-cy="Filter"> | ||
{Object.values(FILTER_BY).map(filter => ( | ||
<a | ||
key={filter} | ||
href={`#/${filter}`} | ||
className={classNames('filter__link', { | ||
selected: currentFilter === filter, | ||
})} | ||
data-cy={`FilterLink${getNameFilter(filter)}`} | ||
onClick={() => onFilter(filter)} | ||
> | ||
{getNameFilter(filter)} | ||
</a> | ||
))} | ||
</nav> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import React from 'react'; | ||
import { Filter } from './Filter'; | ||
import { Todo } from '../types/Todo'; | ||
import { getCompletedTodos } from '../utils/methods'; | ||
|
||
interface Props { | ||
sizeLeft: number; | ||
filter: string; | ||
onFilter: (v: string) => void; | ||
onClear: () => void; | ||
todos: Todo[]; | ||
} | ||
|
||
export const Footer: React.FC<Props> = ({ | ||
sizeLeft, | ||
filter, | ||
onFilter, | ||
onClear, | ||
todos, | ||
}) => { | ||
return ( | ||
<footer className="todoapp__footer" data-cy="Footer"> | ||
<span className="todo-count" data-cy="TodosCounter"> | ||
{`${sizeLeft} items left`} | ||
</span> | ||
|
||
<Filter onFilter={onFilter} filter={filter} /> | ||
|
||
{/* this button should be disabled if there are no completed todos */} | ||
<button | ||
type="button" | ||
className="todoapp__clear-completed" | ||
data-cy="ClearCompletedButton" | ||
disabled={getCompletedTodos(todos).length === 0} | ||
onClick={onClear} | ||
> | ||
Clear completed | ||
</button> | ||
</footer> | ||
); | ||
}; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import classNames from 'classnames'; | ||
import React from 'react'; | ||
import { updateTodoCompleted } from '../api/todos'; | ||
import { Todo } from '../types/Todo'; | ||
import { pause } from '../utils/methods'; | ||
import { NewTodoField } from './NewTodoField'; | ||
|
||
interface Props { | ||
todos: Todo[]; | ||
sizeLeft: number; | ||
onTodos: React.Dispatch<React.SetStateAction<Todo[]>>; | ||
onErrorMessage: (v: string) => void; | ||
onMassLoader: (v: boolean) => void; | ||
onLoader: (v: boolean) => void; | ||
onTempTodo: (v: Todo | null) => void; | ||
} | ||
|
||
export const Header: React.FC<Props> = ({ | ||
todos, | ||
sizeLeft, | ||
onTodos, | ||
onErrorMessage, | ||
onMassLoader, | ||
onLoader, | ||
onTempTodo, | ||
}) => { | ||
const handleChangeAll = async () => { | ||
const notCompletedTodos = todos.filter(e => !e.completed); | ||
|
||
try { | ||
onMassLoader(true); | ||
await pause(); | ||
const todosToUpdate = | ||
notCompletedTodos.length > 0 ? notCompletedTodos : todos; | ||
|
||
const updatedTodos = await Promise.all( | ||
todosToUpdate.map(async e => { | ||
const newTodo = await updateTodoCompleted({ | ||
id: e.id, | ||
completed: notCompletedTodos.length > 0 || sizeLeft !== 0, | ||
}); | ||
|
||
return { | ||
...e, | ||
completed: newTodo.completed, | ||
}; | ||
}), | ||
); | ||
|
||
onTodos(prev => | ||
prev.map(todo => { | ||
const updated = updatedTodos.find( | ||
updatedTodo => updatedTodo.id === todo.id, | ||
); | ||
|
||
return updated ? updated : todo; | ||
}), | ||
); | ||
} catch { | ||
onErrorMessage('Unable to update a todo'); | ||
} finally { | ||
onMassLoader(false); | ||
} | ||
}; | ||
|
||
return ( | ||
<header className="todoapp__header"> | ||
{/* this button should have `active` class only if all todos are completed */} | ||
{todos.length > 0 && ( | ||
<button | ||
type="button" | ||
className={classNames('todoapp__toggle-all', { | ||
active: sizeLeft === 0, | ||
})} | ||
// className="todoapp__toggle-all active" | ||
data-cy="ToggleAllButton" | ||
onClick={handleChangeAll} | ||
/> | ||
)} | ||
|
||
{/* Add a todo on form submit */} | ||
<NewTodoField | ||
onErrorMessage={onErrorMessage} | ||
onTempTodo={onTempTodo} | ||
onLoader={onLoader} | ||
onTodos={onTodos} | ||
/> | ||
</header> | ||
); | ||
}; |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import React, { useEffect, useRef } from 'react'; | ||
|
||
interface Props { | ||
errorMessage: string; | ||
onErrorMessage: (v: string) => void; | ||
} | ||
|
||
export const MyError: React.FC<Props> = ({ errorMessage, onErrorMessage }) => { | ||
const errorDiv = useRef<HTMLDivElement | null>(null); | ||
const timerId = useRef(0); | ||
|
||
useEffect(() => { | ||
if (errorDiv.current && errorMessage) { | ||
errorDiv.current.classList.remove('hidden'); | ||
window.clearTimeout(timerId.current); | ||
timerId.current = window.setTimeout(() => { | ||
errorDiv.current?.classList.add('hidden'); | ||
onErrorMessage(''); | ||
}, 3000); | ||
} | ||
}, [errorMessage, onErrorMessage]); | ||
|
||
const closeError = () => { | ||
window.clearTimeout(timerId.current); | ||
errorDiv.current?.classList.add('hidden'); | ||
}; | ||
|
||
return ( | ||
<div | ||
ref={errorDiv} | ||
data-cy="ErrorNotification" | ||
className="notification is-danger is-light has-text-weight-normal hidden" | ||
> | ||
<button | ||
data-cy="HideErrorButton" | ||
type="button" | ||
className="delete" | ||
onClick={closeError} | ||
/> | ||
{/* show only one message at a time */} | ||
{errorMessage} | ||
</div> | ||
); | ||
}; |
Oops, something went wrong.