Untitled
unknown
plain_text
a year ago
6.7 kB
1
Indexable
Never
/* 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> ); };