diff --git a/.eslintrc.js b/.eslintrc.js
index 17514e75..4178d12e 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -49,7 +49,12 @@ module.exports = {
},
],
rules: {
- "prettier/prettier": "error",
+ "prettier/prettier": [
+ "error",
+ {
+ endOfLine: "auto",
+ },
+ ],
"react/jsx-filename-extension": "off",
"import/prefer-default-export": "off",
"prefer-destructuring": "off",
diff --git a/package-lock.json b/package-lock.json
index a58d3d17..80659b95 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1310,6 +1310,51 @@
}
}
},
+ "@fortawesome/fontawesome-common-types": {
+ "version": "0.2.35",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.35.tgz",
+ "integrity": "sha512-IHUfxSEDS9dDGqYwIW7wTN6tn/O8E0n5PcAHz9cAaBoZw6UpG20IG/YM3NNLaGPwPqgjBAFjIURzqoQs3rrtuw=="
+ },
+ "@fortawesome/fontawesome-svg-core": {
+ "version": "1.2.35",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.35.tgz",
+ "integrity": "sha512-uLEXifXIL7hnh2sNZQrIJWNol7cTVIzwI+4qcBIq9QWaZqUblm0IDrtSqbNg+3SQf8SMGHkiSigD++rHmCHjBg==",
+ "requires": {
+ "@fortawesome/fontawesome-common-types": "^0.2.35"
+ }
+ },
+ "@fortawesome/free-brands-svg-icons": {
+ "version": "5.15.3",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.3.tgz",
+ "integrity": "sha512-1hirPcbjj72ZJtFvdnXGPbAbpn3Ox6mH3g5STbANFp3vGSiE5u5ingAKV06mK6ZVqNYxUPlh4DlTnaIvLtF2kw==",
+ "requires": {
+ "@fortawesome/fontawesome-common-types": "^0.2.35"
+ }
+ },
+ "@fortawesome/free-regular-svg-icons": {
+ "version": "5.15.3",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.3.tgz",
+ "integrity": "sha512-q4/p8Xehy9qiVTdDWHL4Z+o5PCLRChePGZRTXkl+/Z7erDVL8VcZUuqzJjs6gUz6czss4VIPBRdCz6wP37/zMQ==",
+ "requires": {
+ "@fortawesome/fontawesome-common-types": "^0.2.35"
+ }
+ },
+ "@fortawesome/free-solid-svg-icons": {
+ "version": "5.15.3",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.3.tgz",
+ "integrity": "sha512-XPeeu1IlGYqz4VWGRAT5ukNMd4VHUEEJ7ysZ7pSSgaEtNvSo+FLurybGJVmiqkQdK50OkSja2bfZXOeyMGRD8Q==",
+ "requires": {
+ "@fortawesome/fontawesome-common-types": "^0.2.35"
+ }
+ },
+ "@fortawesome/react-fontawesome": {
+ "version": "0.1.14",
+ "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.14.tgz",
+ "integrity": "sha512-4wqNb0gRLVaBm/h+lGe8UfPPivcbuJ6ecI4hIgW0LjI7kzpYB9FkN0L9apbVzg+lsBdcTf0AlBtODjcSX5mmKA==",
+ "requires": {
+ "prop-types": "^15.7.2"
+ }
+ },
"@hapi/address": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
@@ -2140,6 +2185,11 @@
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
},
+ "@types/lodash": {
+ "version": "4.14.169",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.169.tgz",
+ "integrity": "sha512-DvmZHoHTFJ8zhVYwCLWbQ7uAbYQEk52Ev2/ZiQ7Y7gQGeV9pjBqjnQpECMHfKS1rCYAhMI7LHVxwyZLZinJgdw=="
+ },
"@types/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz",
@@ -6937,6 +6987,27 @@
"mime-types": "^2.1.12"
}
},
+ "formik": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/formik/-/formik-2.2.7.tgz",
+ "integrity": "sha512-j4cso6QL90T8hJWgU29GYsBQuj1vynBfrcURyK91KshArvS5CLoxUkP52hKc3wVpGFACd0uWEJo7y30ZOTzc5g==",
+ "requires": {
+ "deepmerge": "^2.1.1",
+ "hoist-non-react-statics": "^3.3.0",
+ "lodash": "^4.17.14",
+ "lodash-es": "^4.17.14",
+ "react-fast-compare": "^2.0.1",
+ "tiny-warning": "^1.0.2",
+ "tslib": "^1.10.0"
+ },
+ "dependencies": {
+ "deepmerge": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
+ "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
+ }
+ }
+ },
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@@ -9433,6 +9504,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
+ "lodash-es": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
+ },
"lodash._reinterpolate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
@@ -9914,6 +9990,11 @@
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
"optional": true
},
+ "nanoclone": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz",
+ "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA=="
+ },
"nanoid": {
"version": "3.1.23",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz",
@@ -11938,6 +12019,11 @@
}
}
},
+ "property-expr": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.4.tgz",
+ "integrity": "sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg=="
+ },
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
@@ -12333,6 +12419,11 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
"integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
},
+ "react-fast-compare": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
+ "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
+ },
"react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@@ -14793,6 +14884,11 @@
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
+ "toposort": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
+ "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA="
+ },
"tough-cookie": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
@@ -15267,8 +15363,7 @@
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
- "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
- "optional": true
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
"v8-compile-cache": {
"version": "2.3.0",
@@ -16880,6 +16975,20 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
+ },
+ "yup": {
+ "version": "0.32.9",
+ "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.9.tgz",
+ "integrity": "sha512-Ci1qN+i2H0XpY7syDQ0k5zKQ/DoxO0LzPg8PAR/X4Mpj6DqaeCoIYEEjDJwhArh3Fa7GWbQQVDZKeXYlSH4JMg==",
+ "requires": {
+ "@babel/runtime": "^7.10.5",
+ "@types/lodash": "^4.14.165",
+ "lodash": "^4.17.20",
+ "lodash-es": "^4.17.15",
+ "nanoclone": "^0.2.1",
+ "property-expr": "^2.0.4",
+ "toposort": "^2.0.2"
+ }
}
}
}
diff --git a/package.json b/package.json
index 326aeb06..b984df4e 100644
--- a/package.json
+++ b/package.json
@@ -12,10 +12,16 @@
"private": true,
"main": "src/index.js",
"dependencies": {
+ "@fortawesome/fontawesome-svg-core": "^1.2.35",
+ "@fortawesome/free-brands-svg-icons": "^5.15.3",
+ "@fortawesome/free-regular-svg-icons": "^5.15.3",
+ "@fortawesome/free-solid-svg-icons": "^5.15.3",
+ "@fortawesome/react-fontawesome": "^0.1.14",
"@testing-library/jest-dom": "^5.12.0",
"@testing-library/react": "^11.2.6",
"@testing-library/user-event": "^13.1.5",
"bootstrap": "^4.6.0",
+ "formik": "^2.2.7",
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@@ -25,7 +31,9 @@
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"sass": "^1.32.11",
- "web-vitals": "^1.1.1"
+ "uuid": "^8.3.2",
+ "web-vitals": "^1.1.1",
+ "yup": "^0.32.9"
},
"devDependencies": {
"@babel/core": "^7.13.16",
diff --git a/src/App.js b/src/App.js
index de524524..f8f38a8e 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,15 +1,321 @@
-import React from "react";
-
-function App() {
- return (
-
-
-
- );
+import React, { Component } from "react";
+import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
+import "./_text.scss";
+import "./styles.scss";
+import "./components/Footer/Footer.scss";
+
+import Header from "./components/Header";
+import NewTodo from "./components/NewTodo";
+import TodoList from "./components/todolist";
+
+import * as api from "./api";
+
+// import * as api from "./api";
+
+const LOCAL_STORAGE_KEY = "react-todo-app";
+
+function loadLocalStorageData() {
+ const prevItems = localStorage.getItem(LOCAL_STORAGE_KEY);
+
+ if (!prevItems) {
+ return null;
+ }
+
+ try {
+ return JSON.parse(prevItems);
+ } catch (error) {
+ return null;
+ }
+}
+
+class App extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ todos: [],
+ };
+ this.handleIsActive = this.handleIsActive.bind(this);
+ this.saveNewTodo = this.saveNewTodo.bind(this);
+ this.clearCompleted = this.clearCompleted.bind(this);
+ this.deleteTodo = this.deleteTodo.bind(this);
+ this.handleIsEdit = this.handleIsEdit.bind(this);
+ this.editTodo = this.editTodo.bind(this);
+ this.displayHashtag = this.displayHashtag.bind(this);
+ this.changeHashtag = this.changeHashtag.bind(this);
+ this.toggleDarkMode = this.toggleDarkMode.bind(this);
+ }
+
+ componentDidMount() {
+ const prevItems = loadLocalStorageData();
+
+ if (!prevItems) {
+ this.setState({
+ todos: [],
+ });
+
+ api.getTodos().then((data) => {
+ this.setState({
+ todos: data,
+ });
+ });
+ return;
+ }
+
+ this.setState({
+ darkMode: prevItems.darkMode,
+ todos: prevItems.todos,
+ });
+ }
+
+ componentDidUpdate() {
+ const { todos, darkMode } = this.state;
+ localStorage.setItem(
+ LOCAL_STORAGE_KEY,
+ JSON.stringify({ todos, darkMode }),
+ );
+ }
+
+ handleIsActive(todosId) {
+ const { todos } = this.state;
+
+ const updatedTodos = todos.map((todo) => {
+ if (todo.id === todosId) {
+ return {
+ ...todo,
+ isActive: !todo.isActive,
+ };
+ }
+
+ return todo;
+ });
+
+ this.setState({ todos: updatedTodos });
+ }
+
+ handleIsEdit(todoId) {
+ const { todos } = this.state;
+
+ const updatedTodos = todos.map((todo) => {
+ if (todo.id === todoId) {
+ return {
+ ...todo,
+ isEdit: !todo.isEdit,
+ };
+ }
+
+ return todo;
+ });
+
+ this.setState({ todos: updatedTodos });
+ }
+
+ saveNewTodo(newTodo) {
+ this.setState((prevState) => ({
+ todos: [newTodo, ...prevState.todos],
+ }));
+ }
+
+ deleteTodo(todoId) {
+ const { todos } = this.state;
+ const todoDelete = todos.filter((todo) => todo.id !== todoId);
+ this.setState(() => ({
+ todos: todoDelete,
+ }));
+ }
+
+ editTodo(todoId, content) {
+ const { todos } = this.state;
+ const updatedTodos = todos.map((todo) => {
+ if (todoId === todo.id) {
+ return {
+ ...todo,
+ content: content,
+ };
+ }
+ return todo;
+ });
+
+ this.setState({ todos: updatedTodos });
+ }
+
+ clearCompleted() {
+ const { todos } = this.state;
+ const newTodos = todos.filter((todo) => todo.isActive === false);
+ this.setState(() => ({
+ todos: newTodos,
+ }));
+ }
+
+ displayHashtag(todoId) {
+ const { todos } = this.state;
+
+ const updatedTodos = todos.map((todo) => {
+ if (todo.id === todoId) {
+ return {
+ ...todo,
+ hashtagDisplayed: !todo.hashtagDisplayed,
+ };
+ }
+
+ return todo;
+ });
+
+ this.setState({ todos: updatedTodos });
+ }
+
+ changeHashtag(todoId, color) {
+ const { todos } = this.state;
+
+ const updatedTodos = todos.map((todo) => {
+ let updateColor;
+ if (todo.id === todoId) {
+ if (color === todo.hashtag) {
+ updateColor = "";
+ } else {
+ updateColor = color;
+ }
+ return {
+ ...todo,
+ hashtag: updateColor,
+ };
+ }
+
+ return todo;
+ });
+
+ this.setState({ todos: updatedTodos });
+ }
+
+ toggleDarkMode() {
+ const { darkMode } = this.state;
+ // eslint-disable-next-line
+ console.log({ darkMode });
+ this.setState({ darkMode: !darkMode });
+ }
+
+ render() {
+ const { todos } = this.state;
+ const completed = todos.filter((todo) => todo.isActive === true);
+ const active = todos.filter((todo) => todo.isActive === false);
+ const { darkMode } = this.state;
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+ }
}
export default App;
diff --git a/src/_colors.scss b/src/_colors.scss
new file mode 100644
index 00000000..b1f02f03
--- /dev/null
+++ b/src/_colors.scss
@@ -0,0 +1,2 @@
+$body-background: rgb(255, 255, 255);
+$main-background: rgb(255, 255, 255);
diff --git a/src/_text.scss b/src/_text.scss
new file mode 100644
index 00000000..a91b0802
--- /dev/null
+++ b/src/_text.scss
@@ -0,0 +1,30 @@
+@import url("https://fonts.googleapis.com/css2?family=Signika&display=swap");
+
+.text__normal--check {
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 18px;
+ color: rgb(161, 161, 161);
+}
+.text__normal {
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 18px;
+ color: rgb(66, 66, 66);
+}
+
+.text__small {
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 11px;
+ color: rgb(88, 88, 88);
+ cursor: pointer;
+
+ &.dark {
+ color: #9499e9;
+ }
+}
+.text__small:hover {
+ color: rgb(165, 165, 165);
+
+ &.dark {
+ color: #44488d;
+ }
+}
diff --git a/src/api/getTodos.js b/src/api/getTodos.js
new file mode 100644
index 00000000..a263d908
--- /dev/null
+++ b/src/api/getTodos.js
@@ -0,0 +1,15 @@
+import todos from "./todos";
+
+function getTodos(fail = false) {
+ return new Promise((res, rej) => {
+ setTimeout(() => {
+ if (fail) {
+ rej(new Error("Failed to fetch"));
+ }
+
+ res(todos);
+ }, 1000);
+ });
+}
+
+export { getTodos };
diff --git a/src/api/index.js b/src/api/index.js
new file mode 100644
index 00000000..e0d8be26
--- /dev/null
+++ b/src/api/index.js
@@ -0,0 +1 @@
+export * from "./getTodos";
diff --git a/src/api/todos.js b/src/api/todos.js
new file mode 100644
index 00000000..60fd4f26
--- /dev/null
+++ b/src/api/todos.js
@@ -0,0 +1,68 @@
+const todos = [
+ {
+ id: "65d6b269-209b-50ac-a566-cjha334aa6f0",
+ isActive: true,
+ isEdit: false,
+ hashtagDisplayed: false,
+ hashtag: "green",
+ content: "Replace .test from styles.scss",
+ },
+ {
+ id: "fbabb4ce-823f-5790-5687-35819d4be380",
+ isActive: true,
+ isEdit: false,
+ hashtagDisplayed: false,
+ hashtag: "red",
+ content: "Fix localStorage and componentDidUpdate",
+ },
+ {
+ id: "5e88d7ab-ecdd-5a92-uy4a-7391a4709994",
+ isActive: false,
+ isEdit: false,
+ hashtagDisplayed: false,
+ hashtag: "red",
+ content: "Prepare everything for DB implementation",
+ },
+ {
+ id: "3ca3c877-0d1c-585e-8we6-e19a97392c93",
+ isActive: false,
+ isEdit: false,
+ hashtagDisplayed: false,
+ hashtag: "yellow",
+ content: "Ask for a beer helping",
+ },
+ {
+ id: "208c1e43-c945-58er-9469-19d9229fded4",
+ isActive: false,
+ isEdit: false,
+ hashtagDisplayed: false,
+ hashtag: "red",
+ content: "Siesta time",
+ },
+ {
+ id: "10691cf1-28ce-fgc7-8eac-812e8eb623cb",
+ isActive: true,
+ isEdit: false,
+ hashtagDisplayed: false,
+ hashtag: "green",
+ content: "Work work work",
+ },
+ {
+ id: "208c1e43-c945-5876-9469-19d9229fded4",
+ isActive: false,
+ isEdit: false,
+ hashtagDisplayed: false,
+ hashtag: "red",
+ content: "Siesta time",
+ },
+ {
+ id: "10691cf1-28ce-5ec7-8e54-812e8eb623cb",
+ isActive: true,
+ isEdit: false,
+ hashtagDisplayed: false,
+ hashtag: "green",
+ content: "Work work work",
+ },
+];
+
+export default todos;
diff --git a/src/components/Footer/Footer.scss b/src/components/Footer/Footer.scss
new file mode 100644
index 00000000..739fa675
--- /dev/null
+++ b/src/components/Footer/Footer.scss
@@ -0,0 +1,14 @@
+footer {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-content: center;
+ padding: 0.8rem;
+}
+
+button {
+ border: none;
+ background-color: transparent;
+ margin: 0 0.5rem;
+ cursor: pointer;
+}
diff --git a/src/components/Hashtag/Hashtag.js b/src/components/Hashtag/Hashtag.js
new file mode 100644
index 00000000..0dc19113
--- /dev/null
+++ b/src/components/Hashtag/Hashtag.js
@@ -0,0 +1,45 @@
+import React from "react";
+
+import "./Hashtag.scss";
+
+function Hashtag({ id, hashtagDisplayed, changeHashtag }) {
+ function changeRed() {
+ changeHashtag(id, "red");
+ }
+ function changeYellow() {
+ changeHashtag(id, "yellow");
+ }
+ function changeGreen() {
+ changeHashtag(id, "green");
+ }
+
+ return (
+
+
+
+
+
+ );
+}
+
+export default Hashtag;
diff --git a/src/components/Hashtag/Hashtag.scss b/src/components/Hashtag/Hashtag.scss
new file mode 100644
index 00000000..c5e04bb4
--- /dev/null
+++ b/src/components/Hashtag/Hashtag.scss
@@ -0,0 +1,41 @@
+.container__hashtag {
+ width: inherit;
+ display: none;
+ padding: 0;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-evenly;
+ cursor: pointer;
+ padding: 0.5rem;
+ transition: display 1s ease;
+}
+
+.display-flex {
+ display: flex;
+}
+
+.container__hashtag > div {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+.container__hashtag > div > div {
+ width: 40px;
+ height: 20px;
+ border-radius: 4px;
+ margin: 0 0.5rem;
+ //align-self: stretch;
+}
+
+.hashtag {
+ align-self: stretch;
+}
+
+.hashtagButton {
+ width: 20%;
+ color: white;
+ font-weight: bold;
+ padding: 0.2rem 0.5rem;
+ border-radius: 0.5rem;
+}
diff --git a/src/components/Hashtag/index.js b/src/components/Hashtag/index.js
new file mode 100644
index 00000000..5a3f89c3
--- /dev/null
+++ b/src/components/Hashtag/index.js
@@ -0,0 +1 @@
+export { default } from "./Hashtag";
diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js
new file mode 100644
index 00000000..6a1cbc53
--- /dev/null
+++ b/src/components/Header/Header.js
@@ -0,0 +1,21 @@
+import React from "react";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faMoon } from "@fortawesome/free-solid-svg-icons";
+import "./Header.scss";
+
+export default function Header({ toggleDarkMode, darkMode }) {
+ function onDarkMode() {
+ toggleDarkMode();
+ }
+ return (
+
+ );
+}
diff --git a/src/components/Header/Header.scss b/src/components/Header/Header.scss
new file mode 100644
index 00000000..faa4f18d
--- /dev/null
+++ b/src/components/Header/Header.scss
@@ -0,0 +1,34 @@
+.header {
+ width: 100%;
+ display: flex;
+ color: white;
+ font-size: 3rem;
+ font-weight: bold;
+ margin-bottom: 3rem;
+
+ .title {
+ width: 90%;
+ letter-spacing: 0.8rem;
+
+ &.dark {
+ color: #151a80;
+ }
+ }
+
+ .dark-mode {
+ display: flex;
+ width: 10%;
+ justify-content: center;
+ align-items: center;
+ transform: scale(3);
+ }
+}
+
+.moon {
+ transform: rotateY(180deg);
+ color: white;
+
+ &.dark {
+ color: #151a80;
+ }
+}
diff --git a/src/components/Header/index.js b/src/components/Header/index.js
new file mode 100644
index 00000000..2764567d
--- /dev/null
+++ b/src/components/Header/index.js
@@ -0,0 +1 @@
+export { default } from "./Header";
diff --git a/src/components/NewTodo/NewTodo.js b/src/components/NewTodo/NewTodo.js
new file mode 100644
index 00000000..a925fad5
--- /dev/null
+++ b/src/components/NewTodo/NewTodo.js
@@ -0,0 +1,86 @@
+import React from "react";
+import { v4 as uuid } from "uuid";
+import { Formik } from "formik";
+
+import "./NewTodo.scss";
+
+import newTodoSchema from "./NewTodoSchema";
+
+import NewTodoInput from "../NewTodoInput";
+
+function addTodoDetails(todo) {
+ return {
+ id: uuid(),
+ ...todo,
+ isActive: false,
+ isEdit: false,
+ hashtagDisplayed: false,
+ hashtag: "",
+ };
+}
+
+export default function NewTodo({
+ saveNewTodo,
+ editTodo,
+ id,
+ content,
+ darkMode,
+}) {
+ function onSave(values) {
+ editTodo(id, values.target.value);
+ }
+ const onSubmit = async (
+ values,
+ { setSubmitting, setErrors, setStatus, resetForm },
+ ) => {
+ try {
+ await saveNewTodo(addTodoDetails(values));
+ resetForm({});
+ setStatus({ success: true });
+ } catch (error) {
+ setStatus({ success: false });
+ setSubmitting(false);
+ setErrors({ submit: error.message });
+ }
+ };
+ return (
+
+ {({
+ handleChange,
+ handleBlur,
+ handleSubmit,
+ errors,
+ values,
+ touched,
+ }) => (
+
+ )}
+
+ );
+}
diff --git a/src/components/NewTodo/NewTodo.scss b/src/components/NewTodo/NewTodo.scss
new file mode 100644
index 00000000..331a2609
--- /dev/null
+++ b/src/components/NewTodo/NewTodo.scss
@@ -0,0 +1,26 @@
+.new-todo {
+ background-color: white;
+ border-radius: 8px;
+ display: flex;
+ align-items: center;
+ padding: 1rem;
+ width: 100%;
+ margin-bottom: 2rem;
+ justify-content: center;
+ box-shadow: 0px 5px 20px 3px rgba(128, 128, 128, 0.432);
+
+ &.dark {
+ background-color: #121669;
+ color: white;
+ box-shadow: 0px 5px 20px 3px #535690;
+ }
+}
+
+.new-todo-form {
+ width: 95%;
+
+ &.dark {
+ background-color: #121669;
+ color: white;
+ }
+}
diff --git a/src/components/NewTodo/NewTodoSchema.js b/src/components/NewTodo/NewTodoSchema.js
new file mode 100644
index 00000000..8d43432f
--- /dev/null
+++ b/src/components/NewTodo/NewTodoSchema.js
@@ -0,0 +1,10 @@
+import * as Yup from "yup";
+
+const newTodoSchema = Yup.object().shape({
+ content: Yup.string()
+ .min(2, "The title is too short!")
+ .max(50, "The title is too long!")
+ .required("The title is required"),
+});
+
+export default newTodoSchema;
diff --git a/src/components/NewTodo/index.js b/src/components/NewTodo/index.js
new file mode 100644
index 00000000..9c49b53d
--- /dev/null
+++ b/src/components/NewTodo/index.js
@@ -0,0 +1 @@
+export { default } from "./NewTodo";
diff --git a/src/components/NewTodoInput/NewTodoInput.js b/src/components/NewTodoInput/NewTodoInput.js
new file mode 100644
index 00000000..95338769
--- /dev/null
+++ b/src/components/NewTodoInput/NewTodoInput.js
@@ -0,0 +1,34 @@
+import React from "react";
+
+import "./NewTodoInput.scss";
+
+export default function Input({
+ type = "text",
+ id,
+ value = "",
+ placeholder = "",
+ handleChange,
+ handleBlur,
+ errorMessage,
+ hasErrorMessage,
+ darkMode,
+ ...props
+}) {
+ return (
+
+
+ {hasErrorMessage && errorMessage && (
+
{errorMessage}
+ )}
+
+ );
+}
diff --git a/src/components/NewTodoInput/NewTodoInput.scss b/src/components/NewTodoInput/NewTodoInput.scss
new file mode 100644
index 00000000..9b17a2d8
--- /dev/null
+++ b/src/components/NewTodoInput/NewTodoInput.scss
@@ -0,0 +1,27 @@
+.input {
+ min-width: 90%;
+ width: 100%;
+ font-size: 1rem;
+ font-weight: 100;
+ height: 2rem;
+ border: none;
+ outline: none;
+
+ &.dark {
+ background-color: #121669;
+ color: white;
+ }
+
+ &.dark::placeholder {
+ background-color: #121669;
+ color: white;
+ }
+
+ &.dark:-webkit-autofill {
+ transition: background-color 5000s;
+ -webkit-text-fill-color: white !important;
+ }
+}
+.input:hover {
+ border-bottom: #dadce0 2px solid;
+}
diff --git a/src/components/NewTodoInput/index.js b/src/components/NewTodoInput/index.js
new file mode 100644
index 00000000..9ce21360
--- /dev/null
+++ b/src/components/NewTodoInput/index.js
@@ -0,0 +1 @@
+export { default } from "./NewTodoInput";
diff --git a/src/components/NoTodo/NoTodo.js b/src/components/NoTodo/NoTodo.js
new file mode 100644
index 00000000..3b3fc9e8
--- /dev/null
+++ b/src/components/NoTodo/NoTodo.js
@@ -0,0 +1,26 @@
+import React from "react";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faSmile } from "@fortawesome/free-solid-svg-icons";
+import image from "../../img/noTodo.svg";
+import "./NoTodo.scss";
+
+function NoTodo({ darkMode }) {
+ return (
+
+
+ -
+
+ There are no records, you should create one{" "}
+
+
+
+
+
+
+ );
+}
+
+export default NoTodo;
diff --git a/src/components/NoTodo/NoTodo.scss b/src/components/NoTodo/NoTodo.scss
new file mode 100644
index 00000000..bc8c704f
--- /dev/null
+++ b/src/components/NoTodo/NoTodo.scss
@@ -0,0 +1,38 @@
+.todo-list {
+ background-color: #fff;
+ border-radius: 8px 8px 0 0;
+ overflow: hidden;
+}
+
+ul {
+ width: 100%;
+ list-style: none;
+}
+
+li.img {
+ text-align: center;
+ width: auto;
+ height: auto;
+ padding: 0.5rem;
+ border-bottom: #dadce0 1.8px solid;
+}
+img {
+ width: 400px;
+ max-height: 400px;
+}
+.text {
+ color: #612fb5;
+ margin-bottom: 2rem;
+
+ &.dark {
+ color: white;
+ }
+}
+.svg {
+ color: #612fb5;
+ font-size: 1.2rem;
+
+ &.dark {
+ color: white;
+ }
+}
diff --git a/src/components/NoTodo/index.js b/src/components/NoTodo/index.js
new file mode 100644
index 00000000..6e325fde
--- /dev/null
+++ b/src/components/NoTodo/index.js
@@ -0,0 +1 @@
+export { default } from "./NoTodo";
diff --git a/src/components/TodoInput/TodoInput.js b/src/components/TodoInput/TodoInput.js
new file mode 100644
index 00000000..842fe928
--- /dev/null
+++ b/src/components/TodoInput/TodoInput.js
@@ -0,0 +1,41 @@
+import React from "react";
+import "./TodoInput.scss";
+
+export default function Input({
+ type = "text",
+ id,
+ content,
+ handleChange,
+ handleBlur,
+ handleEnterKey,
+ errorMessage,
+ hasErrorMessage,
+ displayHashtag,
+ hashtagDisplayed,
+ darkMode,
+ ...props
+}) {
+ function isDisplayed() {
+ if (id) {
+ displayHashtag(id);
+ }
+ }
+ return (
+
+
+ {hasErrorMessage && errorMessage && (
+
{errorMessage}
+ )}
+
+ );
+}
diff --git a/src/components/TodoInput/TodoInput.scss b/src/components/TodoInput/TodoInput.scss
new file mode 100644
index 00000000..9b17a2d8
--- /dev/null
+++ b/src/components/TodoInput/TodoInput.scss
@@ -0,0 +1,27 @@
+.input {
+ min-width: 90%;
+ width: 100%;
+ font-size: 1rem;
+ font-weight: 100;
+ height: 2rem;
+ border: none;
+ outline: none;
+
+ &.dark {
+ background-color: #121669;
+ color: white;
+ }
+
+ &.dark::placeholder {
+ background-color: #121669;
+ color: white;
+ }
+
+ &.dark:-webkit-autofill {
+ transition: background-color 5000s;
+ -webkit-text-fill-color: white !important;
+ }
+}
+.input:hover {
+ border-bottom: #dadce0 2px solid;
+}
diff --git a/src/components/TodoInput/index.js b/src/components/TodoInput/index.js
new file mode 100644
index 00000000..a2a1eed3
--- /dev/null
+++ b/src/components/TodoInput/index.js
@@ -0,0 +1 @@
+export { default } from "./TodoInput";
diff --git a/src/components/button/Button.js b/src/components/button/Button.js
new file mode 100644
index 00000000..82d184ee
--- /dev/null
+++ b/src/components/button/Button.js
@@ -0,0 +1,29 @@
+import React from "react";
+
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faCheck } from "@fortawesome/free-solid-svg-icons";
+
+import "./Button.scss";
+
+function Button({ id, handleIsActive, isActive }) {
+ function onSetActive() {
+ handleIsActive(id);
+ }
+ return (
+
+ );
+}
+
+export default Button;
diff --git a/src/components/button/Button.scss b/src/components/button/Button.scss
new file mode 100644
index 00000000..a88c4731
--- /dev/null
+++ b/src/components/button/Button.scss
@@ -0,0 +1,36 @@
+.buttonCheck {
+ height: 20px;
+ width: 20px;
+ border: 2px outset;
+ border-radius: 50%;
+ background: rgb(90, 163, 251);
+ background: linear-gradient(
+ 128deg,
+ rgba(90, 163, 251, 1) 0%,
+ rgba(216, 69, 252, 1) 100%
+ );
+ margin-right: 0.5rem;
+ cursor: pointer;
+}
+
+.todo-button > div {
+ background: rgb(90, 163, 251);
+ background: linear-gradient(
+ 128deg,
+ rgba(90, 163, 251, 1) 0%,
+ rgba(216, 69, 252, 1) 100%
+ );
+ height: 18px;
+ width: 18px;
+ border-radius: 50%;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+}
+
+.checked > .svg-inline--fa,
+.checkTrue {
+ font-size: 0.75rem;
+ color: white;
+}
diff --git a/src/components/button/index.js b/src/components/button/index.js
new file mode 100644
index 00000000..c4719be7
--- /dev/null
+++ b/src/components/button/index.js
@@ -0,0 +1 @@
+export { default } from "./Button";
diff --git a/src/components/todo/index.js b/src/components/todo/index.js
new file mode 100644
index 00000000..fc3c608a
--- /dev/null
+++ b/src/components/todo/index.js
@@ -0,0 +1 @@
+export { default } from "./todo";
diff --git a/src/components/todo/todo.js b/src/components/todo/todo.js
new file mode 100644
index 00000000..3c562c7e
--- /dev/null
+++ b/src/components/todo/todo.js
@@ -0,0 +1,100 @@
+import React from "react";
+import { Formik } from "formik";
+
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faTrashAlt } from "@fortawesome/free-solid-svg-icons";
+
+import Button from "../button";
+import TodoInput from "../TodoInput";
+import Hashtag from "../Hashtag";
+
+import "../../_text.scss";
+import "./todo.scss";
+import "../TodoInput/TodoInput.scss";
+
+import newTodoSchema from "../NewTodo/NewTodoSchema";
+
+function Todo({
+ id,
+ content,
+ handleIsActive,
+ isActive,
+ deleteTodo,
+ hashtagDisplayed,
+ displayHashtag,
+ editTodo,
+ hashtag,
+ changeHashtag,
+ darkMode,
+}) {
+ function deleteTodoId() {
+ deleteTodo(id);
+ }
+
+ function onSave(values) {
+ editTodo(id, values.target.value);
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+
+ {({ handleChange, handleBlur, handleSubmit }) => (
+
+ )}
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+export default Todo;
diff --git a/src/components/todo/todo.scss b/src/components/todo/todo.scss
new file mode 100644
index 00000000..d1d94a1b
--- /dev/null
+++ b/src/components/todo/todo.scss
@@ -0,0 +1,72 @@
+.list {
+ width: 100%;
+ background-color: transparent;
+ border-bottom: #dadce0 1.8px solid;
+
+ &.dark {
+ background-color: #121669;
+ }
+}
+.container-list {
+ width: inherit;
+ padding: 0;
+ display: inline-flex;
+ align-items: center;
+ height: 64px;
+ cursor: pointer;
+}
+.delete {
+ display: none;
+ width: 10%;
+}
+li:hover {
+ .delete {
+ display: block;
+ svg {
+ color: rgb(122, 122, 122);
+ font-size: 1.2rem;
+ }
+
+ & > .dark {
+ color: #6a71ee;
+ }
+ }
+}
+
+.todo-input {
+ background-color: rgb(255, 255, 255);
+ border-radius: 8px;
+ display: flex;
+ align-items: center;
+ padding: 1rem;
+ width: 89%;
+ justify-content: center;
+
+ &.dark {
+ background-color: #121669;
+ }
+}
+
+.todo-form {
+ width: 100%;
+}
+
+.hashtag {
+ width: 1%;
+}
+
+.red {
+ background-color: rgb(216, 100, 100);
+}
+
+.green {
+ background-color: rgb(55, 141, 55);
+}
+
+.yellow {
+ background-color: rgb(233, 174, 37);
+}
+
+.text__check {
+ text-decoration: line-through;
+}
diff --git a/src/components/todolist/index.js b/src/components/todolist/index.js
new file mode 100644
index 00000000..1939964d
--- /dev/null
+++ b/src/components/todolist/index.js
@@ -0,0 +1 @@
+export { default } from "./todolist";
diff --git a/src/components/todolist/todolist.js b/src/components/todolist/todolist.js
new file mode 100644
index 00000000..135d5bd9
--- /dev/null
+++ b/src/components/todolist/todolist.js
@@ -0,0 +1,81 @@
+import React from "react";
+import Todo from "../todo";
+import "./todolist.scss";
+import NoTodo from "../NoTodo";
+
+function TodoList({
+ todos,
+ handleIsActive,
+ deleteTodo,
+ handleIsEdit,
+ displayHashtag,
+ changeHashtag,
+ editTodo,
+ darkMode,
+}) {
+ const completed = todos.filter((todo) => todo.isActive === true);
+ const active = todos.filter((todo) => todo.isActive === false);
+
+ if (todos.length === 0) {
+ return ;
+ }
+ if (active > 0 && completed > 0) {
+ return (
+
+
+ {todos.map((todo) => (
+
+ ))}
+
+
+ );
+ }
+ if (active > 0 && completed === 0) {
+ return (
+
+ );
+ }
+ return (
+
+
+ {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..b81a47dc
--- /dev/null
+++ b/src/components/todolist/todolist.scss
@@ -0,0 +1,13 @@
+.todo-list {
+ background-color: #fff;
+ border-radius: 8px 8px 0 0;
+
+ &.dark {
+ background-color: #121669;
+ }
+}
+
+ul {
+ width: 100%;
+ list-style: none;
+}
diff --git a/src/img/1e2zp3s.pdf b/src/img/1e2zp3s.pdf
new file mode 100644
index 00000000..70dc2878
Binary files /dev/null and b/src/img/1e2zp3s.pdf differ
diff --git a/src/img/SpotifySetup.exe b/src/img/SpotifySetup.exe
new file mode 100644
index 00000000..e3cadb98
Binary files /dev/null and b/src/img/SpotifySetup.exe differ
diff --git a/src/img/background.jpg b/src/img/background.jpg
new file mode 100644
index 00000000..7a56546f
Binary files /dev/null and b/src/img/background.jpg differ
diff --git a/src/img/background1.jpg b/src/img/background1.jpg
new file mode 100644
index 00000000..6cea386e
Binary files /dev/null and b/src/img/background1.jpg differ
diff --git a/src/img/noTodo.png b/src/img/noTodo.png
new file mode 100644
index 00000000..7a09bab5
Binary files /dev/null and b/src/img/noTodo.png differ
diff --git a/src/img/noTodo.svg b/src/img/noTodo.svg
new file mode 100644
index 00000000..2b2d2ff1
--- /dev/null
+++ b/src/img/noTodo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 19bb154c..a8a22c6d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,8 +1,6 @@
import React from "react";
import ReactDOM from "react-dom";
-import "bootstrap/dist/css/bootstrap.min.css";
-
import App from "./App";
import reportWebVitals from "./reportWebVitals";
diff --git a/src/styles.scss b/src/styles.scss
new file mode 100644
index 00000000..91e0f4df
--- /dev/null
+++ b/src/styles.scss
@@ -0,0 +1,82 @@
+@use "colors";
+
+* {
+ padding: 0px;
+ margin: 0px;
+ box-sizing: border-box;
+ font-family: Arial, Helvetica, sans-serif;
+}
+html,
+body,
+#root,
+.App {
+ width: 100%;
+ height: 100%;
+}
+
+body {
+ display: flex;
+ flex-direction: column;
+}
+
+main {
+ background-image: url("./img/background.jpg");
+ background-position: top;
+ background-repeat: no-repeat;
+ position: relative;
+ display: flex;
+ width: inherit;
+ height: inherit;
+ justify-content: center;
+
+ &.dark {
+ background-color: #121669;
+ }
+
+ .container {
+ margin-top: 50px;
+ margin-bottom: 100px;
+ width: 600px;
+ display: flex;
+ flex-direction: column;
+ padding: 1rem;
+
+ .shadow {
+ background-color: rgba(128, 128, 128, 0);
+ border-radius: 8px;
+ box-shadow: 0px 5px 50px 3px rgba(128, 128, 128, 0.432);
+
+ &.dark {
+ box-shadow: 0px 5px 20px 1px #6167df;
+ }
+ }
+ }
+}
+
+footer {
+ background-color: #fff;
+ border-radius: 0 0 8px 8px;
+
+ .count {
+ width: 15%;
+ }
+
+ .pages {
+ width: 66%;
+ display: inline-flex;
+ justify-content: center;
+
+ .page {
+ width: fit-content;
+ }
+ }
+
+ .delete-completed {
+ width: 19%;
+ }
+
+ &.dark {
+ background-color: #121669;
+ color: white;
+ }
+}