diff --git a/.husky/.gitignore b/.husky/.gitignore
new file mode 100644
index 00000000..31354ec1
--- /dev/null
+++ b/.husky/.gitignore
@@ -0,0 +1 @@
+_
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 00000000..9c5e0906
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,4 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+npm run pre:commit
diff --git a/.husky/pre-push b/.husky/pre-push
new file mode 100755
index 00000000..89dd637e
--- /dev/null
+++ b/.husky/pre-push
@@ -0,0 +1,4 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+npm run pre:push
diff --git a/.lintstagedrc.js b/.lintstagedrc.js
new file mode 100644
index 00000000..084f9d12
--- /dev/null
+++ b/.lintstagedrc.js
@@ -0,0 +1,8 @@
+module.exports = {
+ "**/*.js": [
+ "npm run lint:js",
+ "npm run lint:format:check",
+ "npm run test:related",
+ ],
+ "*.{css,scss,html,md,json,yml,yaml}": ["npm run lint:format:check"],
+};
diff --git a/public/index.html b/public/index.html
index aa069f27..5a7b7d11 100644
--- a/public/index.html
+++ b/public/index.html
@@ -3,6 +3,16 @@
+
+
+
+
-
-
- );
+import React, { Component } from "react";
+import { Route } from "react-router-dom";
+import { v4 as uuid } from "uuid";
+
+import { LOCAL_STORAGE_KEY } from "./utils/contants";
+import {
+ getPreviousLocalStorageValues,
+ setLocalStorageValues,
+} from "./utils/methods";
+
+import TodoList from "./components/TodoList";
+
+class App extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ todos: [],
+ darkMode: false,
+ };
+
+ this.handleAddTodo = this.handleAddTodo.bind(this);
+ this.handleMarkTodoAsDone = this.handleMarkTodoAsDone.bind(this);
+ this.handleDeleteTodo = this.handleDeleteTodo.bind(this);
+ this.handleEditTodo = this.handleEditTodo.bind(this);
+ this.handleThemeChange = this.handleThemeChange.bind(this);
+ this.handleClearCompletedTodos = this.handleClearCompletedTodos.bind(this);
+ this.handleIsEditingTodo = this.handleIsEditingTodo.bind(this);
+ }
+
+ componentDidMount() {
+ const previousTodos = getPreviousLocalStorageValues(LOCAL_STORAGE_KEY);
+
+ if (Array.isArray(previousTodos) && previousTodos.length > 0) {
+ this.setState({
+ todos: previousTodos,
+ });
+ }
+ }
+
+ componentDidUpdate() {
+ const { todos } = this.state;
+ setLocalStorageValues(LOCAL_STORAGE_KEY, todos);
+ }
+
+ handleMarkTodoAsDone(todoId) {
+ const { todos } = this.state;
+
+ const updatedTodos = todos.map((todo) => {
+ if (todo.id === todoId) {
+ return {
+ ...todo,
+ done: !todo.done,
+ isEditing: false,
+ };
+ }
+ return todo;
+ });
+
+ this.setState({ todos: updatedTodos });
+ }
+
+ handleDeleteTodo(todoId) {
+ const { todos } = this.state;
+ const filteredTodos = todos.filter((todo) => todo.id !== todoId);
+ this.setState({ todos: filteredTodos });
+ }
+
+ handleAddTodo(text) {
+ this.setState((prevState) => ({
+ todos: [
+ ...prevState.todos,
+ {
+ id: uuid(),
+ text: text,
+ done: false,
+ isEditing: false,
+ },
+ ],
+ }));
+ }
+
+ handleEditTodo(todoId, editedText) {
+ const { todos } = this.state;
+
+ const updatedTodos = todos.map((todo) => {
+ if (todo.id === todoId) {
+ return {
+ ...todo,
+ text: editedText,
+ isEditing: false,
+ };
+ }
+
+ return todo;
+ });
+
+ this.setState({ todos: updatedTodos });
+ }
+
+ handleThemeChange() {
+ this.setState((prevState) => ({
+ darkMode: !prevState.darkMode,
+ }));
+ }
+
+ handleClearCompletedTodos() {
+ const { todos } = this.state;
+ const updatedTodos = todos.filter((todo) => !todo.done);
+ this.setState({ todos: updatedTodos });
+ }
+
+ handleIsEditingTodo(todoId) {
+ const { todos } = this.state;
+
+ const mappedTodos = todos.map((todo) => {
+ if (todo.id === todoId) {
+ return {
+ ...todo,
+ isEditing: true,
+ };
+ }
+
+ return {
+ ...todo,
+ isEditing: false,
+ };
+ });
+
+ this.setState({
+ todos: mappedTodos,
+ });
+ }
+
+ render() {
+ const { todos, darkMode } = this.state;
+ const activeTodos = todos.filter((todo) => !todo.done);
+ const completedTodos = todos.filter((todo) => todo.done);
+ const activeTodosCount = todos.filter((todo) => !todo.done).length;
+
+ return (
+ <>
+ (
+
+ )}
+ />
+ (
+
+ )}
+ />
+ (
+
+ )}
+ />
+ >
+ );
+ }
}
export default App;
diff --git a/src/components/AppFooter/AppFooter.js b/src/components/AppFooter/AppFooter.js
new file mode 100644
index 00000000..85c5f597
--- /dev/null
+++ b/src/components/AppFooter/AppFooter.js
@@ -0,0 +1,83 @@
+import React from "react";
+import { NavLink } from "react-router-dom";
+import classNames from "classnames";
+
+import "./AppFooter.scss";
+
+function AppFooter({ todosLeft, darkMode, handleClearCompletedTodos }) {
+ const darkModeTodosLeft = classNames({
+ "items-left footer-item": true,
+ "footer-item-dark": darkMode,
+ });
+
+ const darkModeClear = classNames({
+ "clear-all-div footer-item clear-button": true,
+ "footer-item-dark": darkMode,
+ });
+
+ const darkModeNavLink = classNames({
+ "nav-link": true,
+ "nav-link-dark": darkMode,
+ });
+
+ const darkModeActive = classNames({
+ active: true,
+ "active-dark": darkMode,
+ });
+
+ return (
+
+ );
+}
+
+export default AppFooter;
diff --git a/src/components/AppFooter/AppFooter.scss b/src/components/AppFooter/AppFooter.scss
new file mode 100644
index 00000000..6cf49f81
--- /dev/null
+++ b/src/components/AppFooter/AppFooter.scss
@@ -0,0 +1,76 @@
+.app-footer {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+
+ @media screen and(min-width: 560px) {
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ font-size: 14px;
+ color: #969696;
+ border-top: 1px solid lightgray;
+}
+
+.footer-item {
+ margin-bottom: 0.5rem;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ @media screen and(min-width: 560px) {
+ margin-bottom: 0;
+ }
+
+ &-dark {
+ color: white;
+ }
+
+ &-dark:hover {
+ font-weight: bold;
+ }
+
+ .navbar-nav {
+ @media screen and(min-width: 560px) {
+ justify-content: space-between;
+ flex-direction: row !important;
+ }
+
+ .active {
+ color: black;
+ font-weight: 700;
+
+ &-dark {
+ color: white;
+ }
+ }
+ }
+
+ .nav-link {
+ @media screen and(min-width: 560px) {
+ padding: 8px;
+ }
+
+ color: #969696;
+
+ &:hover {
+ color: black;
+ font-weight: 700;
+ }
+
+ &-dark:hover {
+ color: white;
+ }
+ }
+}
+
+.clear-button {
+ border: none;
+ padding: 0;
+ background-color: transparent;
+ font-weight: normal;
+ display: inline;
+}
diff --git a/src/components/AppFooter/index.js b/src/components/AppFooter/index.js
new file mode 100644
index 00000000..569644b5
--- /dev/null
+++ b/src/components/AppFooter/index.js
@@ -0,0 +1 @@
+export { default } from "./AppFooter";
diff --git a/src/components/CreateTodo/CreateTodo.js b/src/components/CreateTodo/CreateTodo.js
new file mode 100644
index 00000000..12c209c3
--- /dev/null
+++ b/src/components/CreateTodo/CreateTodo.js
@@ -0,0 +1,96 @@
+import React, { Component } from "react";
+import classNames from "classnames";
+
+import "./CreateTodo.scss";
+
+class CreateTodo extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ text: "",
+ hasError: false,
+ errorMessage: "",
+ };
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.handleTodoInputChange = this.handleTodoInputChange.bind(this);
+ }
+
+ handleSubmit(event) {
+ event.preventDefault();
+ const { text: newText } = this.state;
+ const { handleAddTodo } = this.props;
+
+ if (newText === "") {
+ this.setState({
+ errorMessage: "Please enter a todo name",
+ hasError: true,
+ });
+ } else {
+ this.setState({
+ text: "",
+ errorMessage: "",
+ hasError: false,
+ });
+
+ handleAddTodo(newText);
+ }
+ }
+
+ handleTodoInputChange(event) {
+ this.setState({
+ text: event.target.value,
+ });
+ }
+
+ render() {
+ const { text, errorMessage, hasError } = this.state;
+ const { darkMode } = this.props;
+
+ const backgroundDarkModeClass = classNames({
+ row: true,
+ "create-todo-section": true,
+ "custom-section": true,
+ "custom-section-dark": darkMode,
+ });
+
+ const inputDarkModeClass = classNames({
+ "addtodo-input": true,
+ "addtodo-input-dark": darkMode,
+ });
+
+ return (
+
+
+ {hasError && (
+
+ )}
+
+ );
+ }
+}
+
+export default CreateTodo;
diff --git a/src/components/CreateTodo/CreateTodo.scss b/src/components/CreateTodo/CreateTodo.scss
new file mode 100644
index 00000000..e2d4d754
--- /dev/null
+++ b/src/components/CreateTodo/CreateTodo.scss
@@ -0,0 +1,54 @@
+@use "../../styles/partials/colors";
+
+.create-todo-section {
+ margin: 2rem 0rem;
+ padding: 1.5rem 1rem;
+ width: 100%;
+}
+
+.error-message-wrapper {
+ display: block;
+ margin-top: 1rem;
+ padding: 0.5rem 1rem;
+ background-color: hsl(10, 68%, 91%);
+ color: darkred;
+}
+
+.checkbox-wrapper {
+ margin-right: 20px;
+}
+
+.custom-checkbox {
+ border: none;
+ border: 2px solid #7e76b7;
+ color: #7e76b7;
+ border-radius: 50%;
+ width: 22px;
+ height: 22px;
+
+ i {
+ width: fit-content;
+ height: fit-content;
+ }
+}
+
+form {
+ flex-grow: 1;
+ background-color: transparent;
+}
+
+.addtodo-input {
+ background-color: transparent;
+ flex-grow: 1;
+ border: none;
+ font-size: 15px;
+ transition: 0.4s;
+
+ &-dark {
+ color: white;
+ }
+}
+
+.addtodo-input:focus {
+ outline: none;
+}
diff --git a/src/components/CreateTodo/index.js b/src/components/CreateTodo/index.js
new file mode 100644
index 00000000..fb11201a
--- /dev/null
+++ b/src/components/CreateTodo/index.js
@@ -0,0 +1 @@
+export { default } from "./CreateTodo";
diff --git a/src/components/Layout/Layout.js b/src/components/Layout/Layout.js
new file mode 100644
index 00000000..480de4a9
--- /dev/null
+++ b/src/components/Layout/Layout.js
@@ -0,0 +1,67 @@
+import React from "react";
+import classNames from "classnames";
+
+import CreateTodo from "../CreateTodo";
+import AppFooter from "../AppFooter";
+
+import lightModeImage from "../../img/header-light-mode-background-image.jpeg";
+import darkModeImage from "../../img/header-dark-mode-background-image.jpeg";
+
+function Layout({
+ handleThemeChange,
+ handleAddTodo,
+ darkMode,
+ todosLeft,
+ handleClearCompletedTodos,
+ children,
+}) {
+ const bottomBackgroundClasses = classNames({
+ "bottom-background": true,
+ "bottom-background-dark": darkMode,
+ });
+
+ const todosSectionClasses = classNames({
+ "col todo-list-section custom-section mx-0 px-0 d-flex": true,
+ "custom-section-dark": darkMode,
+ });
+
+ return (
+ <>
+
+
+
+

+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+export default Layout;
diff --git a/src/components/Layout/index.js b/src/components/Layout/index.js
new file mode 100644
index 00000000..d4dca0dc
--- /dev/null
+++ b/src/components/Layout/index.js
@@ -0,0 +1 @@
+export { default } from "./Layout";
diff --git a/src/components/NoTodos/NoTodos.js b/src/components/NoTodos/NoTodos.js
new file mode 100644
index 00000000..c5b389d7
--- /dev/null
+++ b/src/components/NoTodos/NoTodos.js
@@ -0,0 +1,141 @@
+import React from "react";
+
+export default function NoTodos() {
+ return (
+
+ );
+}
diff --git a/src/components/NoTodos/index.js b/src/components/NoTodos/index.js
new file mode 100644
index 00000000..d9736b02
--- /dev/null
+++ b/src/components/NoTodos/index.js
@@ -0,0 +1 @@
+export { default } from "./NoTodos";
diff --git a/src/components/Todo/Todo.js b/src/components/Todo/Todo.js
new file mode 100644
index 00000000..d473ec31
--- /dev/null
+++ b/src/components/Todo/Todo.js
@@ -0,0 +1,145 @@
+import React, { Component, createRef } from "react";
+import classNames from "classnames";
+
+import "./Todo.scss";
+
+class Todo extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ todoText: "",
+ };
+
+ this.inputRef = createRef(null);
+
+ this.handleDoneCheckboxChange = this.handleDoneCheckboxChange.bind(this);
+ this.handleTodoDelete = this.handleTodoDelete.bind(this);
+ this.handleTodoNameChange = this.handleTodoNameChange.bind(this);
+ this.handleTodoSubmit = this.handleTodoSubmit.bind(this);
+ this.handleOpenForm = this.handleOpenForm.bind(this);
+ }
+
+ componentDidMount() {
+ const { text } = this.props;
+
+ this.setState({
+ todoText: text,
+ });
+ }
+
+ componentDidUpdate() {
+ const { isEditing } = this.props;
+
+ if (isEditing && this.inputRef.current) {
+ this.inputRef.current.focus();
+ }
+ }
+
+ handleDoneCheckboxChange(event) {
+ const { id, handleMarkTodoAsDone } = this.props;
+ handleMarkTodoAsDone(id, event);
+ }
+
+ handleTodoDelete() {
+ const { id, handleDeleteTodo } = this.props;
+ handleDeleteTodo(id);
+ }
+
+ handleTodoNameChange(event) {
+ this.setState({
+ todoText: event.target.value,
+ });
+ }
+
+ handleTodoSubmit(event) {
+ event.preventDefault();
+
+ const { id, handleEditTodo } = this.props;
+ const { todoText } = this.state;
+
+ handleEditTodo(id, todoText, event);
+ }
+
+ handleOpenForm() {
+ const { handleIsEditingTodo, id } = this.props;
+ handleIsEditingTodo(id);
+ }
+
+ render() {
+ const { done, darkMode, isEditing } = this.props;
+ const { todoText } = this.state;
+
+ const todoInputClasses = classNames({
+ "todo-item__text": true,
+ "todo-item__text--done": done,
+ "todo-item__text--dark": darkMode,
+ });
+
+ const todoButtonClasses = classNames({
+ "todo-item__text": true,
+ "todo-item__text--done": done,
+ "todo-item__text--dark": darkMode,
+ });
+
+ const closeClasses = classNames({
+ "close uil uil-times": true,
+ "close-dark": darkMode,
+ });
+
+ return (
+
+
+ {isEditing ? (
+
+ ) : (
+
+ )}
+
+
+ );
+ }
+}
+
+export default Todo;
diff --git a/src/components/Todo/Todo.scss b/src/components/Todo/Todo.scss
new file mode 100644
index 00000000..235caa30
--- /dev/null
+++ b/src/components/Todo/Todo.scss
@@ -0,0 +1,104 @@
+@use "../../styles/partials/colors" as c;
+
+.todo-item {
+ list-style-type: none;
+ width: 100%;
+ height: 75px;
+ border-bottom: 1px solid lightgray;
+
+ &__text {
+ background-color: transparent;
+ cursor: pointer;
+ font-size: 15px;
+ width: 95%;
+ margin: 0;
+ outline: none;
+ border: none;
+ text-align: left;
+ padding: 0.5rem;
+
+ &:focus {
+ outline: 2px solid lightgray;
+ }
+
+ &--dark {
+ color: white;
+ }
+
+ &--done {
+ color: rgb(139, 148, 156);
+ text-decoration: line-through;
+ }
+ }
+}
+
+.checkbox-wrapper {
+ margin-right: 20px;
+ position: relative;
+
+ input {
+ position: absolute;
+ opacity: 0;
+ cursor: pointer;
+ height: 100%;
+ width: 100%;
+ }
+}
+
+.custom-checkbox {
+ border: none;
+ border: 2px solid #7e76b7;
+ color: #7e76b7;
+ border-radius: 50%;
+ width: 22px;
+ height: 22px;
+
+ i {
+ width: fit-content;
+ height: fit-content;
+ }
+}
+
+input:checked ~ .custom-checkbox {
+ color: white;
+ border: none;
+
+ background-image: linear-gradient(125deg, #b62b86, #9f589e, #7e76b7, #3a8fd1);
+}
+
+form {
+ flex-grow: 1;
+}
+
+.edit-todo-input {
+ background-color: transparent;
+ cursor: pointer;
+ font-size: 15px;
+ width: 95%;
+ margin: 0;
+ outline: none;
+ border: none;
+ text-align: left;
+ padding: 0.5rem;
+
+ &:focus {
+ outline: 2px solid lightgray;
+ }
+
+ &-dark {
+ color: white;
+ }
+}
+
+button {
+ border: none;
+ background-color: transparent;
+}
+
+.close {
+ font-size: 18px;
+
+ &-dark {
+ color: white;
+ }
+}
diff --git a/src/components/Todo/index.js b/src/components/Todo/index.js
new file mode 100644
index 00000000..012972be
--- /dev/null
+++ b/src/components/Todo/index.js
@@ -0,0 +1 @@
+export { default } from "./Todo";
diff --git a/src/components/TodoList/TodoList.js b/src/components/TodoList/TodoList.js
new file mode 100644
index 00000000..56b95d52
--- /dev/null
+++ b/src/components/TodoList/TodoList.js
@@ -0,0 +1,55 @@
+import React from "react";
+
+import "./TodoList.scss";
+import Todo from "../Todo";
+import NoTodos from "../NoTodos";
+import Layout from "../Layout";
+
+function TodoList({
+ todos,
+ hasTodos,
+ darkMode,
+ handleMarkTodoAsDone,
+ handleDeleteTodo,
+ handleEditTodo,
+ handleClearCompletedTodos,
+ todosLeft,
+ handleIsEditingTodo,
+ handleThemeChange,
+ handleAddTodo,
+}) {
+ return (
+
+
+ {!hasTodos ? (
+
+
+
+ ) : (
+ todos.map((todo) => (
+
+ ))
+ )}
+
+
+ );
+}
+
+export default TodoList;
diff --git a/src/components/TodoList/TodoList.scss b/src/components/TodoList/TodoList.scss
new file mode 100644
index 00000000..9bf55157
--- /dev/null
+++ b/src/components/TodoList/TodoList.scss
@@ -0,0 +1,29 @@
+.todo-list-section {
+ display: flex;
+ flex-direction: column;
+
+ @media screen and(min-width: 560px) {
+ max-height: auto;
+ min-height: 400px;
+ }
+}
+
+.todos-list {
+ max-height: 330px;
+ margin-bottom: 0;
+ overflow-y: auto;
+
+ @media screen and(min-width: 560px) {
+ flex-grow: 1;
+ }
+}
+
+.no-todos {
+ width: 100%;
+ height: 100%;
+
+ svg {
+ width: 60%;
+ height: auto;
+ }
+}
diff --git a/src/components/TodoList/index.js b/src/components/TodoList/index.js
new file mode 100644
index 00000000..8d439448
--- /dev/null
+++ b/src/components/TodoList/index.js
@@ -0,0 +1 @@
+export { default } from "./TodoList";
diff --git a/src/img/header-dark-mode-background-image.jpeg b/src/img/header-dark-mode-background-image.jpeg
new file mode 100644
index 00000000..6b87b6f3
Binary files /dev/null and b/src/img/header-dark-mode-background-image.jpeg differ
diff --git a/src/img/header-light-mode-background-image.jpeg b/src/img/header-light-mode-background-image.jpeg
new file mode 100644
index 00000000..2e14b759
Binary files /dev/null and b/src/img/header-light-mode-background-image.jpeg differ
diff --git a/src/index.js b/src/index.js
index 19bb154c..d09e8d51 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,14 +1,18 @@
import React from "react";
import ReactDOM from "react-dom";
+import { BrowserRouter } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
+import "./styles/styles.scss";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
ReactDOM.render(
-
+
+
+
,
document.getElementById("root"),
);
diff --git a/src/styles/partials/_colors.scss b/src/styles/partials/_colors.scss
new file mode 100644
index 00000000..1f525fe0
--- /dev/null
+++ b/src/styles/partials/_colors.scss
@@ -0,0 +1,9 @@
+/* -------------------------------------------------------------------------- */
+/* LIGHT MODE */
+/* -------------------------------------------------------------------------- */
+$lightBgColor: #fafafa;
+
+/* -------------------------------------------------------------------------- */
+/* DARK MODE */
+/* -------------------------------------------------------------------------- */
+$darkBgColor: #121818;
diff --git a/src/styles/partials/_typography.scss b/src/styles/partials/_typography.scss
new file mode 100644
index 00000000..825ceb51
--- /dev/null
+++ b/src/styles/partials/_typography.scss
@@ -0,0 +1 @@
+$mainFont: "Work Sans", sans-serif;
diff --git a/src/styles/styles.scss b/src/styles/styles.scss
new file mode 100644
index 00000000..9b60bf3e
--- /dev/null
+++ b/src/styles/styles.scss
@@ -0,0 +1,116 @@
+@use "./partials/colors" as c;
+@use "./partials/typography" as t;
+
+* {
+ padding: 0;
+ margin: 0;
+ box-sizing: border-box;
+ font-family: "Work Sans", sans-serif;
+ transition: 0.4s;
+
+ ::placeholder {
+ color: lightgray;
+ opacity: 1;
+ }
+
+ :disabled {
+ background-color: transparent;
+ }
+}
+
+body {
+ height: 100vh;
+}
+
+#root {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+.general-background {
+ height: 100vh;
+}
+
+.top-background {
+ height: 40%;
+ position: relative;
+}
+
+.gradient {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ background-image: linear-gradient(90deg, #b62b86, #9f589e, #7e76b7, #3a8fd1);
+
+ mix-blend-mode: multiply;
+ opacity: 85%;
+ z-index: 1;
+}
+
+.img-background {
+ object-fit: cover;
+ height: 100%;
+ width: 100%;
+}
+
+.bottom-background {
+ background-color: c.$lightBgColor;
+ flex-grow: 1;
+
+ &-dark {
+ background-color: c.$darkBgColor;
+ }
+}
+
+.main-container {
+ position: absolute;
+ z-index: 1;
+ height: fit-content;
+ margin-bottom: 110px;
+
+ @media screen and (min-width: 1024px) {
+ width: 100%;
+ max-width: 640px;
+ }
+}
+
+.main-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ color: white;
+
+ &-title {
+ font-weight: 300;
+ }
+
+ button {
+ background: none;
+ outline: none;
+ border: none;
+ color: inherit;
+
+ i {
+ cursor: pointer;
+ font-size: 24px;
+ }
+ }
+}
+
+.custom-section {
+ border-radius: 5px;
+ -moz-box-shadow: 0px 0px 30px rgba(68, 68, 68, 0.6);
+ -webkit-box-shadow: 0px 0px 30px rgba(68, 68, 68, 0.6);
+ box-shadow: 0px 0px 30px rgba(68, 68, 68, 0.6);
+ background-color: white;
+
+ &-dark {
+ color: white;
+ border: 1px solid white;
+ box-shadow: none;
+ background-color: c.$darkBgColor;
+ }
+}
diff --git a/src/utils/contants.js b/src/utils/contants.js
new file mode 100644
index 00000000..8f5b1dc9
--- /dev/null
+++ b/src/utils/contants.js
@@ -0,0 +1 @@
+export const LOCAL_STORAGE_KEY = "reactjs-todo-list";
diff --git a/src/utils/methods.js b/src/utils/methods.js
new file mode 100644
index 00000000..efc83e51
--- /dev/null
+++ b/src/utils/methods.js
@@ -0,0 +1,18 @@
+export function getPreviousLocalStorageValues(localStorageKey) {
+ const previousTodos = localStorage.getItem(localStorageKey);
+
+ // If there are no todos
+ if (!previousTodos) {
+ return [];
+ }
+ // If there are previous todos
+ try {
+ return JSON.parse(previousTodos);
+ } catch (error) {
+ return [];
+ }
+}
+
+export function setLocalStorageValues(localStorageKey, data) {
+ localStorage.setItem(localStorageKey, JSON.stringify(data));
+}