Skip to content

Commit

Permalink
solution
Browse files Browse the repository at this point in the history
  • Loading branch information
RUBIC0N5545 committed Jan 3, 2025
1 parent 5381a7d commit b90c37f
Show file tree
Hide file tree
Showing 9 changed files with 461 additions and 145 deletions.
181 changes: 37 additions & 144 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,156 +1,49 @@
/* eslint-disable jsx-a11y/label-has-associated-control */
/* eslint-disable jsx-a11y/control-has-associated-label */
import React from 'react';
import React, { useEffect, useState } from 'react';
import { Todo } from './types/Todo';
import { FilterTypes } from './types/FilterType';
import Header from './Components/Header';
import TodoList from './Components/TodoList';
import Footer from './Components/Footer';
import { useTodoContext } from './Context/TodoProvider';

export const App: React.FC = () => {
const { todoItems } = useTodoContext();
const [todosToShow, setTodosToShow] = useState<Todo[]>([]);
const [selectedFilter, setSelectedFilter] = useState<FilterTypes>(
FilterTypes.ALL,
);

useEffect(() => {
setTodosToShow(
todoItems.filter(todo => {
switch (selectedFilter) {
case FilterTypes.ACTIVE:
return !todo.completed;
case FilterTypes.COMPLETED:
return todo.completed;
default:
return true;
}
}),
);
}, [todoItems, selectedFilter]);

return (
<div className="todoapp">
<h1 className="todoapp__title">todos</h1>

<div className="todoapp__content">
<header className="todoapp__header">
{/* this button should have `active` class only if all todos are completed */}
<button
type="button"
className="todoapp__toggle-all active"
data-cy="ToggleAllButton"
/>

{/* Add a todo on form submit */}
<form>
<input
data-cy="NewTodoField"
type="text"
className="todoapp__new-todo"
placeholder="What needs to be done?"
/>
</form>
</header>

<section className="todoapp__main" data-cy="TodoList">
{/* This is a completed todo */}
<div data-cy="Todo" className="todo completed">
<label className="todo__status-label">
<input
data-cy="TodoStatus"
type="checkbox"
className="todo__status"
checked
/>
</label>

<span data-cy="TodoTitle" className="todo__title">
Completed Todo
</span>

{/* Remove button appears only on hover */}
<button type="button" className="todo__remove" data-cy="TodoDelete">
×
</button>
</div>

{/* This todo is an active todo */}
<div data-cy="Todo" className="todo">
<label className="todo__status-label">
<input
data-cy="TodoStatus"
type="checkbox"
className="todo__status"
/>
</label>

<span data-cy="TodoTitle" className="todo__title">
Not Completed Todo
</span>

<button type="button" className="todo__remove" data-cy="TodoDelete">
×
</button>
</div>
<Header />

{/* This todo is being edited */}
<div data-cy="Todo" className="todo">
<label className="todo__status-label">
<input
data-cy="TodoStatus"
type="checkbox"
className="todo__status"
/>
</label>

{/* This form is shown instead of the title and remove button */}
<form>
<input
data-cy="TodoTitleField"
type="text"
className="todo__title-field"
placeholder="Empty todo will be deleted"
value="Todo is being edited now"
/>
</form>
</div>

{/* This todo is in loadind state */}
<div data-cy="Todo" className="todo">
<label className="todo__status-label">
<input
data-cy="TodoStatus"
type="checkbox"
className="todo__status"
/>
</label>

<span data-cy="TodoTitle" className="todo__title">
Todo is being saved now
</span>

<button type="button" className="todo__remove" data-cy="TodoDelete">
×
</button>
</div>
</section>

{/* Hide the footer if there are no todos */}
<footer className="todoapp__footer" data-cy="Footer">
<span className="todo-count" data-cy="TodosCounter">
3 items left
</span>

{/* Active link should have the 'selected' class */}
<nav className="filter" data-cy="Filter">
<a
href="#/"
className="filter__link selected"
data-cy="FilterLinkAll"
>
All
</a>

<a
href="#/active"
className="filter__link"
data-cy="FilterLinkActive"
>
Active
</a>

<a
href="#/completed"
className="filter__link"
data-cy="FilterLinkCompleted"
>
Completed
</a>
</nav>

{/* this button should be disabled if there are no completed todos */}
<button
type="button"
className="todoapp__clear-completed"
data-cy="ClearCompletedButton"
>
Clear completed
</button>
</footer>
<TodoList todosToShow={todosToShow} />
{todoItems.length > 0 && (
<Footer
selectedFilter={selectedFilter}
setSelectedFilter={setSelectedFilter}
/>
)}
</div>
</div>
);
Expand Down
56 changes: 56 additions & 0 deletions src/Components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react';
import { FilterTypes } from '../types/FilterType';
import cn from 'classnames';
import { useTodoContext } from '../Context/TodoProvider';

type FooterProps = {
selectedFilter: FilterTypes;
setSelectedFilter: (filter: FilterTypes) => void;
};

const Footer: React.FC<FooterProps> = ({
selectedFilter,
setSelectedFilter,
}) => {
const { todoItems, clearCompletedTodos } = useTodoContext();

const countItemsLeft = todoItems.reduce((accumulator, value) => {
return accumulator + (value.completed ? 0 : 1);
}, 0);

return (
<footer className="todoapp__footer" data-cy="Footer">
<span className="todo-count" data-cy="TodosCounter">
{countItemsLeft} items left
</span>

<nav className="filter" data-cy="Filter">
{Object.values(FilterTypes).map(filter => (
<a
key={filter}
href={`#/${filter}`}
className={cn('filter__link', {
selected: selectedFilter === filter,
})}
data-cy={`FilterLink${filter.charAt(0).toUpperCase() + filter.slice(1)}`}
onClick={() => setSelectedFilter(filter as FilterTypes)}
>
{filter.charAt(0).toUpperCase() + filter.slice(1)}
</a>
))}
</nav>

<button
type="button"
className="todoapp__clear-completed"
data-cy="ClearCompletedButton"
onClick={() => clearCompletedTodos()}
disabled={!todoItems.some(todo => todo.completed)}
>
Clear completed
</button>
</footer>
);
};

export default Footer;
66 changes: 66 additions & 0 deletions src/Components/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { useState } from 'react';
import cn from 'classnames';
import { useTodoContext } from '../Context/TodoProvider';

const Header: React.FC = () => {
const { todoItems, addTodo, setTodoItems } = useTodoContext();
const [newTodoTitle, setNewTodoTitle] = useState<string>('');

const addTodoFunc = (e: React.FormEvent<HTMLFormElement>, title: string) => {
e.preventDefault();
addTodo(title);
setNewTodoTitle('');
};

const handleToggleAll = async () => {
if (
!todoItems.some(todo => todo.completed) ||
!todoItems.some(todo => !todo.completed)
) {
setTodoItems(
todoItems.map(todo => ({
...todo,
completed: !todo.completed,
})),
);

return;
} else {
setTodoItems(
todoItems.map(todo => ({
...todo,
completed: true,
})),
);
}
};

return (
<header className="todoapp__header">
{todoItems.length > 0 && (
<button
type="button"
className={cn('todoapp__toggle-all', {
active: todoItems.every(todo => todo.completed),
})}
data-cy="ToggleAllButton"
onClick={handleToggleAll}
/>
)}

<form onSubmit={e => addTodoFunc(e, newTodoTitle)}>
<input
data-cy="NewTodoField"
type="text"
className="todoapp__new-todo"
placeholder="What needs to be done?"
value={newTodoTitle}
onChange={e => setNewTodoTitle(e.target.value)}
autoFocus
/>
</form>
</header>
);
};

export default Header;
Loading

0 comments on commit b90c37f

Please sign in to comment.