Skip to content

Commit

Permalink
add task solution
Browse files Browse the repository at this point in the history
  • Loading branch information
Luk2asz committed Sep 1, 2023
1 parent 856fa44 commit 8720317
Show file tree
Hide file tree
Showing 14 changed files with 738 additions and 13 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ Implement the ability to edit a todo title on double click:

- Implement a solution following the [React task guideline](https://github.com/mate-academy/react_task-guideline#react-tasks-guideline).
- Use the [React TypeScript cheat sheet](https://mate-academy.github.io/fe-program/js/extra/react-typescript).
- Replace `<your_account>` with your Github username in the [DEMO LINK](https://<your_account>.github.io/react_todo-app-with-api/) and add it to the PR description.
- Replace `<your_account>` with your Github username in the [DEMO LINK](https://Luk2asz.github.io/react_todo-app-with-api/) and add it to the PR description.
161 changes: 149 additions & 12 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,161 @@
/* eslint-disable max-len */
/* eslint-disable @typescript-eslint/no-unused-expressions */
/* eslint-disable jsx-a11y/control-has-associated-label */
import React from 'react';
import React, { useMemo, useState } from 'react';
import { UserWarning } from './UserWarning';
import { Todo } from './types/Todo';
import { SortBy } from './types/SortBy';
import { deleteTodo, updateTodo } from './api/todos';
import { Todos } from './components/Todos';
import { useGetTodos } from './hooks';
import { Errors } from './types';
import { Footer } from './components/Footer';
import { Header } from './components/Header';

const USER_ID = 0;
const USER_ID = 11361;

export const App: React.FC = () => {
const [userId] = useState(USER_ID);
const [sortBy, setSortBy] = useState<SortBy>(SortBy.all);
const [selectedTodo, setSelectedTodo] = useState<number[]>([]);
const [tempTodo, setTempTodo] = useState<Todo | null>(null);
const [isDeleteUpdateTodo, setIsDeleteUpdateTodo] = useState<boolean>(false);
const [makeAnyChange, setMakeAnyChange] = useState<boolean>(false);
const {
isLoading,
todos,
errorMessage,
handleError,
} = useGetTodos(USER_ID, makeAnyChange);

const handleSelectedTodo = (value: number[]) => {
setSelectedTodo(value);
};

const handleSetTempTodo = (value:Todo | null) => {
setTempTodo(value);
};

const handleSetMakeAnyChange = (value:boolean) => {
setMakeAnyChange(value);
};

const handleDeleteUptadeTodo = (value: boolean) => {
setIsDeleteUpdateTodo(value);
};

const todosNotCompleted = useMemo(() => {
return todos.filter(todo => todo.completed === false).length;
}, [todos]);

const deleteOneTodo = async (todoId: number) => {
setSelectedTodo(prevSelectedTodo => [...prevSelectedTodo, todoId]);
setIsDeleteUpdateTodo(true);
try {
await deleteTodo(USER_ID, todoId);
} catch (error) {
handleError(Errors.delete);
} finally {
setSelectedTodo([]);
}

setIsDeleteUpdateTodo(false);
setMakeAnyChange(!makeAnyChange);
};

const updateCheckTodo = async (todoId: number) => {
setSelectedTodo(prevSelectedTodo => [...prevSelectedTodo, todoId]);
setIsDeleteUpdateTodo(true);
let updatedTodo: Todo | undefined = todos.find(todo => todo.id === todoId);

updatedTodo
? updatedTodo = { ...updatedTodo, completed: !updatedTodo.completed }
: null;

try {
await updateTodo(USER_ID, updatedTodo, todoId);
} catch (error) {
handleError(Errors.update);
} finally {
setSelectedTodo([]);
}

setIsDeleteUpdateTodo(true);
setMakeAnyChange(!makeAnyChange);
};

const handleUpdateCheckTodo = (value: number) => updateCheckTodo(value);
const handleDeleteTodo = (value: number) => deleteOneTodo(value);

const handleSetSortBy = (value: SortBy) => {
setSortBy(value);
};

if (!USER_ID) {
return <UserWarning />;
}

return (
<section className="section container">
<p className="title is-4">
Copy all you need from the prev task:
<br />
<a href="https://github.com/mate-academy/react_todo-app-add-and-delete#react-todo-app-add-and-delete">React Todo App - Add and Delete</a>
</p>

<p className="subtitle">Styles are already copied</p>
</section>
<div className="todoapp">
<h1 className="todoapp__title">todos</h1>

<div className="todoapp__content">
<Header
todosNotCompleted={todosNotCompleted}
todos={todos}
handleUpdateCheckTodo={handleUpdateCheckTodo}
handleSelectedTodo={handleSelectedTodo}
handleError={handleError}
handleSetTempTodo={handleSetTempTodo}
userId={userId}
handleSetMakeAnyChange={handleSetMakeAnyChange}
makeAnyChange={makeAnyChange}
selectedTodo={selectedTodo}
/>

<Todos
todos={todos}
tempTodo={tempTodo}
sortBy={sortBy}
handleDeleteTodo={handleDeleteTodo}
isLoading={isLoading}
selectedTodo={selectedTodo}
handleUpdateCheckTodo={handleUpdateCheckTodo}
handleSelectedTodo={handleSelectedTodo}
handleError={handleError}
userId={userId}
handleSetMakeAnyChange={handleSetMakeAnyChange}
makeAnyChange={makeAnyChange}
isDeleteUpdateTodo={isDeleteUpdateTodo}
handleDeleteUptadeTodo={handleDeleteUptadeTodo}
/>

{todos.length > 0 && (
<Footer
todosNotCompleted={todosNotCompleted}
handleSetSortBy={handleSetSortBy}
sortBy={sortBy}
todos={todos}
handleDeleteTodo={handleDeleteTodo}
handleSelectedTodo={handleSelectedTodo}
selectedTodo={selectedTodo}
/>
)}

</div>

<div
className={errorMessage
? 'notification is-danger is-light has-text-weight-normal'
: 'notification is-danger is-light has-text-weight-normal hidden'}
>
<button
type="button"
className="delete"
onClick={() => handleError(Errors.noEroor)}
/>

{errorMessage}
</div>
</div>
);
};
18 changes: 18 additions & 0 deletions src/api/todos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Todo } from '../types/Todo';
import { client } from '../utils/fetchClient';

export const getTodos = (userId: number) => client.get<Todo[]>(`/todos?userId=${userId}`);

export const deleteTodo = (userId: number, todoId: number) => {
return client.delete(`/todos/${todoId}/?userId=${userId}`);
};

export const addTodo = (userId: number, data: Todo | null) => {
return client.post<Todo[]>(`/todos/?userId=${userId}`, data);
};

export const updateTodo = (
userId: number, data: Todo | undefined, todoId: number,
) => {
return client.patch<Todo[]>(`/todos/${todoId}/?userId=${userId}`, data);
};
93 changes: 93 additions & 0 deletions src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// import { deleteTodo } from '../api/todos';
import { SortBy, Todo } from '../types';

type Props = {
todosNotCompleted: number,
handleSetSortBy: (value:SortBy) => void,
sortBy: SortBy,
todos: Todo[],
handleDeleteTodo: (value: number) => void,
handleSelectedTodo: (todoID: number[]) => void;
selectedTodo: number[];
};

export const Footer: React.FC<Props> = ({
todosNotCompleted,
handleSetSortBy,
sortBy,
todos,
handleDeleteTodo,
handleSelectedTodo,
selectedTodo,
}) => {
const deleteCompletedtodo = () => {
try {
todos.forEach(async todo => {
if (todo.completed === true) {
handleSelectedTodo([...selectedTodo, todo.id]);
}
});
} finally {
handleSelectedTodo([]);
}

todos.forEach(todo => {
if (todo.completed === true) {
handleDeleteTodo(todo.id);
}
});
};

return (
<footer className="todoapp__footer">
<span className="todo-count">
{`${todosNotCompleted} items left`}
</span>

<nav className="filter">
<a
href="#/"
className={sortBy === 'all'
? 'filter__link selected'
: 'filter__link'}
onClick={() => handleSetSortBy(SortBy.all)}
>
All
</a>

<a
href="#/active"
className={sortBy === 'active'
? 'filter__link selected'
: 'filter__link'}
onClick={() => handleSetSortBy(SortBy.active)}
>
Active
</a>

<a
href="#/completed"
className={sortBy === 'completed'
? 'filter__link selected'
: 'filter__link'}
onClick={() => handleSetSortBy(SortBy.completed)}
>
Completed
</a>
</nav>

<button
type="button"
data-cy="ClearCompletedButton"
className="todoapp__clear-completed"
disabled={todosNotCompleted === todos.length}
onClick={() => {
deleteCompletedtodo();
}}
>
Clear completed
</button>

</footer>
);
};
Loading

0 comments on commit 8720317

Please sign in to comment.