Skip to content

Commit

Permalink
Fix comments
Browse files Browse the repository at this point in the history
  • Loading branch information
LiliiaVol committed Dec 27, 2024
1 parent 3218113 commit f3f929c
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 290 deletions.
229 changes: 65 additions & 164 deletions src/App.tsx

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion src/api/todos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export const getTodos = () => {
return client.get<Todo[]>(`/todos?userId=${USER_ID}`);
};

// Add more methods here
export const postTodo = (data: Partial<Todo>): Promise<TodoResponse> => {
return client.post('/todos', data);
};
Expand Down
14 changes: 3 additions & 11 deletions src/components/ErrorNotification/ErrorNotification.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import classNames from 'classnames';
import React from 'react';
import { ErrorType } from '../../types/ErrorType';
import { Todo } from '../../types/Todo';

type Props = {
todos: Todo[];
errorState: Record<ErrorType, boolean>;
errorState: ErrorType;
};

export const ErrorNotification: React.FC<Props> = (props: Props) => {
const { todos, errorState } = props;
const { errorState } = props;

return (
<div
Expand All @@ -22,13 +20,7 @@ export const ErrorNotification: React.FC<Props> = (props: Props) => {
)}
>
<button data-cy="HideErrorButton" type="button" className="delete" />
{!todos.length && `Unable to load todos`}
{errorState[ErrorType.Input] && `Title should not be empty`}
{errorState[ErrorType.Add] && `Unable to add a todo`}
{(errorState[ErrorType.Delete] ||
errorState[ErrorType.CompletedDelete]) &&
`Unable to delete a todo`}
{errorState[ErrorType.Update] && `Unable to update a todo`}
{!!errorState.length && errorState}
</div>
);
};
80 changes: 32 additions & 48 deletions src/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
import classNames from 'classnames';
import React from 'react';
import React, { useMemo } from 'react';
import { Todo } from '../../types/Todo';
import { FilterType } from '../../types/FilterType';

type Props = {
todosLeft: Todo[];
todos: Todo[];
filterType: FilterType;
setFilterType: React.Dispatch<React.SetStateAction<FilterType>>;
todosCompleted: Todo[];
onDeleteCompletedTodos: () => void;
};

export const Footer: React.FC<Props> = (props: Props) => {
const {
todosLeft,
filterType,
setFilterType,
todosCompleted,
onDeleteCompletedTodos,
} = props;
const { todos, filterType, setFilterType, onDeleteCompletedTodos } = props;

const todosLeft = useMemo((): Todo[] => {
return todos.filter(todo => !todo.completed);
}, [todos]);

const todosCompleted = useMemo((): Todo[] => {
return todos.filter(todo => todo.completed);
}, [todos]);

const filterOptions = [
{ label: 'All', value: FilterType.All, href: '#/' },
{ label: 'Active', value: FilterType.Active, href: '#/active' },
{ label: 'Completed', value: FilterType.Completed, href: '#/completed' },
];

return (
<footer className={classNames('todoapp__footer')} data-cy="Footer">
Expand All @@ -27,44 +34,21 @@ export const Footer: React.FC<Props> = (props: Props) => {
</span>

<nav className="filter" data-cy="Filter">
<a
href="#/"
className={classNames('filter__link', {
selected: filterType === FilterType.All,
})}
data-cy="FilterLinkAll"
onClick={() => {
setFilterType(FilterType.All);
}}
>
All
</a>

<a
href="#/active"
className={classNames('filter__link', {
selected: filterType === FilterType.Active,
})}
data-cy="FilterLinkActive"
onClick={() => {
setFilterType(FilterType.Active);
}}
>
Active
</a>

<a
href="#/completed"
className={classNames('filter__link', {
selected: filterType === FilterType.Completed,
})}
data-cy="FilterLinkCompleted"
onClick={() => {
setFilterType(FilterType.Completed);
}}
>
Completed
</a>
{filterOptions.map(option => (
<a
key={option.value}
href={option.href}
className={classNames('filter__link', {
selected: filterType === option.value,
})}
data-cy={`FilterLink${option.label}`}
onClick={() => {
setFilterType(option.value);
}}
>
{option.label}
</a>
))}
</nav>

<button
Expand Down
2 changes: 1 addition & 1 deletion src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const Header: React.FC<Props> = (props: Props) => {

return (
<header className="todoapp__header">
{!todos.length || (
{!!todos.length && (
<button
type="button"
className={classNames('todoapp__toggle-all', {
Expand Down
42 changes: 17 additions & 25 deletions src/components/TodoComponent/TodoComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,39 @@
/* eslint-disable jsx-a11y/label-has-associated-control */
/* eslint-disable jsx-a11y/control-has-associated-label */
import React from 'react';
import React, { useState } from 'react';
import classNames from 'classnames';
import { Todo } from '../../types/Todo';

type Props = {
onDoubleClickTodo: (todo: Todo) => void;
todo: Todo;
onTitleEdit: (newValue: string) => void;
onCompletedTodo: (todo: Todo) => void;
todoChangedTitle: Todo | null;
onSubmitEdit: (event: React.FormEvent<HTMLFormElement>) => Promise<void>;
inputEditRef: React.RefObject<HTMLInputElement> | null;
titleEdit: string;
handleBlurEdit: () => void;
loadingTodoIds: number[];
onDoubleClickTodo: (todo: Todo) => void;
onCompletedTodo: (todo: Todo) => void;
onSubmitEdit: (
titleEdit: string,
event: React.FormEvent<HTMLFormElement> | null,
) => Promise<void>;
handleBlurEdit: (titleEdit: string) => void;
onDeleteTodo: (todoId: number | null) => void;
idDeletedTodo: number | null;
activeTodos: Todo[];
isLoading: boolean;
};

export const TodoComponent: React.FC<Props> = (props: Props) => {
const {
onDoubleClickTodo,
todo,
onTitleEdit,
onCompletedTodo,
todoChangedTitle,
onSubmitEdit,
inputEditRef,
titleEdit,
handleBlurEdit,
onDeleteTodo,
idDeletedTodo,
activeTodos,
isLoading,
loadingTodoIds,
} = props;

const [titleEdit, setTitleEdit] = useState('');

const { id, completed, title } = todo;

return (
Expand All @@ -45,7 +42,7 @@ export const TodoComponent: React.FC<Props> = (props: Props) => {
className={classNames('todo', { completed: completed })}
onDoubleClick={() => {
onDoubleClickTodo(todo);
onTitleEdit(title);
setTitleEdit(title);
}}
>
<label className="todo__status-label">
Expand All @@ -61,16 +58,16 @@ export const TodoComponent: React.FC<Props> = (props: Props) => {
</label>

{todoChangedTitle?.id === id ? (
<form onSubmit={onSubmitEdit}>
<form onSubmit={event => onSubmitEdit(titleEdit, event)}>
<input
data-cy="TodoTitleField"
type="text"
className="todo__title-field"
placeholder="Empty todo will be deleted"
ref={inputEditRef}
value={titleEdit}
onChange={event => onTitleEdit(event.target.value)}
onBlur={handleBlurEdit}
onChange={event => setTitleEdit(event.target.value)}
onBlur={() => handleBlurEdit(titleEdit)}
/>
</form>
) : (
Expand All @@ -95,12 +92,7 @@ export const TodoComponent: React.FC<Props> = (props: Props) => {
<div
data-cy="TodoLoader"
className={classNames('modal overlay', {
'is-active':
idDeletedTodo === id ||
(activeTodos.find(activeTodo => todo.id === activeTodo.id) &&
isLoading) ||
(isLoading && todoChangedTitle?.id === id) ||
todo.id === 0,
'is-active': loadingTodoIds.includes(id),
})}
>
<div className="modal-background has-background-white-ter" />
Expand Down
56 changes: 22 additions & 34 deletions src/components/TodoList/TodoList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,16 @@ type Props = {
onDeleteTodo: (todoId: number | null) => void;
onCompletedTodo: (todo: Todo) => void;
onDoubleClickTodo: (todo: Todo) => void;
onSubmitEdit: (event: React.FormEvent<HTMLFormElement>) => Promise<void>;
onSubmitEdit: (
titleEdit: string,
event: React.FormEvent<HTMLFormElement> | null,
) => Promise<void>;
tempTodo: Todo | null;
idDeletedTodo: number | null;
todoChangedTitle: Todo | null;
isLoading: boolean;
activeTodos: Todo[];
titleEdit: string;
onTitleEdit: (newValue: string) => void;
inputEditRef: React.RefObject<HTMLInputElement> | null;
onCancelEdit: () => void;
handleBlurEdit: () => void;
handleBlurEdit: (titleEdit: string) => void;
loadingTodoIds: number[];
};

export const TodoList: React.FC<Props> = (props: Props) => {
Expand All @@ -31,34 +30,31 @@ export const TodoList: React.FC<Props> = (props: Props) => {
onDoubleClickTodo,
onSubmitEdit,
tempTodo,
idDeletedTodo,
todoChangedTitle,
isLoading,
activeTodos,
titleEdit,
onTitleEdit,
inputEditRef,
onCancelEdit,
handleBlurEdit,
loadingTodoIds,
} = props;

const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
onCancelEdit();
}
};
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
onCancelEdit();
}
};

document.addEventListener('keydown', handleKeyDown);

return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [onCancelEdit]);

useEffect(() => {
if (inputEditRef?.current) {
inputEditRef.current.focus();
inputEditRef.current.addEventListener('keydown', handleKeyDown);
}

return () => {
if (inputEditRef?.current) {
inputEditRef.current.removeEventListener('keydown', handleKeyDown);
}
};
}, [todoChangedTitle, inputEditRef]);

return (
Expand All @@ -69,17 +65,13 @@ export const TodoList: React.FC<Props> = (props: Props) => {
key={todo.id}
onDoubleClickTodo={onDoubleClickTodo}
todo={todo}
onTitleEdit={onTitleEdit}
onCompletedTodo={onCompletedTodo}
todoChangedTitle={todoChangedTitle}
onSubmitEdit={onSubmitEdit}
inputEditRef={inputEditRef}
titleEdit={titleEdit}
handleBlurEdit={handleBlurEdit}
onDeleteTodo={onDeleteTodo}
idDeletedTodo={idDeletedTodo}
activeTodos={activeTodos}
isLoading={isLoading}
loadingTodoIds={loadingTodoIds}
/>
);
})}
Expand All @@ -88,17 +80,13 @@ export const TodoList: React.FC<Props> = (props: Props) => {
<TodoComponent
onDoubleClickTodo={onDoubleClickTodo}
todo={tempTodo}
onTitleEdit={onTitleEdit}
onCompletedTodo={onCompletedTodo}
todoChangedTitle={todoChangedTitle}
onSubmitEdit={onSubmitEdit}
inputEditRef={inputEditRef}
titleEdit={titleEdit}
handleBlurEdit={handleBlurEdit}
onDeleteTodo={onDeleteTodo}
idDeletedTodo={idDeletedTodo}
activeTodos={activeTodos}
isLoading={isLoading}
loadingTodoIds={loadingTodoIds}
/>
)}
</section>
Expand Down
13 changes: 7 additions & 6 deletions src/types/ErrorType.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
export enum ErrorType {
Load = 'LOAD_ERROR',
Input = 'INPUT_ERROR',
Add = 'ADD_ERROR',
Delete = 'DELETE_ERROR',
CompletedDelete = 'COMPLETED_DELETE_ERROR',
Update = 'UPDATE_ERROR',
Default = '',
Input = 'Title should not be empty',
Add = 'Unable to add a todo',
Delete = 'Unable to delete a todo',
// CompletedDelete = 'Unable to delete a completed todo',
Update = 'Unable to update a todo',
Load = 'Unable to load todos',
}
15 changes: 15 additions & 0 deletions src/utils/filterTodos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { FilterType } from '../types/FilterType';
import { Todo } from '../types/Todo';

export const filterTodos = (todos: Todo[], filterType: FilterType) => {
switch (filterType) {
case FilterType.All:
return todos;
case FilterType.Active:
return todos.filter(todo => !todo.completed);
case FilterType.Completed:
return todos.filter(todo => todo.completed);
default:
return todos;
}
};

0 comments on commit f3f929c

Please sign in to comment.