Skip to content

Commit

Permalink
Solution for fix2
Browse files Browse the repository at this point in the history
  • Loading branch information
OksanaShvets1 committed Nov 22, 2023
1 parent 3dec94e commit 4dd297a
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 0 deletions.
67 changes: 67 additions & 0 deletions src/components/AddTodo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* eslint-disable no-console */
import React, {
useState, useEffect, ChangeEvent, useContext,
} from 'react';
import { TodoContext } from '../context/TodoContext';

export const AddTodo: React.FC = () => {
const [title, setTitle] = useState('');
const { todos, setTodos } = useContext(TodoContext);
const [, setErrorMessage] = useState<string | null>(null);

const handleAddTodo = (e: React.KeyboardEvent<HTMLInputElement>) => {
console.log('start');
if (e.key === 'Enter') {
e.preventDefault();
}

console.log('next');
console.log(title);

if (title.trim() === '') {
setErrorMessage('Field can not be blank');
console.log('emty');
} else {
console.log('else');

setTodos((prevTodos) => [
...prevTodos,
{ id: new Date().getTime(), title, completed: false },
]);
setTitle('');
setErrorMessage(null);
}
};

useEffect(() => {
console.log('Todos updated:', todos);

localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]);

const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
setTitle(e.target.value);
};

const handleKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => {
console.log('Key pressed:', event.key);

if (event.key === 'Enter') {
handleAddTodo(event);
}
};

return (
<form>
<input
type="text"
data-cy="createTodo"
className="new-todo"
placeholder="What needs to be done?"
onKeyDown={handleKeyPress}
value={title}
onChange={handleInputChange}
/>
</form>
);
};
93 changes: 93 additions & 0 deletions src/components/TodoItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React, {
useContext, useState, useEffect, useRef, ChangeEvent,
} from 'react';
import { TodoContext } from '../context/TodoContext';
import { Todo } from '../types/Todo';

type TodoItemProps = {
todo: Todo;
};

export const TodoItem: React.FC<TodoItemProps> = ({ todo }) => {
const { todos, setTodos } = useContext(TodoContext);
const [isEditing, setEditing] = useState(false);
const [editedTitle, setEditedTitle] = useState(todo.title);
const inputRef = useRef<HTMLInputElement>(null);

const completeTodo = (e: ChangeEvent<HTMLInputElement>) => {
const updatedTodos = todos.map((item: Todo) => (item.id === todo.id
? { ...item, completed: e.target.checked } : item));

setTodos(updatedTodos);
};

const deleteTodo = () => {
const updatedTodos = todos.filter((item) => item.id !== todo.id);

setTodos(updatedTodos);
};

const handleDoubleClick = () => {
setEditing(true);
};

const handleBlur = () => {
if (editedTitle.trim() === '') {
deleteTodo();
} else {
const updatedTodos = todos.map((item) => (item.id === todo.id
? { ...item, title: editedTitle.trim() } : item));

setTodos(updatedTodos);
}

setEditing(false);
};

const handleKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Escape') {
setEditedTitle(todo.title);
setEditing(false);
} else if (e.key === 'Enter') {
handleBlur();
}
};

useEffect(() => {
if (isEditing && inputRef.current) {
inputRef.current.focus();
}
}, [isEditing]);

return (
<li className={`todo-item ${todo.completed ? 'completed' : ''} ${isEditing ? 'editing' : ''}`}>
<div className="view">
<input
type="checkbox"
checked={todo.completed}
onChange={completeTodo}
className="toggle"
id={`todo-toggle-${todo.id}`}
/>
<label onDoubleClick={handleDoubleClick}>{todo.title}</label>
<button
onClick={deleteTodo}
className="destroy"
type="button"
aria-label="Delete Todo"
/>

</div>
{isEditing && (
<input
ref={inputRef}
className="edit"
value={editedTitle}
onChange={(e) => setEditedTitle(e.target.value)}
onBlur={handleBlur}
onKeyUp={handleKeyUp}
/>
)}
</li>
);
};
20 changes: 20 additions & 0 deletions src/components/TodoList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { TodoItem } from './TodoItem';
import { Todo } from '../types/Todo';

type Props = {
todos: Todo[];
};

export const TodoList: React.FC<Props> = ({ todos }) => {
// eslint-disable-next-line no-console
console.log('Todos in TodoList:', todos);

return (
<ul className="todo-list" data-cy="todoList">
{todos.map((todo) => (
<TodoItem key={todo.id} todo={todo} />
))}
</ul>
);
};
30 changes: 30 additions & 0 deletions src/context/TodoContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, { useState, ReactNode, useMemo } from 'react';
import { Todo } from '../types/Todo';

const getTodos = JSON.parse(localStorage.getItem('todos') || '[]') as Todo[];

export const TodoContext = React.createContext<{
todos: Todo[];
setTodos: React.Dispatch<React.SetStateAction<Todo[]>>;
}>({
todos: getTodos,
setTodos: () => {},
});

interface TodoProviderProps {
children: ReactNode;
}

export const TodoProvider: React.FC<TodoProviderProps> = ({ children }) => {
const [todos, setTodos] = useState(getTodos);
const value = useMemo(() => ({
todos,
setTodos,
}), [todos]);

return (
<TodoContext.Provider value={value}>
{children}
</TodoContext.Provider>
);
};
5 changes: 5 additions & 0 deletions src/types/Todo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type Todo = {
id: number;
title: string;
completed: boolean;
};

0 comments on commit 4dd297a

Please sign in to comment.