Untitled
unknown
plain_text
2 years ago
6.7 kB
8
Indexable
/* eslint-disable jsx-a11y/control-has-associated-label */
import React, { useEffect, useState } from "react";
import { Todo } from "./types/Todo";
import { getTodos, postTodo, deleteTodo } from "./api/todos";
import { ErrorPopup } from "./components/ErrorPopup";
const USER_ID = 11546;
export const App: React.FC = () => {
const [todos, setTodos] = useState<Todo[]>([]);
const [error, setError] = useState<string | null>(null);
const [filterType, setFilterType] = useState<"All" | "Active" | "Completed">(
"All"
);
const [newTitle, setNewTitle] = useState("");
const [isLoading, setIsLoading] = useState(false);
const handleAddTodo = async (title: string) => {
try {
const trimmedTitle = title.trim();
if (trimmedTitle === "") {
handleErrorMessage("Title should not be empty");
return;
}
setIsLoading(true);
const newTodo: Omit<Todo, "id"> = {
title: trimmedTitle,
completed: false,
userId: USER_ID,
};
const createdTodo = await postTodo(newTodo);
setTodos([...todos, createdTodo]);
setIsLoading(false);
} catch (e) {
handleErrorMessage("Unable to add a todo");
setIsLoading(false);
}
};
const handleDeleteTodo = async (todo: Todo) => {
try {
await deleteTodo(todo.id);
setTodos((prevTodos) => prevTodos.filter((t) => t.id !== todo.id));
} catch (e) {
handleErrorMessage("Unable to delete a todo");
}
};
const handleErrorMessage = (message: string | null) => {
setError(message);
setTimeout(() => {
setError(null);
}, 3000);
};
const filterTodos = (
todosArray: Todo[],
filterStatus: "All" | "Active" | "Completed"
) => {
return todosArray.filter((todo) => {
if (filterStatus === "All") {
return true;
}
if (filterStatus === "Active") {
return !todo.completed;
}
return todo.completed;
});
};
const changeFilterStatus = (type: "All" | "Active" | "Completed") => {
setFilterType(type);
};
useEffect(() => {
setError(null);
if (USER_ID) {
getTodos(USER_ID)
.then((data) => {
setTodos(data);
})
.catch(() => {
handleErrorMessage("Unable to load todos");
});
}
}, []);
return (
<div className="todoapp">
<h1 className="todoapp__title">todos</h1>
<div className="todoapp__content">
<header className="todoapp__header">
{todos.some((todo) => !todo.completed) && (
<button
type="button"
className="todoapp__toggle-all active"
data-cy="ToggleAllButton"
/>
)}
<form
onSubmit={(e) => {
e.preventDefault();
handleAddTodo(newTitle);
setNewTitle("");
}}
>
<input
data-cy="NewTodoField"
type="text"
className="todoapp__new-todo"
placeholder="What needs to be done?"
value={newTitle}
onChange={(e) => setNewTitle(e.target.value)}
autoFocus
disabled={isLoading}
/>
</form>
</header>
<section className="todoapp__main" data-cy="TodoList">
{filterTodos(todos, filterType).map((todo) => (
<div
data-cy="Todo"
className={`todo ${todo.completed ? "completed" : ""}`}
key={todo.id}
>
<label className="todo__status-label">
<input
data-cy="TodoStatus"
type="checkbox"
className="todo__status"
checked={todo.completed}
/>
</label>
<span data-cy="TodoTitle" className="todo__title">
{todo.title}
</span>
{/* Remove button appears only on hover */}
<button
type="button"
className="todo__remove"
data-cy="TodoDelete"
onClick={() => handleDeleteTodo(todo)}
>
×
</button>
{/* overlay will cover the todo while it is being updated */}
<div data-cy="TodoLoader" className="modal overlay">
<div className="modal-background has-background-white-ter" />
<div className="loader" />
</div>
</div>
))}
</section>
{todos.some((todo) => !todo.completed) && (
<footer className="todoapp__footer" data-cy="Footer">
<span className="todo-count" data-cy="TodosCounter">
{`${todos.filter((todo) => !todo.completed).length} item${
todos.filter((todo) => !todo.completed).length !== 1 ? "s" : ""
} left`}
</span>
<nav className="filter" data-cy="Filter">
<a
href="#/"
className={`filter__link ${
filterType === "All" ? "selected" : ""
}`}
onClick={() => changeFilterStatus("All")}
data-cy="FilterLinkAll"
>
All
</a>
<a
href="#/active"
className={`filter__link ${
filterType === "Active" ? "selected" : ""
}`}
onClick={() => changeFilterStatus("Active")}
data-cy="FilterLinkActive"
>
Active
</a>
<a
href="#/completed"
className={`filter__link ${
filterType === "Completed" ? "selected" : ""
}`}
onClick={() => changeFilterStatus("Completed")}
data-cy="FilterLinkCompleted"
>
Completed
</a>
</nav>
{todos.filter((todo) => todo.completed).length > 0 && (
<button
type="button"
className="todoapp__clear-completed"
data-cy="ClearCompletedButton"
>
Clear completed
</button>
)}
</footer>
)}
</div>
<ErrorPopup error={error} setError={setError} />
</div>
);
};
Editor is loading...