-
- {/* this button should have `active` class only if all todos are completed */}
-
+
- {/* Add a todo on form submit */}
-
-
-
-
- {/* This is a completed todo */}
-
-
-
-
- Completed Todo
-
-
- {/* Remove button appears only on hover */}
-
-
-
- {/* This todo is an active todo */}
-
-
-
-
- Not Completed Todo
-
-
-
-
-
- {/* This todo is being edited */}
-
-
-
- {/* This form is shown instead of the title and remove button */}
-
-
-
- {/* This todo is in loadind state */}
-
-
-
-
- Todo is being saved now
-
-
-
-
-
+
{/* Hide the footer if there are no todos */}
-
+
);
diff --git a/src/components/TodoFooter.tsx b/src/components/TodoFooter.tsx
new file mode 100644
index 000000000..fc2bc3d80
--- /dev/null
+++ b/src/components/TodoFooter.tsx
@@ -0,0 +1,41 @@
+import React from 'react';
+
+type TodoFooterProps = {};
+
+export const TodoFooter: React.FC = () => {
+ return (
+
+ );
+};
diff --git a/src/components/TodoHeader.tsx b/src/components/TodoHeader.tsx
new file mode 100644
index 000000000..40f05c0c6
--- /dev/null
+++ b/src/components/TodoHeader.tsx
@@ -0,0 +1,32 @@
+import React, { useCallback, useContext } from 'react';
+import { TodoContext } from '../context/TodoContext';
+import { Todo } from '../type';
+
+type TodoHeaderProps = {};
+
+export const TodoHeader: React.FC = () => {
+ const { todos, setTodos } = useContext(TodoContext);
+
+ console.log(todos)
+
+ return (
+
+ {/* this button should have `active` class only if all todos are completed */}
+
+
+ {/* Add a todo on form submit */}
+
+
+ );
+};
diff --git a/src/components/TodoItem.tsx b/src/components/TodoItem.tsx
new file mode 100644
index 000000000..11ca06f99
--- /dev/null
+++ b/src/components/TodoItem.tsx
@@ -0,0 +1,11 @@
+import React from 'react';
+
+type TodoItemProps = {};
+
+export const TodoItem: React.FC = () => {
+ return (
+
+
+
+ );
+};
diff --git a/src/components/TodoList.tsx b/src/components/TodoList.tsx
new file mode 100644
index 000000000..bf762fd80
--- /dev/null
+++ b/src/components/TodoList.tsx
@@ -0,0 +1,90 @@
+import React from 'react';
+
+type TodoListProps = {};
+
+export const TodoList: React.FC = () => {
+ return (
+
+ {/* This is a completed todo */}
+
+
+
+
+ Completed Todo
+
+
+ {/* Remove button appears only on hover */}
+
+
+
+ {/* This todo is an active todo */}
+
+
+
+
+ Not Completed Todo
+
+
+
+
+
+ {/* This todo is being edited */}
+
+
+
+ {/* This form is shown instead of the title and remove button */}
+
+
+
+ {/* This todo is in loadind state */}
+
+
+
+
+ Todo is being saved now
+
+
+
+
+
+ );
+};
diff --git a/src/context/TodoContext.tsx b/src/context/TodoContext.tsx
new file mode 100644
index 000000000..05d997524
--- /dev/null
+++ b/src/context/TodoContext.tsx
@@ -0,0 +1,66 @@
+import React, {
+ createContext,
+ MutableRefObject,
+ useEffect,
+ useReducer,
+ useRef,
+} from 'react';
+import { State } from '../types/State';
+import { Todo } from '../types/Todo';
+import { Filters } from '../enums/TodoFilterEnum';
+
+interface Props {
+ state: State;
+ dispatch: React.Dispatch;
+ inputRef: MutableRefObject;
+}
+
+export const TodoContext = createContext({
+ state: { todos: [], filter: Filters.All },
+ dispatch: () => {},
+ inputRef: { current: null },
+});
+
+type Action =
+ | { type: 'ADD_TODO'; payload: Todo }
+ | { type: 'EDIT_TODO'; payload: Todo }
+ | { type: 'TOGGLE_TODO'; payload: number }
+ | { type: 'DELETE_TODO'; payload: number }
+ | { type: 'FILTER_TODOS'; payload: Filters }
+ | { type: 'DELETE_COMPLETED_TODOS'; payload: number[] }
+ | { type: 'TOGGLE_ALL_TODOS'; payload: number[] };
+
+function reduceTodos(state: State, action: Action): State {
+ switch (action.type) {
+ case 'ADD_TODO':
+ return { ...state, todos: [...state.todos, action.payload] };
+
+ default:
+ return state;
+ }
+}
+
+export const TodoProvider = ({ children }: { children: React.ReactNode }) => {
+ const inputRef = useRef(null);
+
+ const getStoredTodos = () => {
+ const stored = localStorage.getItem('todos');
+
+ return stored ? JSON.parse(stored) : [];
+ };
+
+ const [state, dispatch] = useReducer(reduceTodos, {
+ todos: getStoredTodos(),
+ filter: Filters.All,
+ });
+
+ useEffect(() => {
+ localStorage.setItem('todos', JSON.stringify(state.todos));
+ }, [state.todos]);
+
+ return (
+
+ {children}
+
+ );
+};
\ No newline at end of file
diff --git a/src/enums/TodoFilterEnum.ts b/src/enums/TodoFilterEnum.ts
new file mode 100644
index 000000000..d6f410111
--- /dev/null
+++ b/src/enums/TodoFilterEnum.ts
@@ -0,0 +1,5 @@
+export enum Filters {
+ All = 'All',
+ Active = 'Active',
+ Completed = 'Completed',
+}
\ No newline at end of file
diff --git a/src/index.tsx b/src/index.tsx
index b2c38a17a..a4207b1fc 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -3,7 +3,12 @@ import { createRoot } from 'react-dom/client';
import './styles/index.scss';
import { App } from './App';
+import { TodoProvider } from './context/TodoContext';
const container = document.getElementById('root') as HTMLDivElement;
-createRoot(container).render();
+createRoot(container).render(
+
+
+ ,
+);
diff --git a/src/type.ts b/src/type.ts
new file mode 100644
index 000000000..b624f601a
--- /dev/null
+++ b/src/type.ts
@@ -0,0 +1,5 @@
+export type Todo = {
+ id: number
+ title: string
+ completed: boolean
+}
\ No newline at end of file
diff --git a/src/types/State.ts b/src/types/State.ts
new file mode 100644
index 000000000..0b30a3145
--- /dev/null
+++ b/src/types/State.ts
@@ -0,0 +1,7 @@
+import { Filters } from '../enums/TodoFilterEnum';
+import { Todo } from './Todo';
+
+export interface State {
+ todos: Todo[];
+ filter: Filters;
+}
\ No newline at end of file
diff --git a/src/types/Todo.ts b/src/types/Todo.ts
new file mode 100644
index 000000000..f9e06b381
--- /dev/null
+++ b/src/types/Todo.ts
@@ -0,0 +1,5 @@
+export interface Todo {
+ id: number;
+ title: string;
+ completed: boolean;
+}